2011-12-05 8 views
6

Tôi thường sử dụng boost :: scoped_ptr cho pimpl's (vì một lý do nào đó vì sau đó tôi không có bất ngờ nếu tôi quên xử lý các nhà xây dựng sao chép)pimpl-idiom trong mẫu; con trỏ thông minh nào?

Với các mẫu tuy nhiên tôi không thể chỉ đặt hàm hủy trong tệp cpp trong đó impl được định nghĩa đầy đủ để đáp ứng các yêu cầu của destructor scoped_ptr. Dù sao thì nó vẫn hoạt động nhưng tôi không chắc chắn nếu nó làm việc hay chỉ tình cờ. Có một số 'thực hành tốt nhất' hoặc tiêu chuẩn? Có phải scoped_ptr là con trỏ thông minh tốt nhất cho các pimpl trong các lớp không thể sao chép được không?

template <class T> class C { 
public: 
    C(){} 
    ~C(){} 
private: 
    boost::scoped_ptr<T> pimpl_; 
}; 
+4

Loại triển khai PIMPL này không có ý nghĩa gì bởi vì để khởi tạo mẫu C bạn phải biết về loại T. PIMPL, mặt khác, ẩn hoàn toàn tương đương với T từ người dùng. –

+0

@VladLazarenko Hmm, tôi nghĩ tăng :: scoped_ptr hoạt động tốt trên các lớp được khai báo trước. Trong trường hợp này, nó phụ thuộc nếu T được xác định hoặc được khai báo trước. Bản sao của scoped_ptr này sẽ bị ẩn trong quá trình thực hiện (pimpl_ (new T()) –

+0

@DavidFeurle: Không thực sự, để template này hoạt động, kích thước của 'T', cũng như giao diện của nó, phải tiếp xúc, bởi vì "khách hàng" cần phải nhanh chóng mẫu. Ví dụ, nơi bạn gọi là 'mới T()'? Bạn không thể ẩn nó trong "cpp" tập tin bởi vì nó phải được trong mẫu.Vì vậy, nó không phải là PIMPL. –

Trả lời

1

boost::shared_ptr không đòi hỏi một định nghĩa hoàn toàn khác so với ở điểm instantiation — trong phương thức khởi tạo, trong trường hợp của pimpl. boost::shared_ptrkhông phải thích hợp cho thành ngữ pimpl, tuy nhiên, vì nó cung cấp ngữ nghĩa rất bất ngờ (ngữ nghĩa tham chiếu để gán hoặc sao chép); nếu bạn thực sự muốn thêm độ phức tạp của con trỏ thông minh , boost::scoped_ptr sẽ có nhiều ứng dụng hơn (nhưng nó yêu cầu định nghĩa đầy đủ tại điểm hủy của nó là được khởi tạo).

Đối với các mẫu, việc sử dụng thành ngữ pimpl là không có ý nghĩa đối với chi tiết triển khai từ tiêu đề. Trong trường hợp vắng mặt của export, tất cả các chi tiết triển khai của mẫu lớp phải được bao gồm ở mọi nơi mẫu được sử dụng, vì vậy động lực đằng sau pimpl thành ngữ không còn tồn tại.

13

Nó chỉ xảy ra rằng Herb Sutter bắt đầu viết lại GotWs của mình sau một thời gian dài. Một trong những cái mới đầu tiên có liên quan đến "Tường lửa Biên dịch".

Bạn có thể muốn có một cái nhìn tại địa chỉ:

GotW #100: Compilation Firewalls (Difficulty: 6/10)

GotW #101: Compilation Firewalls, Part 2 (Difficulty: 8/10)

+0

Cool Tôi thích cuốn sách của anh ấy. Có vẻ như anh ấy đã trả lời câu hỏi của tôi về loại con trỏ để sử dụng bằng cách sử dụng unique_ptr – odinthenerd

+0

. unique_ptr pimpl' – sehe

+1

Có vẻ như Herb mua vào phức tạp hơn là triết lý tốt hơn. Tất cả các giải pháp dựa trên tiêu chuẩn của ông giới thiệu thêm sự phức tạp để không đạt được thực sự. –

2

Hai năm sau, tôi hiểu tình hình tốt hơn nhiều, vì lợi ích của việc giữ các câu trả lời tràn ngăn xếp có liên quan và cập nhật ở đây là cách tôi sẽ trả lời câu hỏi ngày hôm nay.

Tiền đề của câu hỏi ban đầu của tôi có phần thiếu sót. Lý do để sử dụng thành ngữ pimpl là để ẩn các chi tiết thực hiện từ trình biên dịch. Điều này được thực hiện bằng cách lưu trữ việc thực hiện thông qua một con trỏ mờ (con trỏ đến một kiểu dữ liệu đã được khai báo nhưng không được xác định). Điều này có thể làm giảm đáng kể số lượng tiêu đề cần thiết bởi các đơn vị biên dịch khác tương tác với lớp và do đó tăng tốc thời gian biên dịch. Trong trường hợp mẫu trong câu hỏi của tôi, kiểu T được biết đầy đủ tại thời điểm diễn giải mà trong thực tế đòi hỏi kiểu của impl được định nghĩa đầy đủ ở bất cứ nơi nào C<ImplType> được sử dụng làm cho điều này rõ ràng không phải là một ví dụ về thành ngữ pimpl theo nghĩa cổ điển của thuật ngữ.

Có nhiều lý do khác để giữ dữ liệu lớp học thông qua một con trỏ riêng, ví dụ nó cho phép dễ dàng thực hiện di chuyển và hoán đổi không ném và cũng tốt nếu lớp của bạn cần thực hiện bảo đảm ngoại lệ mạnh mẽ (xem bản sao và hoán đổi thành ngữ What is the copy-and-swap idiom?). Mặt khác, nó bổ sung thêm một lớp indirection (thường dẫn đến một lỗi cache) trên mọi truy cập vào impl và phân bổ đống/deallocation khi tạo và phá hủy impl.Đây có thể là hình phạt hiệu suất đáng kể, do đó giải pháp này không nên được coi là một viên đạn bạc.

Nếu bạn có thể sử dụng C++ 11 thì std :: unique_ptr nên được sử dụng thay vì tăng :: scoped_ptr.