2015-01-02 17 views
19

Tôi nghĩ rằng nó rất tò mò khi tôi phát hiện ra rằng tiêu chuẩn định nghĩa std::unique_ptrstd::shared_ptr theo hai cách hoàn toàn khác nhau liên quan đến Deleter mà con trỏ có thể sở hữu. Đây là tuyên bố từ cppreference::unique_ptrcppreference::shared_ptr:Loại Deleter trong unique_ptr và shared_ptr

template< 
    class T, 
    class Deleter = std::default_delete<T> 
> class unique_ptr; 

template< class T > class shared_ptr; 

Như bạn có thể thấy unique_ptr "tiết kiệm" các loại của các Deleter đối tượng làm mẫu đối số. Điều này cũng có thể được nhìn thấy trong cách Deleter được lấy từ con trỏ sau này:

// unique_ptr has a member function to retrieve the Deleter 
template< 
    class T, 
    class Deleter = std::default_delete<T> 
> 
Deleter& unique_ptr<T, Deleter>::get_deleter(); 

// For shared_ptr this is not a member function 
template<class Deleter, class T> 
Deleter* get_deleter(const std::shared_ptr<T>& p); 

Ai đó có thể giải thích lý do đằng sau sự khác biệt này? Tôi rõ ràng ủng hộ khái niệm cho unique_ptr tại sao điều này không được áp dụng cho shared_ptr? Ngoài ra, tại sao get_deleter là một chức năng không phải là thành viên trong trường hợp sau?

+2

Có người sẽ phải đào lên đề nghị ban đầu, nhưng võ đoán của tôi: Không có deleter như là đối số mẫu làm 'shared_ptr' dễ dàng hơn để sử dụng, nhưng bạn cần phải trả các chi phí loại tẩy xoá. Làm 'get_deleter' thành viên sẽ thực hiện viết mã generic tham gia một' shared_ptr 'tẻ nhạt hơn - bạn cần phải viết' get_deleter sp.template () 'thay vì' get_deleter (sp) '. Đây là lý do tại sao 'std :: get' là một người không phải là thành viên. –

+3

Mở rộng một chút về những gì @ T.C. cho biết, một trong những mục tiêu thiết kế cho 'unique_ptr' là nó cần phải có (rất gần) zero overhead. Việc xóa kiểu của deleter là thuận tiện nhưng giới thiệu thời gian chạy trên không, vì vậy nó ít thích hợp hơn cho 'unique_ptr' hơn là' shared_ptr' – wakjah

+0

Bạn cũng nên lưu ý rằng vì sự khác biệt đó, 'shared_ptr p = make_shared ()' làm điều đúng ngay cả khi 'Base' không có destructor ảo. [bằng chứng] (http://coliru.stacked-crooked.com/a/f3a50f90e00d4e58). –

Trả lời

18

đây bạn có thể tìm thấy những đề nghị ban đầu cho con trỏ thông minh: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2003/n1450.html

Nó trả lời câu hỏi của bạn khá chính xác:

Kể từ khi deleter không phải là một phần của các loại, thay đổi chiến lược phân bổ không phá vỡ nguồn hoặc khả năng tương thích nhị phân, và không yêu cầu biên dịch lại máy khách.

này cũng rất hữu ích vì cung cấp cho các khách hàng của std::shared_ptr một số linh hoạt hơn, ví dụ shared_ptr hợp với deleters khác nhau có thể được lưu trữ trong cùng một container.

Ngoài ra, vì việc triển khai shared_ptr cần chia sẻ khối bộ nhớ chia sẻ (vì lưu trữ số tham chiếu) và vì có một số chi phí cần thiết so với các con trỏ thô, việc thêm deleter loại không phải là một vấn đề lớn đây. Mặt khác,

unique_ptr không có chi phí và mọi trường hợp phải nhúng dấu phân cách của nó, do đó, làm cho nó trở thành một phần của loại đó là điều tự nhiên cần làm.

+0

Làm thế nào để loại bỏ deleter được gọi là btw? – WorldSEnder

+0

@WorldSEnder: Đây là tính đến thực hiện, nhưng theo cách thông thường là để đóng gói các deleter bê tông vào một lớp templated rằng dụng cụ (kế thừa) một giao diện để xóa một T *. Vì vậy, khi số tham chiếu đến bằng không, việc thực hiện gọi phương thức ảo, cuộc gọi này được gửi đến lớp nhúng mà lần lượt gọi deleter cụ thể. Đó là cơ chế tương tự như đối với std :: function. – Horstling

+1

Lưu ý rằng việc thực hiện cấp độ 'thô' thấp hơn của 'std :: function' và có lẽ' shared_ptr' sẽ nhanh hơn trong thực tế so với vtable, nhưng ít thư viện std sử dụng chúng (xem 'đại biểu có thể nhanh nhất' qua google). Việc thực hiện vtable chỉ là dễ hiểu nhất, vì nó trông giống như bình thường C++. – Yakk

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