alec.tu
alec.tu

Reputation: 1757

protected destructor with unique_ptr

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

Answers (2)

Maxim Egorushkin
Maxim Egorushkin

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 deleteing 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

Peter
Peter

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

  • If someone has gone to the trouble of giving 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.
  • Your code can be forced to compile as is (e.g. by making 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

Related Questions