Alex Aparin
Alex Aparin

Reputation: 4512

Should I use unique_ptr to keep class' members?

I have such code:

class A
{
public:
    A(void);
    ~A(void)
    {
        delete b;
        delete c;
        delete d;
        // ...
    }
private:
    B* b;
    C* c;
    D* d;
    // ...
};

//A.cpp
    A(void) : b(new B()), c(new C()), d(new D()) //...
    {  
    }

Class A takes ownership over own objects b, c, d ... What is the best way to keep these objects? I guess, that usage of std::unique_ptr<B/C/D> type will be suitable for this way. For example, it allows to don't care about carefull writing of destructor.

Upvotes: 1

Views: 2568

Answers (3)

perencia
perencia

Reputation: 1552

I think it's worth mentioning that if you do not want to transfer ownership, you must use const std::unique_ptr. Using a non-const std:unique_ptr allows to transfer it to another std:unique_ptr.

Upvotes: 2

songyuanyao
songyuanyao

Reputation: 172934

it allows to don't care about carefull writing of destructor.

More than that.

  1. Your code is not exception-safe. For example, if new D() failed by exception being thrown, delete b and delete c won't be executed and memory will leak, because destructor won't be called if constructor fails. Smart pointers can help you to avoid this kind of situation.

  2. Holding raw pointer as members you need to implement destructor carefully, and copy constructor and assignment etc too. See What is The Rule of Three? and Rule-of-Three becomes Rule-of-Five with C++11?.

Upvotes: 6

bobah
bobah

Reputation: 18864

Best is to keep everything by value. If it fits*, and does not need to be hidden**. If it does not fit or needs to be hidden first preference is std::unique_ptr***, second preference (if ownership has to be shared) is std::shared_ptr. And only as a last resort (example for which I cannot even think up). You would actually have raw pointers and manage lifetime yourself, with risk of memory errors and leaks.

* - sometimes you want to be able to have parent object on stack by value and child objects are, say, large arrays which, if stored by value would overflow the stack

** - sometimes you don't want to show what child objects really are (because they are complex, say boost.fusion adapted classes. Then you would want some form of PIMPL idiom:

class.hpp
struct b;
struct A { std::unique_ptr<b> b_; A(); ~A(); }

class.cpp:
struct b { ... }
A::A() = default;
A::~A() = default;

*** - automatic management of dynamically allocated members with unique_ptr

struct A {
  std::unique_ptr<b> b_;
  A(...):
    b_(std::make_unique<b>(...)) {}
};

Upvotes: 5

Related Questions