Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Proper initialisation of smart pointers array

For such case:

    class A
    {
        //implementation
    };

    class B
    {
     public:
         B();
        ~B();
     private:
         std::vector<std::shared_ptr<A>> _innerArray;
    };

what should I do in the B() to create an object with valid state? Do I need to manually call default constructor for every A object in array? And do I need to do something special in ~B()? If B class is example of bad design, feel free to say how to make it better. Thanks.

Edit So, here is a scheme of what I really need here.

enter image description here

So real values stored only in array of A and all other objects are for storing connections. The easiest example - A = dot, B = Line (or curve) going via selected dots and C = a plane described by lines. Hope it makes question more exact.

like image 754
Pavel Oganesyan Avatar asked Nov 07 '12 11:11

Pavel Oganesyan


People also ask

How do you create an array of unique pointers in C++?

If you want to create a unique_ptr , you can write: class Object { }; // unique_ptr auto ptr = std::make_unique<Object>(); auto intPtr = std::make_unique<int>(); // or shared_ptr auto shared = std::make_shared<Object>(); auto intShared = std::make_shared<int>();

How are smart pointers implemented?

In C++, a smart pointer is implemented as a template class that mimics, by means of operator overloading, the behaviors of a traditional (raw) pointer, (e.g. dereferencing, assignment) while providing additional memory management features.

What are the main features of smart pointers in general?

What are the main features of smart pointers in C++ in general? a) They employ garbage collection to manage memory allocation and deallocation. b) They support both exclusive and shared ownership of objects and memory. c) They know whether they are the last owner of a resource.


1 Answers

To create a B object in a valid state you do not have to do anything more. You even do not have to declare and implement constructor and destructor for B. std::vector<std::shared_ptr<A>> that is a member of B will be default initialized in B's constructor which means it will not have any elements in a container yet. It will also be properly deleted in ~B thanks to std::vector and std::shared_ptr destructors.

On the other hand if you for example want to initialize it somehow (i.e. 3 values) you can use std::vector's std::initializer_list constructor in a B's constructor initialization list. For example:

class B
{
 public:
     B(): _innerArray{ std::make_shared<A>(),
                       std::make_shared<A>(),
                       std::make_shared<A>() } {}
    ~B() {}
 private:
     std::vector<std::shared_ptr<A>> _innerArray;
};

Remember that std::make_shared uses perfect forwarding so you pass A's constructor arguments as the function arguments and not the class object itself.

Answering your concerns about the design I would like to encourage you to first think about the exclusive ownership of members in a vector before you decide to share them.

class B
{
 public:
     B();
    ~B();
 private:
     std::vector<std::unique_ptr<A>> _innerArray;
};

Above implementation is more effective on many grounds. First of all it makes your design more clear on who is responsible for the lifetime of As. Next std::unique_ptr is faster because it does not demand thread safe reference counting. And last but not least it does not cost any additional memory (compared to regular C pointer) while std::shared_ptr may take tens of bytes (24-48) to store shared state data which is highly ineffective when you operate on small classes. That is why I always use std::unique_ptr as my first resort smart pointer and I only fallback to std::shared_ptr when it is really needed.

EDIT:

Answering your edit I would create 3 containers of classes A, B, C. Depending of the fact if you need them to be polymorphic or not I would store either values like that (non-polymorphic types):

std::deque<A> as;
std::deque<B> bs;
std::deque<C> cs;

or (polymorphic types):

std::vector<std::unique_ptr<A>> as;
std::vector<std::unique_ptr<B>> bs;
std::vector<std::unique_ptr<C>> cs;

in that order (as must live longer than bs and bs must live longer than cs). Then I would just have std::vector<A*> inside B class and std::vector<B*> inside C class without any smart pointers usage.

I hope that helps.

EDIT:

Changed std::vector to std::deque in the first case which allows references/pointers to container elements survive containers extensions with push_back(). However they will not survive erasing elements, sorting or other stuff.

like image 111
Mateusz Pusz Avatar answered Oct 05 '22 03:10

Mateusz Pusz