I have the classic (possible problematic) multiple inheritance diamond scheme.
I want to have a std::vector
that can contain either C
or D
objects so I make it as std::vector<C>
which is D
's dad and it
works fine.
BUT when I use: std::vector<std::unique_ptr<C>>
then I have segmentation fault upon the destruction of the vector.
** glibc detected *** ./a.out: free(): invalid pointer: 0x0000000009948018***
Why is there a difference? To me, even the first implementation is problematic.
Code
#include <string>
#include <vector>
#include <memory>
class A
{
public:
A() = default;
};
class B : public virtual A
{
public:
B() = default;
};
class C : public virtual A
{
public:
C() = default;
};
class D : public B, public C
{
public:
D() = default;
};
int main()
{
{ // this crashes
std::vector<std::unique_ptr<C>> v;
std::unique_ptr<D> s1(new D());
v.push_back(std::move(s1));
std::unique_ptr<C> s2(new C());
v.push_back(std::move(s2));
}
{ // this is fine
std::vector<C> v;
D s1;
v.push_back(s1);
C s2;
v.push_back(s2);
}
return 0;
};
Destructors are called in the order "most derived to most basal", and in reverse order of declaration. So ~AB is called first, then ~B , then ~A , because AB is the most derived class.
In simple terms, a virtual destructor ensures that when derived subclasses go out of scope or are deleted the order of destruction of each class in a hierarchy is carried out correctly. If the destruction order of the class objects is incorrect, in can lead to what is known as a memory leak.
Yes. Well the unique ptr has a function object that by default invokes delete on the pointed to object, which calls the destructor. You can change the type of that default deleter to do almost anything.
Use unique_ptr when you want to have single ownership(Exclusive) of the resource. Only one unique_ptr can point to one resource. Since there can be one unique_ptr for single resource its not possible to copy one unique_ptr to another. A shared_ptr is a container for raw pointers.
Thus, when you use a unique_ptr, you do not have to delete dynamically allocated objects, and the destructor of unique_ptr will take care of that. Below is an illustrated code that shows the scope outside which the pointer is destroyed. The object is destroyed when the control goes out of these curly braces.
the managing unique_ptr object is destroyed the managing unique_ptr object is assigned another pointer via operator= or reset (). The object is disposed of, using a potentially user-supplied deleter by calling get_deleter()(ptr). The default deleter uses the delete operator, which destroys the object and deallocates the memory.
Unlike std::shared_ptr, std::unique_ptr may manage an object through any custom handle type that satisfies NullablePointer. This allows, for example, managing objects located in shared memory, by supplying a Deleter that defines typedef boost::offset_ptr pointer; or another fancy pointer .
(2) (since C++11) std::unique_ptr is a smart pointer that owns and manages another object through a pointer and disposes of that object when the unique_ptr goes out of scope.
You should declare your destructors as virtual. Otherwise, if your class D
is deleted using a pointer to C
, then the ~C()
destructor will be called and essential parts of the cleanup will be missed.
Also note that in your second part (not using the unique_ptr
) you do some object slicing. It means you are creating a copy of s1
of type D
into a new object of class C
, hence you can lose the extra information specific to D
.
Here is the corrected code :
#include <string>
#include <vector>
#include <memory>
class A
{
public:
A() = default;
virtual ~A() {};
};
class B : public virtual A
{
public:
B() = default;
virtual ~B() {};
};
class C : public virtual A
{
public:
C() = default;
virtual ~C() {};
};
class D : public B, public C
{
public:
D() = default;
virtual ~D() {};
};
int main()
{
{ // this does not crashe anymore
std::vector<std::unique_ptr<C>> v;
std::unique_ptr<D> s1(new D());
v.push_back(std::move(s1));
std::unique_ptr<C> s2(new C());
v.push_back(std::move(s2));
}
{ // this is fine because you slice D into C, still that fine ?
std::vector<C> v;
D s1;
v.push_back(s1);
C s2;
v.push_back(s2);
}
return 0;
}
As stated in the comments, if you mark A destructor's as virtual, every derived class will also have a virtual destructor. Writing it everywhere can make it more explicit. It is a matter of style.
Your "this is fine" example does slicing, the vector only contains instances of C
, that's why it 'works' but does not do what you expected. The solution is as dkg points out is to use virtual dtors.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With