2012-01-26 33 views
54

Đây là cách đơn giản hóa những gì tôi thấy khi tôi cố gắng sử dụng unique_ptr cho pimpl. Tôi đã chọn unique_ptr vì tôi thực sự muốn lớp sở hữu con trỏ - tôi muốn thời gian tồn tại của con trỏ pimpl và lớp đó giống nhau.Làm cách nào để sử dụng unique_ptr cho pimpl?

Dù sao, đây là tiêu đề:

#ifndef HELP 
#define HELP 1 

#include <memory> 

class Help 
{ 

public: 

    Help(int ii); 
    ~Help() = default; 

private: 

    class Impl; 
    std::unique_ptr<Impl> _M_impl; 
}; 

#endif // HELP 

Dưới đây là nguồn:

#include "Help.h" 

class Help::Impl 
{ 
public: 
    Impl(int ii) 
    : _M_i{ii} 
    { } 

private: 

    int _M_i; 
}; 

Help::Help(int ii) 
: _M_impl{new Help::Impl{ii}} 
{ } 

tôi có thể biên dịch chúng thành một thư viện tốt. Nhưng khi tôi cố gắng sử dụng nó trong một chương trình thử nghiệm tôi nhận được

[email protected]:~/ext_distribution$ ../bin/bin/g++ -std=c++0x -o test_help test_help.cpp Help.cpp 
In file included from /home/ed/bin/lib/gcc/x86_64-unknown-linux-gnu/4.7.0/../../../../include/c++/4.7.0/memory:86:0, 
       from Help.h:4, 
       from test_help.cpp:3: 
/home/ed/bin/lib/gcc/x86_64-unknown-linux-gnu/4.7.0/../../../../include/c++/4.7.0/bits/unique_ptr.h: In instantiation of 'void std::default_delete<_Tp>::operator()(_Tp*) const [with _Tp = Help::Impl]': 
/home/ed/bin/lib/gcc/x86_64-unknown-linux-gnu/4.7.0/../../../../include/c++/4.7.0/bits/unique_ptr.h:245:4: required from 'void std::unique_ptr<_Tp, _Dp>::reset(std::unique_ptr<_Tp, _Dp>::pointer) [with _Tp = Help::Impl; _Dp = std::default_delete<Help::Impl>; std::unique_ptr<_Tp, _Dp>::pointer = Help::Impl*]' 
/home/ed/bin/lib/gcc/x86_64-unknown-linux-gnu/4.7.0/../../../../include/c++/4.7.0/bits/unique_ptr.h:169:32: required from 'std::unique_ptr<_Tp, _Dp>::~unique_ptr() [with _Tp = Help::Impl; _Dp = std::default_delete<Help::Impl>]' 
Help.h:6:7: required from here 
/home/ed/bin/lib/gcc/x86_64-unknown-linux-gnu/4.7.0/../../../../include/c++/4.7.0/bits/unique_ptr.h:63:14: error: invalid application of 'sizeof' to incomplete type 'Help::Impl' 

Đây là một nổi tiếng safety feature. Tôi đã cố gắng làm theo.

Vấn đề của tôi là nếu tôi đặt Trợ giúp :: Khai báo Impl trong tiêu đề, nó có vẻ sẽ làm giảm bớt lợi thế của pimpl. Bố cục lớp hiển thị với người dùng. Định nghĩa bị ẩn nhưng tôi có thể làm điều đó với lớp Trợ giúp và các thành viên riêng tư. Ngoài ra, bao gồm cả tuyên bố của Impl mang đến các tiêu đề mới mà tôi muốn giữ riêng biệt.

Tôi đang thiếu gì? Những gì folks đặt trong một tuyên bố Impl và ở đâu? Tôi có làm trợ giúp dtor sai? Argh!

+2

Xem thêm [GotW # 101: Tường lửa biên soạn, Phần 2] (http://herbsutter.com/gotw/_101/) và [câu hỏi liên quan này] (http://stackoverflow.com/q/8595471/636019). – ildjarn

+1

Mặc dù đó là một câu hỏi cũ, nó có thể có liên quan để chỉ ra rằng, [như được giải thích trong cppreference] (http://en.cppreference.com/w/cpp/language/pimpl), một PImpl thực hiện với 'unique_ptr' nên được bọc trong một cái gì đó như ['propagate_const'] (http://en.cppreference.com/w/cpp/experimental/propagate_const) để có được sự chính xác hoàn chỉnh. – jdehesa

+1

@jdehesa Cảm ơn bạn, tôi đã xem xét propagate_const như một giải pháp cho một số lúng túng của API. Tôi gần như tự hỏi nếu unique_ptr bị phá vỡ theo mặc định trong ý nghĩa này. Nó có vẻ như propagate_const nên được xây dựng trong hoặc ít nhất là mặc định. – emsr

Trả lời

69

Tôi tin rằng test_help.cpp của bạn thực sự thấy trình phá hủy ~Help() mà bạn đã khai báo mặc định. Trong đó destructor, trình biên dịch cố gắng để tạo ra các unique_ptr destructor, quá, nhưng nó cần tuyên bố Impl cho điều đó.

Vì vậy, nếu bạn di chuyển định nghĩa hủy đối với Help.cpp, sự cố này sẽ biến mất.

- EDIT - Bạn có thể xác định destructor được mặc định trong file cpp, quá:

Help::~Help() = default; 
+9

Được rồi, trong tiêu đề tôi chỉ * khai báo * dtor cho Trợ giúp. Sau đó, trong tập tin thực hiện, tôi đặt Trợ giúp :: ~ Help() = mặc định; Woot !!! Cảm ơn. Làm tất cả mọi việc! – emsr

+0

Công cụ tuyệt vời –

+4

Các con trỏ thông minh, bao gồm 'unique_ptr' được yêu cầu để làm việc với loại không đầy đủ. Chức năng "deleter" được gán vào thời gian xây dựng, do đó người dùng của con trỏ không cần phải biết nó bị xóa như thế nào. Nếu điều này khắc phục được sự cố, nó sẽ ngụ ý việc triển khai 'unique_ptr' của anh ta bị hỏng. –

1

Chú giải này từ unique_ptr định nghĩa:

std :: unique_ptr có thể được xây dựng cho một loại T không đầy đủ, chẳng hạn như để tạo thuận lợi cho việc sử dụng như một xử lý trong thành ngữ pImpl. Nếu deleter mặc định được sử dụng, T phải được hoàn thành tại điểm trong mã nơi deleter được gọi, xảy ra trong destructor, di chuyển toán tử gán, và thiết lập lại chức năng thành viên của std :: unique_ptr.

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