2011-12-09 25 views
14

Bởi vì danh tiếng Noob của tôi, tôi không thể trả lời Thread này, cụ thể là câu trả lời được chấp nhận:Làm thế nào để loại bỏ (non-intrusive) con trỏ thông minh từ một bộ nhớ cache khi không có tài liệu tham khảo nhiều hơn?

tôi không bao giờ sử dụng tăng :: gợi ý thông minh xâm nhập, nhưng nếu bạn sẽ sử dụng con trỏ shared_ptr thông minh, bạn có thể sử dụng weak_ptr đối tượng cho bộ nhớ cache của bạn.

Những con trỏ weak_ptr này không được tính là tham chiếu khi hệ thống quyết định giải phóng bộ nhớ của chúng, nhưng có thể được sử dụng để truy xuất shared_ptr miễn là đối tượng chưa bị xóa.

Đây chắc chắn là một ý tưởng trực quan, tuy nhiên, tiêu chuẩn C++ không hỗ trợ so sánh weak_ptrs, vì vậy nó không thể được sử dụng làm khóa cho vùng chứa liên kết. Điều này có thể được phá vỡ bằng cách thực hiện một toán tử so sánh cho weak_ptrs:

template<class Ty1, class Ty2> 
    bool operator<(
     const weak_ptr<Ty1>& _Left, 
     const weak_ptr<Ty2>& _Right 
    ); 

Vấn đề với giải pháp này là

(1) các toán tử so sánh có để có được quyền sở hữu cho mỗi so sánh (tức là tạo shared_ptrs từ weak_ptr ref)

(2) weak_ptr không bị xóa khỏi bộ nhớ cache khi shared_ptr cuối cùng quản lý tài nguyên bị hủy, nhưng tệp weak_ptr hết hạn được lưu trong bộ nhớ cache.

Đối với (2), chúng tôi có thể cung cấp một tùy chỉnh destructor (DeleteThread), tuy nhiên, điều này sẽ yêu cầu một lần nữa để tạo ra một weak_ptr từ T * đó là xóa, mà sau đó có thể được sử dụng để xóa weak_ptr từ bộ nhớ cache .

Câu hỏi của tôi sẽ là nếu có cách tiếp cận nào tốt hơn với bộ nhớ cache bằng con trỏ thông minh (tôi đang sử dụng trình biên dịch VC100, không tăng) hoặc tôi không nhận được nó?

Chúc mừng, Daniel

+1

bạn không thể thay đổi khóa của bất kỳ container dựa trên khóa nào !!! Nó phải là bất biến! Bạn có thể có một số tùy chỉnh xóa-the-last mà đạt ra và loại bỏ các mục từ container của bạn. Nhưng có một giá trị thay đổi quan trọng trong khi trong một container là rất lớn không-không. – Mordachai

+0

Đó là chính xác vấn đề, tôi sẽ cần phải cung cấp một deleter tùy chỉnh mà xóa các đối tượng từ bộ nhớ cache để tránh có một con trỏ hết hạn trong bộ nhớ cache. Hay bạn đang nói chung hơn, vì số lượng ref thay đổi? – dwn

Trả lời

2

Có điều là, bạn Cache không được giải quyết bằng các đối tượng được lưu trữ chính nó, nếu không nó sẽ là vô ích.

Ý tưởng về Cache là để tránh một số tính toán, vì vậy chỉ mục sẽ là các tham số của phép tính, sẽ trực tiếp ánh xạ tới kết quả nếu đã có.

Bây giờ, bạn có thể thực sự cần một chỉ mục thứ hai, để xóa các đối tượng khỏi bộ nhớ cache, nhưng nó không phải là bắt buộc. Chắc chắn có những chiến lược khác.

Nếu bạn thực sự muốn xóa các đối tượng khỏi bộ nhớ cache ngay khi chúng không được sử dụng ở bất kỳ nơi nào khác trong ứng dụng, thì hiệu quả, bạn có thể sử dụng chỉ mục phụ. Ý tưởng ở đây mặc dù sẽ được lập chỉ mục theo T*, không phải weak_ptr<T>, nhưng để giữ weak_ptr<T> xung quanh, bởi vì nếu không, bạn không thể tạo mới shared_ptr với cùng số lượng tham chiếu.

Cấu trúc chính xác phụ thuộc vào việc các thông số của tính toán rất khó recompute sau khi thực tế, nếu họ đang có, một giải pháp đơn giản là:

template <typename K, typename V> 
class Cache: boost::enable_shared_from_this<Cache> 
{ 
    typedef std::map<K, boost::weak_ptr<V>> KeyValueMap; 
    typedef std::map<V*, KeyValueMap::iterator> DeleterMap; 

    struct Deleter { 
    Deleter(boost::weak_ptr<Cache> c): _cache(c) {} 

    void operator()(V* v) { 
     boost::shared_ptr<Cache> cache = _cache.lock(); 
     if (cache.get() == 0) { delete v; return; } 

     DeleterMap::iterator it = _cache.delmap.find(v); 
     _cache.key2val.erase(it->second); 
     _delmap.erase(it); 
     delete v; 
    } 

    boost::weak_ptr<Cache> _cache; 
    }; // Deleter 

public: 
    size_t size() const { return _key2val.size(); } 

    boost::shared_ptr<V> get(K const& k) const { 
    KeyValueMap::const_iterator it = _key2val.find(k); 
    if (it != _key2val.end()) { return boost::shared_ptr<V>(it->second); } 

    // need to create it 
    boost::shared_ptr<V> ptr(new_value(k), 
     Deleter(boost::shared_from_this())); 

    KeyValueMap::iterator kv = _key2val.insert(std::make_pair(k, ptr)).first; 
    _delmap.insert(std::make_pair(ptr.get(), kv)); 

    return ptr; 
    } 


private: 
    mutable KeyValueMap _key2val; 
    mutable DeleterMap _delmap; 
}; 

Lưu ý đặc biệt khó khăn: con trỏ có thể sống lâu hơn các Cache , vì vậy chúng tôi cần một số trick ở đây ...

Và đối với thông tin của bạn, trong khi nó có vẻ khả thi, tôi không tự tin trong mã này: chưa được kiểm tra, chưa được chứng minh, bla, bla;)

+0

Các giải pháp là tốt đẹp, cụ thể là 'DeleterMap' là những gì tôi đang tìm kiếm. Nhưng tôi không đồng ý về điểm mà đối tượng được lưu trữ không phải là chìa khóa của bộ nhớ cache: ý tưởng là có một bộ nhớ đệm của con trỏ để đảm bảo rằng không có 2 con trỏ bằng nhau khi bị hủy đăng ký (sử dụng so sánh ít hơn để phân loại). Điều này cho phép tôi sử dụng lại con trỏ đối tượng (không thay đổi) trên nhiều đối tượng đến bộ nhớ an toàn. Tuy nhiên, tôi có thể đơn giản sử dụng cách tiếp cận chung chung hơn của bạn 'typedef std :: map > KeyValueMap;' và thay thế nó bằng 'std :: set'. – dwn

+0

Tôi cũng chưa thử nghiệm, nhưng hãy nghĩ rằng dòng 'if (cache.get() == 0) {delete v; } 'nên là' if (_cache.get() == 0) {xóa v; trở về; } ' – dwn

+0

@dawn: liên quan đến tình huống cụ thể của bạn: thực sự, thích nghi với nhu cầu của riêng bạn :) về nhận xét của bạn ... doh! Cảm ơn, tôi đã sửa nó :) –

4

Một giải pháp khả thi cho những gì bạn muốn đạt được có thể là

phép nói rằng T là đối tượng của bạn và shared_ptr<T> được chia sẻ của bạn ptr

  1. Chỉ có thường xuyên T* tại của bạn bộ nhớ cache.
  2. Có một tùy chỉnh deleter cho shared_ptr<T>
  3. Xóa tùy chỉnh của bạn xóa T* khỏi bộ nhớ cache khi xóa.

Bằng cách này, bộ nhớ cache không làm tăng số lượng ref của shared_ptr<T> của bạn, nhưng được thông báo khi số lượng ref đạt 0.

struct Obj{}; 

struct Deleter 
{ 
    std::set<Obj*>& mSet; 
    Deleter(std::set<Obj*>& setIn ) 
     : mSet(setIn) {} 

    void operator()(Obj* pToDelete) 
    { 
     mSet.erase(pToDelete); 
     delete pToDelete; 
    } 
}; 

int main() 
{ 

    std::set< Obj* > mySet; 
    Deleter d(mySet); 
    std::shared_ptr<Obj> obj1 = std::shared_ptr<Obj>(new Obj() , d); 
    mySet.insert(obj1.get()); 
    std::shared_ptr<Obj> obj2 = std::shared_ptr<Obj>(new Obj() , d); 
    mySet.insert(obj2.get()); 

    //Here set should have two elements 
    obj1 = 0; 
    //Here set will only have one element 

    return 42; 
} 
+0

Thanks parapura, giải pháp là tốt đẹp và những gì tôi muốn. Tôi nghĩ rằng bạn có một sai lầm nhỏ: 'std :: set mSet;' phải là một tham chiếu. – dwn

+0

@Thanks .... Tất nhiên trong sự vội vàng của tôi, tôi đã không kiểm tra nó hoàn toàn ... chỉnh sửa và sửa lỗi của tôi. Tôi cũng không chắc chắn về chữ ký ** Deleter functor **. Dường như làm việc theo VS 2011 –

+0

@dawn Bạn có thể chấp nhận nó làm câu trả lời nếu nó giải quyết được vấn đề của bạn không? –

Các vấn đề liên quan