std::auto_ptr or boost::shared_ptr for pImpl idiom?

后端 未结 9 1344
醉梦人生
醉梦人生 2020-12-23 15:04

When using the pImpl idiom is it preferable to use a boost:shared_ptr instead of a std::auto_ptr? I\'m sure I once read that the boost version is

相关标签:
9条回答
  • 2020-12-23 15:39

    The boost alternative to std::auto_ptr is boost::scoped_ptr. The main difference from auto_ptr is that boost::scoped_ptr is noncopyable.

    See this page for more details.

    0 讨论(0)
  • 2020-12-23 15:40

    shared_ptr is much preferable to auto_ptr for pImpl because your outer class could suddenly end up losing its pointer when you copy it.

    With shared_ptr you can use a forwardly-declared type so that works. auto_ptr does not allow a forwardly-declared type. Nor does scoped_ptr and if your outer class is going to be non-copyable anyway and only has one pointer, it may as well be a regular one.

    There is a lot to be said for using an intrusive reference count in the pImpl and get the outer class to call its copy and assign semantics in its implementation. Assuming this is a true vendor (supplies the class) model it is better the vendor does not force the user to be using shared_ptr, or to be using the same version of shared_ptr (boost or std).

    0 讨论(0)
  • 2020-12-23 15:45

    I have been really happy about impl_ptr by Vladimir Batov [modified]. It makes it really easy to create a pImpl without needing to make explicit copy-constructor and assignment operator.

    I have modified the original code, so it now resembles a shared_ptr, so it can be used in epilog code, and remains speedy.

    0 讨论(0)
  • 2020-12-23 15:46

    I tend to use auto_ptr. Be sure to make your class noncopyable (declare private copy ctor & operator=, or else inherit boost::noncopyable). If you use auto_ptr, one wrinkle is that you need to define a non-inline destructor, even if the body is empty. (This is because if you let the compiler generate the default destructor, impl will be an incomplete type when the call to delete impl_ is generated, invoking undefined behaviour).

    There's little to choose between auto_ptr & the boost pointers. I tend not to use boost on stylistic grounds if a standard library alternative will do.

    0 讨论(0)
  • 2020-12-23 15:52

    If you are being really pedantic there is no absolute guarantee that using an auto_ptr member does not require a full definition of the auto_ptr's template parameter at the point at which it is used. Having said that, I've never seen this not work.

    One variation is to use a const auto_ptr. This works so long as you can construct your 'pimpl' with a new expression inside the initialiser list and guarantees that the compiler cannot generate default copy constructor and assignment methods. A non-inline destructor for the enclosing class still needs to be provided.

    Other things being equal, I would favour an implementation that uses just the standard libraries as it keeps things more portable.

    0 讨论(0)
  • 2020-12-23 15:56

    You shouldn't really use std::auto_ptr for this. The destructor won't be visible at the point you declare the std::auto_ptr, so it might not be called properly. This is assuming that you are forward declaring your pImpl class, and creating the instance inside the constructor in another file.

    If you use boost::scoped_ptr (no need for shared_ptr here, you won't be sharing the pimpl with any other objects, and this is enforced by scoped_ptr being noncopyable), you only need the pimpl destructor visible at the point you call the scoped_ptr constructor.

    E.g.

    // in MyClass.h
    
    class Pimpl;
    
    class MyClass 
    { 
    private:
        std::auto_ptr<Pimpl> pimpl;
    
    public: 
        MyClass();
    };
    
    // Body of these functions in MyClass.cpp
    

    Here, the compiler will generate the destructor of MyClass. Which must call auto_ptr's destructor. At the point where the auto_ptr destructor is instantiated, Pimpl is an incomplete type. So in to the auto_ptr destructor when it deletes the the Pimpl object, it won't know how to call the Pimpl destructor.

    boost::scoped_ptr (and shared_ptr) don't have this problem, because when you call the constructor of a scoped_ptr (or the reset method) it also makes a function-pointer-equivalent that it will use instead of calling delete. The key point here is that it instantiates the deallocation function when Pimpl is not an incomplete type. As a side note, shared_ptr allows you to specify a custom deallocation function, so you can use for it for things like GDI handles or whatever else you may want - but that's overkill for your needs here.

    If you really want to use std::auto_ptr, then you need to take extra care by making sure you define your MyClass destructor in MyClass.cpp when Pimpl is fully defined.

    // in MyClass.h
    
    class Pimpl;
    
    class MyClass 
    { 
    private:
        std::auto_ptr<Pimpl> pimpl;
    
    public: 
        MyClass();
        ~MyClass();
    };
    

    and

    // in MyClass.cpp
    
    #include "Pimpl.h"
    
    MyClass::MyClass() : pimpl(new Pimpl(blah))
    {
    }
    
    MyClass::~MyClass() 
    {
        // this needs to be here, even when empty
    }
    

    The compiler will generate the code destruct all of the MyClass members effectively 'in' the empty destructor. So at the point the auto_ptr destructor is instantiated, Pimpl is no longer incomplete and the compiler now knows how to call the destructor.

    Personally, I don't think it's worth the hassle of making sure everything is correct. There's also the risk that somebody will come along later and tidy up the code by removing the seemingly redundant destructor. So it's just safer all round to go with boost::scoped_ptr for this kind of thing.

    0 讨论(0)
提交回复
热议问题