简体   繁体   中英

Using a unique_ptr as a private member in an abstract base class

I'm fairly new to C++ (but not C or OOP) but I'm trying to do things the "right" way and avoid any bad and/or dangerous habits, so I'm using Google's C++ coding guidelines and Effective C++ as starting points for my learning.

I have an abstract base class with a unique_ptr as a member. If I make it private and only provide access to derived classes via a getter (per Google C++ style guidelines), is this the best way to do it? Or am I missing a potential pitfall here?

Base.h:

#include "Document.h"

typedef std::unique_ptr<Document> pDOC;

class Base {
public:
    Base();
    virtual ~Base() = 0;

    pDOC& GetDoc();

private:
    // Unique pointers cannot be shared, so don't allow copying
    Base(Base const &); // not supported
    Base &operator=(Base const &); // not supported
    pDOC m_doc;
};

Base.cpp

#include "base.h"

Base::Base()
: m_xmldoc(new Document) {}

// Class destructor (no need to delete m_doc since it is a smart pointer)
Base::~Base() {}

pDOC& Base::GetDoc() {
    return m_doc;
}

First, call it upDoc instead of pDOC -- unique_ptr is sufficiently strange that you'd want some indication that the type isn't just a pointer. (There is a strong tendency to define pointer types as starting with a lower-case p, so many people could be confused by your use).

Second, GetDoc if it is only intended for derived classes should be protected not public .

Third, I'd question fully exposing a reference to the unique_ptr to all children. What is the responsibility of Base ? Does it manage the lifetime of the m_doc ? If so, expose a Document* instead of a upDoc ( return m_doc.get(); instead of return m_doc; )

If all Base does is hold an m_doc without managing its lifetime, then why does it exist? It provides no public interface, and it provides little to no functionality.

If I make it private and only provide access to derived classes via a getter

Then you are doing extra typing to get the same effect as offering a public/protected member directly. Admittedly, you can add code to accessors and you can set breakpoints, but that does not change the fact that you are breaking encapsulation by providing direct access to your internals.

Said differently, there is nothing that inhibits users of the type from doing obj.GetDoc().release() and breaking the invariants you might have for your type.

Another alternative is to expose a reference to the object managed by std::unique_ptr , for example:

    const Document& getDocument() const { return *m_doc; }
    Document& getMutableDocument() { return *m_doc; }

This is suggested by Jonas Devlieghere here as a possible approach to expose "containers of unique pointers", as he argues that " [b]y returning references rather than pointers, you make it clear that the caller is not responsible for managing [the pointed object]'s lifetime. "

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM