2016-10-10 30 views
6

Trong thiết kế C++ bình thường, hầu hết các đối tượng có thể bị xóa bằng câu lệnh delete, hàm free hoặc tương đương với thư viện cụ thể là free. Đối với các đối tượng như vậy, việc triển khai unique_ptrDeleter có thể là đối tượng không trạng thái được loại bỏ thông qua Tối ưu hóa lớp cơ sở trống. Tuy nhiên, một số thư viện yêu cầu sử dụng một đối tượng khác (có thể chứa một con trỏ hàm hoặc một số ngữ cảnh khác) để xóa các đối tượng khỏi thư viện đó.unique_ptr deleter overhead

typedef struct lib_object lib_object; 

struct lib_api { 
    lib_object (*createInstance)(); 
    void (*freeInstance)(lib_object *o); 
}; 

Người ta có thể quấn này trong unique_ptr bằng cách lưu trữ một con trỏ lib_api như một thành viên dữ liệu trong một tùy chỉnh Deleter, nhưng nếu nhiều lib_object trường hợp cần phải được quản lý, ví dụ trong một container, nó sẽ tăng gấp đôi chi phí bộ nhớ của việc theo dõi các đối tượng. Những loại mô hình có thể được sử dụng để duy trì nguyên tắc RAII khi giao dịch với thư viện này, trong khi vẫn còn bộ nhớ hiệu quả?

+0

Bạn có thể đặt 'lib_api *' thành thành viên tĩnh của lớp deleter không? – Brian

+0

Tôi nghĩ rằng bạn cần lưu trữ 'freeInstance' trong một lớp dẫn xuất tùy chỉnh của lớp container và vùng chứa của bạn sẽ chứa' lib_object * '.Trong lớp dẫn xuất, bạn phải thực thi hàm hủy có thể gọi 'freeInstance' trên mọi phần tử. – Franck

Trả lời

5

Nếu chỉ có một đối tượng lib_api, thì bạn có thể yêu cầu deleter của bạn lấy con trỏ tĩnh.

Nếu có thể có nhiều hơn một đối tượng lib_api thì bạn không còn cách nào khác ngoài việc lưu một con trỏ vào nó trong Deleter.

+0

Tôi nghĩ về điều này, và có vẻ như các biến toàn cầu là giải pháp hữu ích duy nhất. – 68ejxfcj5669

2

Tôi sử dụng mẫu deleter tùy chỉnh cho các đối tượng như vậy.

template<typename T, T Function> 
struct function_deleter 
{ 
    template<typename U> 
    auto operator()(U&& u) const noexcept(noexcept(Function(std::forward<U>(u)))) 
    { 
     return Function(std::forward<U>(u)); 
    } 
}; 

sau đó bạn có thể có sử dụng của bạn gọi deleter free:

unique_ptr<int, function_deleter<void(*)(void*), &free>> uniq; 

Và kích thước của nó vẫn còn tương đương với một con trỏ. live demo

Hãy đến C++ 17 bạn sẽ có thể sử dụng auto cho người không kiểu mẫu thông số, đơn giản hóa mã để:

template<auto Function> 
struct function_deleter 
{ 
    template<typename U> 
    auto operator()(U&& u) const noexcept(noexcept(Function(std::forward<U>(u)))) 
    { 
     return Function(std::forward<U>(u)); 
    } 
}; 

unique_ptr<int, function_deleter<&call_free>> uniq; 

live demo

Có điều này, trong trường hợp của bạn, tôi sẽ giữ unique_ptr<pair<lib_object, lib_api>> với dấu phân cách tĩnh hỗ trợ cấu trúc này.

using lib_pair = pair<lib_object, lib_api>; 

void lib_free(lib_pair* p){ 
    p->second.freeInstance(p->first); 
    delete p; 
} 


using unique_lib_ptr = unique_ptr<lib_pair, function_deleter<void(*)(lib_pair*), &lib_free>> 

Điều này có vẻ khó khăn hơn nhưng có thể chỉ là điều của bạn.

0

Có nên tồn tại một giải pháp thanh lịch hơn nhưng tôi sẽ viết một cái gì đó giống như

template <class ContainerType> 
class TObjectContainer : public ContainerType { 
    public: 
    TObjectContainer() = default; 
    TObjectContainer(const TObjectContainer&); // should call createCopy 
    TObjectContainer(TObjectContainer&&) = default; 
    ~TObjectContainer() 
    { for (lib_object* element : *this) 
     (*freeInstance)(element); 
    } 

    private: 
    void (*freeInstance)(lib_object *o); 
}; 

typedef TObjectContainer<std::vector<lib_object*>> ObjectVector; 

Nó không sử dụng unique_ptr nhưng nó về cơ bản nên thực hiện công việc.

Lưu ý rằng bạn có khả năng quá tải mọi phương pháp xóa như clear để gọi freeInstance hoặc pop_back để trả lại số std::unique_ptr ban đầu của mình.