Reputation: 1757
I am trying to call APIs from a third party library.
There is a trouble when I want to use unique_ptr with a class having protected destructor.
Here is the example,
#include <memory>
#include <iostream>
using namespace std;
class Parent {
public:
Parent () //Constructor
{
cout << "\n Parent constructor called\n" << endl;
}
protected:
~ Parent() //Dtor
{
cout << "\n Parent destructor called\n" << endl;
}
};
class Child : public Parent
{
public:
Child () //Ctor
{
cout << "\nChild constructor called\n" << endl;
}
~Child() //dtor
{
cout << "\nChild destructor called\n" << endl;
}
};
Parent* get() {
return new Child();
}
int main(int argc, char const* argv[])
{
Parent * p1 = get(); // this is ok
std::unique_ptr<Parent> p2(get()); // this is not ok
return 0;
}
I am trying to use unique_ptr with Parent class. But the compiler threw the errors
/usr/include/c++/5/bits/unique_ptr.h: In instantiation
of ‘void std::default_delete<_Tp>::operator()(_Tp*) const
[with _Tp = Parent]’:
/usr/include/c++/5/bits/unique_ptr.h:236:17: required
from ‘std::unique_ptr<_Tp, _Dp>::~unique_ptr() [with _Tp
= Parent; _Dp = std::default_delete<Parent>]’
main.cpp:38:35: required from here
main.cpp:12:5: error: ‘Parent::~Parent()’ is protected
~ Parent() //Dtor
^
In file included from /usr/include/c++/5/memory:81:0,
from main.cpp:2:
/usr/include/c++/5/bits/unique_ptr.h:76:2: error: within
this context
delete __ptr;
Any idea about getting rid of this issue? I can't hack Parent and Child class since they are the classes of the third party library.
Upvotes: 5
Views: 2143
Reputation: 136266
You can make std::default_delete<Parent>
a friend of Parent
to fix that error. And you may also like to make ~Parent
virtual
to avoid undefined behaviour when delete
ing a derived class through Parent
pointer.
E.g.:
class Parent {
friend std::default_delete<Parent>;
// ...
protected:
virtual ~Parent();
// ...
However, Parent
design makes it clear that you are not supposed to delete
through Parent
pointer, this is why the destructor is non-public. Read Virtuality for more details:
Guideline #4: A base class destructor should be either public and virtual, or protected and nonvirtual.
You may like to introduce another intermediate base class to solve the issue:
class Parent { // Comes from a 3rd-party library header.
protected:
~Parent();
};
struct MyParent : Parent { // The intermediate base class.
virtual ~MyParent();
};
class Derived : public MyParent {};
std::unique_ptr<MyParent> createDerived() {
return std::unique_ptr<MyParent>(new Derived);
}
int main() {
auto p = createDerived();
}
Upvotes: 10
Reputation: 36597
Unfortunately, the real way to get rid of this issue is to not derive classes from Parent
, and to not manage the lifetime of Parent
objects (or any derived classes) using std::unique_ptr<Parent>
.
In other words, you need to redesign your classes.
The reasons I say this are
Parent
a protected non-virtual destructor, the intent is most likely to avoid having a Parent *
which actually points at an instance of a derived class and is released using operator delete
. A library designer will not (normally) do this without good reason.std::default_delete<Parent>
a friend of Parent
). However, std::default_delete
is used by unique_ptr
to release the managed object. And it uses operator delete
. That gives undefined behaviour if the object being managed by the unique_ptr<Parent>
is of a type derived from Parent
.So, in short, you're working around the intent of (whoever designed) your third party library and, if you force the code to compile anyway, your reward will be undefined behaviour.
Upvotes: 2