本文针对析构做一个知识面的深度整理:C++面试真题!如何实现一个类,在父类没有虚析构函数的情况下,通过父类指针析构子类对象?
C++析构函数原理:
- 如果基类析构是
非虚函数
,当删除基类指针的时候,只会调用基类自己的析构函数- 如果基类析构是
虚函数
,当删除基类指针的时候,会先调用子类析构,然后调用基类析构。
问题一:基类是非虚析构,删除基类指针。输出什么
class CA
{
public:
~CA() { printf("~CA\r\n"); }
};
class CB :public CA
{
public:
~CB() { printf("~CB\r\n"); }
};
int main()
{
CA* pCa = new CB;
delete pCa;
}
输出结果:
~CA
因为基类是非虚析构,删除基类指针不会调用子类析构。
问题二:如果使用智能指针输出什么
std::shared_ptr<CA> ptrCb = std::make_shared<CB>();
输出结果:
~CB
~CA
和虚析构表现一样。会触发子类虚构。但是CA明显是非虚析构,不存在虚表。
问题三:为什么智能指针可以调用子类析构函数?
关于非析构函数的理论肯定不会有问题,那么真相只有一个:智能指针内部保存了new 对象
的类型。
下面将模拟一段保存new 对象类型的方法。
方法一:
class manager
{
public:
virtual ~manager() {}
};
template <class T>
class manager_ptr:public manager
{
public:
manager_ptr(T p) : ptr(p) {}
~manager_ptr()
{
delete ptr;
}
private:
T ptr;
};
template <class T>
class _shared_ptr
{
public:
template<class Y>
_shared_ptr(Y* p) { ptr_manager = new manager_ptr<Y*>(p); }
~_shared_ptr()
{
delete ptr_manager;
}
private:
manager* ptr_manager;
};
manager_ptr的模板T保存了,new 对象的类型
。所以delete的时候实际是删除的CB对象,即:delete pCB
方法二:
template <typename T>
class MySharedV2
{
public:
template <typename T2>
MySharedV2(T2 *p)
{
data_ = p;
deleter_ = [p](){ delete p;};
}
~MySharedV2()
{
deleter_();
}
T* operator->()
{
return data_;
}
private:
std::function<void()> deleter_;
T* data_ = nullptr;
};
通过lambda表达式保存new 的对象类型
。当析构的时候正确释放。