Reputation: 45
I am creating my custom shared pointer class and I want my shared pointer class should call derived class destructor when it goes out of scope for the below code.
...
...
template<class T>
MySharedPtr<T>::MySharedPtr(T * p) : ptr(p), refCnt(new RefCount())
{
refCnt->AddRef();
}
template<class T>
void MySharedPtr<T>::release()
{
if (refCnt->Release() == 0)
{
delete ptr;
delete refCnt;
}
ptr = nullptr;
refCnt = nullptr;
}
...
...
Base class destructor called when it goes out of scope but if I use std::shared_ptr<Base> bptr(new Derived());
, it calls derived destructor and base destructor when it goes out of scope. How can I achieve the same behaviour with my custom class?
class Base
{
public:
Base() {
cout << "Base default constructor" << endl;
}
~Base() {
cout << "Base destructor" << endl;
}
virtual void display() {
cout << "in Base" << endl;
}
};
class Derived : public Base
{
public:
Derived() {
cout << "Derived default constructor" << endl;
}
~Derived() {
cout << "Derived destructor" << endl;
}
virtual void display() {
cout << "in Derived" << endl;
}
};
int main()
{
MySharedPtr<Base> bptr(new Derived());
bptr->display();
}
Upvotes: 0
Views: 126
Reputation: 119174
You can store a callback as part of the RefCount
object which will be called when the reference count goes to zero. This callback can "remember" what it needs to do based on the pointer type that was used to originally construct the MySharedPtr
object, even though the knowledge of that most derived type might have been lost elsewhere.
You must modify the constructor of MySharedPtr
so that the type information is not immediately destroyed when the constructor is called. It needs to take U*
, not T*
. If it takes T*
, then as soon as the constructor is entered, you already don't know what the original argument type was, since it has been converted to T*
.
template <class T>
class MySharedPtr {
public:
template <class U>
MySharedPtr(U* p);
// ...
};
template <class T>
template <class U>
MySharedPtr<T>::MySharedPtr(U* p) : ptr(p), refCnt(new RefCount(p))
{
refCnt->AddRef();
}
The RefCount
constructor needs to set up the stored callback based on this pointer:
class RefCount {
public:
template <class U>
RefCount(U* p) : deleter_{[p]{ delete p; }} {}
// ...
void invoke_deleter() { deleter_(); }
private:
int ref_count_ = 0;
std::function<void()> deleter_;
};
So now, if you do:
MySharedPtr<Base> bptr(new Derived());
the Derived*
will be passed to the RefCount
constructor, and it will store a callback that calls delete
on that pointer (of type Derived*
) and not the ptr
stored in the MySharedPtr
, which is of type Base*
.
You need to add the code to invoke this deleter when the ref count goes to 0:
template<class T>
void MySharedPtr<T>::release()
{
if (refCnt->Release() == 0)
{
refCnt->invoke_deleter();
delete refCnt;
}
ptr = nullptr;
refCnt = nullptr;
}
This is basically how std::shared_ptr
works, although I suspect it doesn't use std::function
internally, but some custom type erasure mechanism with lower overhead since it doesn't need to support most std::function
features.
Upvotes: 1