Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

boost shared pointer constructor destructor

The following code

struct Base
{
  public:
  Base()
  {
    std::cout<<"Base Ctr";
  }

  ~Base()
  {
    std::cout<<"Base Dtr";
  }
};

struct Derived : Base
{
  Derived()
  {
    std::cout<<"Derived Ctr";
  }

  ~Base()
  {
    std::cout<<"Derived Dtr";
  }
};

int main()
{
  Base* b = new Derived;
  delete b;
}

gives me the following output :

Base Ctr
Derived Ctr
Base Dtr

The solution to this is to make the base destructor virtual.

However when I use boost smart pointers without virtual base destructor. I get a different output.

int main()
{
  boost::shared_ptr<Base> b = boost::make_shared<Derived>();
}

The output is

 Base Ctr
 Derived Ctr
 Derived Dtr
 Base Dtr

How is boost shared_ptr able to achieve this without affecting(I'm assuming) the Base and Derived classes.
How does it scale it for multiple level inheritance, i.e base points to dervderv where dervderv is inherited from derv.

EDIT:

Most answers tell me that the "magic" happens in make_shared. I however get the same behaviour for the following code

boost::shared_ptr<Base> ptr(new Derived);  
like image 206
Pranav Kapoor Avatar asked Jun 04 '15 09:06

Pranav Kapoor


2 Answers

In brief, boost::smart_ptr contains pointer to an object, reference count and a deleter function, which is called in destructor, when you call boost::make_shared<Derived>(), it will create default-constructed object of class Derived and deleter will point to a destructor of Derived. This should work for inheritance chain of any length, but having virtual destructor in the base class is really mandatory.

Consider next simplified naive example implementation of such technique:

#include <iostream>
#include <functional>
using namespace std;

struct Base
{
  public:
  Base()
  {
    std::cout<<"Base Ctr ";
  }

  ~Base()
  {
    std::cout<<"Base Dtr ";
  }
};

struct Derived : Base
{
  Derived()
  {
    std::cout<<"Derived Ctr ";
  }

  Derived(const Derived& d)
  {
    std::cout<<"Derived copy Ctr ";
  }

  ~Derived()
  {
    std::cout<<"Derived Dtr ";
  }
};

template <typename T>
void default_deleter(T* p) {
    delete p;
}

template <typename T>
struct pointer {
    pointer(T* p, std::function<void ()> d) : p_(p), deleter_(d) {}
    template <typename U>
    pointer(pointer<U> other) : p_(new U(*other.p_)), 
    deleter_(std::bind(&default_deleter<U>, (U*)p_)) {}
    template <typename Y>
    explicit pointer(Y* p) : p_(p), deleter_(std::bind(&default_deleter<Y>, p)) {}
    ~pointer() {
        deleter_();
    }
    T* p_;
    std::function<void ()> deleter_;
};


template <typename T>
pointer<T> make_pointer() {
    T* p = new T;
    return pointer<T>(p, std::bind(&default_deleter<T>, p));
}

int main() {
    pointer<Base> b = make_pointer<Derived>();
    pointer<Base> b2(new Derived);
    return 0;
}

The output is:

Base Ctr Derived Ctr Base Ctr Derived copy Ctr Derived Dtr Base Dtr Derived Dtr Base Dtr

destructors of Derived class are called twice because there are 2 instances of pointer : one which was created in make_pointer function and the second is in main function.

like image 22
Nikita Avatar answered Oct 02 '22 22:10

Nikita


A solution implemented using normal function pointers:

#include <iostream>
#include <boost/shared_ptr.hpp>
#include <boost/make_shared.hpp>
#include <typeinfo>

using namespace std;

struct Base{

  Base(){
    cout<<"Base ctor."<<endl;
  }

  ~Base(){
    cout<<"Base dtor."<<endl;
  }

};

struct Derv: Base{

  Derv():Base(){
    cout<<"Derv ctor."<<endl;
  }

  ~Derv(){
    cout<<"Derv dtor."<<endl;
  }

};

typedef void (*DEL)(void*);

template<typename U>
void deleter(void* ptr)
{
  delete static_cast<U*>(ptr);
}

template<typename T>
struct SmartPtr{

  T* memPtr;

  DEL func;

  template<typename U>
    SmartPtr(U* p):
      memPtr(p)
  {
    func = deleter<U>;
  }

  ~SmartPtr(){

      func(memPtr);
  }
};
int main()
{
  //case 1
  SmartPtr<Base> ptrSmart1(new Derv());

  //case 2
  SmartPtr<Base> ptrSmart2(new Base());

  //case 3
  SmartPtr<Derv> ptrSmart3(new Derv());

  return 0;
}
like image 171
Nik Avatar answered Oct 02 '22 23:10

Nik