如何在没有虚拟析构函数的情况下使用我的自定义共享指针 class 调用派生析构函数?
How to call derived destructor using my custom shared pointer class without virtual destructor?
我正在创建我的自定义共享指针 class,我希望我的共享指针 class 在超出以下代码的范围时调用派生的 class 析构函数。
...
...
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 析构函数在超出范围时调用,但如果我使用 std::shared_ptr<Base> bptr(new Derived());
,它会在超出范围时调用派生析构函数和基析构函数。我怎样才能用我的自定义实现相同的行为 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();
}
您可以将 回调 存储为 RefCount
对象的一部分,当引用计数变为零时将调用该对象。此回调可以根据最初用于构造 MySharedPtr
对象的指针类型“记住”它需要做什么,即使有关大多数派生类型的知识可能已在其他地方丢失。
必须修改MySharedPtr
的构造函数,这样调用构造函数时类型信息不会立即销毁。它需要 U*
,而不是 T*
。如果取T*
,那么一进入构造函数,你就已经不知道原来的参数类型是什么了,因为它已经被转换成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();
}
RefCount
构造函数需要根据这个指针设置存储回调:
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_;
};
现在,如果你这样做:
MySharedPtr<Base> bptr(new Derived());
Derived*
将被传递给 RefCount
构造函数,它将存储一个调用 delete
到 that 指针的回调(类型 Derived*
) 而不是存储在 MySharedPtr
中的 ptr
,它的类型是 Base*
.
您需要添加代码以在引用计数变为 0 时调用此删除器:
template<class T>
void MySharedPtr<T>::release()
{
if (refCnt->Release() == 0)
{
refCnt->invoke_deleter();
delete refCnt;
}
ptr = nullptr;
refCnt = nullptr;
}
这基本上就是 std::shared_ptr
的工作方式,虽然我怀疑它在内部不使用 std::function
,但是一些自定义类型擦除机制具有较低的开销,因为它不需要支持大多数 std::function
特征。
我正在创建我的自定义共享指针 class,我希望我的共享指针 class 在超出以下代码的范围时调用派生的 class 析构函数。
...
...
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 析构函数在超出范围时调用,但如果我使用 std::shared_ptr<Base> bptr(new Derived());
,它会在超出范围时调用派生析构函数和基析构函数。我怎样才能用我的自定义实现相同的行为 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();
}
您可以将 回调 存储为 RefCount
对象的一部分,当引用计数变为零时将调用该对象。此回调可以根据最初用于构造 MySharedPtr
对象的指针类型“记住”它需要做什么,即使有关大多数派生类型的知识可能已在其他地方丢失。
必须修改MySharedPtr
的构造函数,这样调用构造函数时类型信息不会立即销毁。它需要 U*
,而不是 T*
。如果取T*
,那么一进入构造函数,你就已经不知道原来的参数类型是什么了,因为它已经被转换成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();
}
RefCount
构造函数需要根据这个指针设置存储回调:
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_;
};
现在,如果你这样做:
MySharedPtr<Base> bptr(new Derived());
Derived*
将被传递给 RefCount
构造函数,它将存储一个调用 delete
到 that 指针的回调(类型 Derived*
) 而不是存储在 MySharedPtr
中的 ptr
,它的类型是 Base*
.
您需要添加代码以在引用计数变为 0 时调用此删除器:
template<class T>
void MySharedPtr<T>::release()
{
if (refCnt->Release() == 0)
{
refCnt->invoke_deleter();
delete refCnt;
}
ptr = nullptr;
refCnt = nullptr;
}
这基本上就是 std::shared_ptr
的工作方式,虽然我怀疑它在内部不使用 std::function
,但是一些自定义类型擦除机制具有较低的开销,因为它不需要支持大多数 std::function
特征。