std::shared_ptr<X>
đã có một loạt chi phí trên một số liệu B*
.
Một shared_ptr<X>
về cơ bản duy trì 4 điều. Nó duy trì một con trỏ-to- B
, nó duy trì hai tội tham chiếu (một số tài liệu tham khảo "cứng" và "mềm" một cho weak_ptr
), và nó duy trì một hàm dọn dẹp.
Chức năng dọn dẹp là lý do tại sao shared_ptr<X>
cư xử khác nhau. Khi bạn tạo một shared_ptr<X>
, một chức năng mà các cuộc gọi mà đặc biệt loại của destructor được tạo ra và lưu trữ trong các chức năng dọn dẹp bởi shared_ptr<X>
quản lý.
Khi bạn thay đổi loại được quản lý (B*
trở thành C*
), chức năng dọn dẹp sẽ không thay đổi.
Vì shared_ptr<X>
cần quản lý số lượng tham chiếu, chi phí bổ sung của bộ nhớ chức năng dọn dẹp đó là biên.
Đối với một số unique_ptr<B>
, lớp này gần như rẻ với giá trị B*
. Nó duy trì trạng thái zero khác với B*
của nó, và hành vi của nó (tại hủy diệt) xuống đến if (b) delete b;
. (Có, rằng if (b)
là dư thừa, nhưng trình tối ưu hóa có thể tìm ra điều đó).
Để hỗ trợ cast-to-base và xóa-như-có nguồn gốc, tiểu bang thêm sẽ phải được lưu trữ mà nhớ unique_ptr
là thực sự đến một lớp dẫn xuất. Điều này có thể ở dạng của một con trỏ được lưu trữ-to-deleter, giống như một shared_ptr
.
Đó sẽ, tuy nhiên, tăng gấp đôi kích thước của một unique_ptr<B>
, hoặc yêu cầu nó để lưu trữ dữ liệu trên đống ở đâu đó.
Nó đã được quyết định rằng unique_ptr<B>
nên là số không trên không, và như vậy nó không hỗ trợ cast-to-base trong khi vẫn gọi destructor của cơ sở.
Bây giờ, bạn có thể dạy unique_ptr<B>
để làm điều này bằng cách thêm một loại deleter và lưu trữ một chức năng hủy diệt biết loại điều nó đang phá hủy. Ở trên đã nói về các deleter mặc định của unique_ptr
, đó là không quốc tịch và tầm thường.
struct deleter {
void* state;
void(*f)(void*);
void operator()(void*)const{if (f) f(state);}
deleter(deleter const&)=default;
deleter(deleter&&o):deleter(o) { o.state = nullptr; o.f=nullptr; }
deleter()=delete;
template<class T>
deleter(T*t):
state(t),
f([](void*p){delete static_cast<T*>(p);})
{}
};
template<class T>
using smart_unique_ptr = std::unique_ptr<T, deleter>;
template<class T, class...Args>
smart_unique_ptr<T> make_smart_unique(Args&&... args) {
T* t = new T(std::forward<Args>(args)...);
return { t, t };
}
live example, nơi tôi tạo ra một ptr duy nhất có nguồn gốc, lưu trữ nó trong một duy nhất-ptr đến cơ sở, và sau đó đặt lại cơ sở. Con trỏ có nguồn gốc bị xóa.
(A void(*)(void*)
deleter đơn giản có thể chạy vào các vấn đề trong đó thông qua vào void*
sẽ khác nhau về giá trị giữa các cơ sở và các trường hợp có nguồn gốc.)
Lưu ý rằng việc thay đổi con trỏ lưu trữ trong đó một unique_ptr
mà không thay đổi deleter sẽ cho kết quả trong hành vi bị bệnh.
Điều này có thể hữu ích: http://stackoverflow.com/questions/6876751/differences-between-unique-ptr-and-shared-ptr –