C++析构函数

结论是:
1.对象的析构会调用自己及其所有父类的析构函数(不管是否有virtual)
2.对于有virtual的析构,总是从new的类型开始,依次析构到最顶父类
3.对于无virtual的析构,总是以delete时的类类型开始,依次析构到最顶父类

下面是例子程序:

#include <iostream>


class Base
{
public:
	Base()
	{
		std::cout<<__FUNCTION__<<std::endl;
	}
	virtual ~Base()
	{
		std::cout<<__FUNCTION__<<std::endl;
	}

};

class Sub:public Base
{
public:
	Sub()
	{
		std::cout<<__FUNCTION__<<std::endl;
	}
	virtual ~Sub()
	{
		std::cout<<__FUNCTION__<<std::endl;
	}
};

class SubSub : public Sub
{
public:
	SubSub()
	{
		std::cout<<__FUNCTION__<<std::endl;
	}
	virtual ~SubSub()
	{
		std::cout<<__FUNCTION__<<std::endl;
	}
};


class Base2
{
public:
	Base2()
	{
		std::cout<<__FUNCTION__<<std::endl;
	}
	~Base2()
	{
		std::cout<<__FUNCTION__<<std::endl;
	}

};
class Sub2:public Base2
{
public:
	Sub2()
	{
		std::cout<<__FUNCTION__<<std::endl;
	}
	~Sub2()
	{
		std::cout<<__FUNCTION__<<std::endl;
	}
};
class SubSub2 : public Sub2
{
public:
	SubSub2()
	{
		std::cout<<__FUNCTION__<<std::endl;
	}
	~SubSub2()
	{
		std::cout<<__FUNCTION__<<std::endl;
	}
};


/// 结论:
/// 不管父类析构函数是否有virtual,子类析构都会调用父类的析构函数。
/// 没有virtual的父类容易造成子类的内存泄露
///
/// 对于无virtual的情况:可能存在子类的析构函数不能正确调用
/// 1.只有用父类指针去delete才不会调用子类析构函数:
///		Base2* base_sub_ptr = new Sub2(); 
///		delete base_sub_ptr;// 只调用父类的析构函数
///     
///		Sub2* sub_sub_ptr = new Sub2();
///		delete ((Base2*)sub_sub_ptr);// 只调用父类析构
///  
///		Sub2* sub_sub_ptr = new Sub2();
///		delete sub_sub_ptr;// 会调用子类父类的析构,不管是否有virtual
///
/// 也就是如果析构函数没有virtual,那么指针转换之后就可以导致不能正确析构

int main()
{
	// 父类有virtual的情况
	{
		std::cout<<"\nBase* base_sub_ptr = new Sub();"<<std::endl;
		Base* base_sub_ptr = new Sub(); 
		delete base_sub_ptr; // 有virtual,会调用子类和父类的析构函数
	}

	{
		std::cout<<"\nSub* base_sub_ptr = new Sub();"<<std::endl;
		Sub* sub_sub_ptr = new Sub(); 
		delete sub_sub_ptr; // 有virtual,会调用子类和父类的析构函数
	}

	{
		std::cout<<"\nSub* base_sub_ptr = new Sub();"<<std::endl;
		Base* base_base_ptr = new Base(); 
		delete base_base_ptr; // 有virtual,调用父类析构函数
	}

	{
		std::cout<<"\nSub* sub_subsub_ptr = new SubSub();"<<std::endl;
		Sub* sub_subsub_ptr = new SubSub(); 
		delete sub_subsub_ptr; // 有virtual,从最子类依次析构到父类
	}
	{
		std::cout<<"\nSub* sub_subsub_ptr = new SubSub();"<<std::endl;
		Base* sub_subsub_ptr = new SubSub(); 
		delete (Sub*)sub_subsub_ptr; // 有virtual,从最子类依次析构到父类
	}
	{
		std::cout<<"\nSub* sub_subsub_ptr = new SubSub();"<<std::endl;
		Base* sub_subsub_ptr = new SubSub(); 
		delete (SubSub2*)sub_subsub_ptr; // 有virtual,从最子类依次析构到父类
	}
	{
		std::cout<<"\nSub* sub_subsub_ptr = new Sub();"<<std::endl;
		Base* sub_sub_ptr = new Sub(); 
		delete (SubSub2*)sub_sub_ptr; // 有virtual,从最子类依次析构到父类
	}



	// 父类没有virtual的情况
	{
		std::cout<<"\nBase2 base_base = Base2();"<<std::endl;
		Base2 base_base = Base2();// 无virtual,调用父类析构函数
	}
	{
		std::cout<<"\nSub2 sub_sub = Sub2();"<<std::endl;
		Sub2 sub_sub = Sub2();// 无virtual,会调用子类和父类的析构函数
	}
	{
		std::cout<<"\nSub2* sub_sub_ptr = new Sub2();"<<std::endl;
		Sub2* sub_sub_ptr = new Sub2();
		delete sub_sub_ptr;// 会调用自己及父类的析构,不管是否有virtual
	}
	{
		std::cout<<"\nSub2* sub_sub_ptr = new Sub2();"<<std::endl;
		Sub2* sub_sub_ptr = new Sub2();
		delete ((Base2*)sub_sub_ptr);// 只调用Base2析构
	}

	{
		std::cout<<"\nBase2* base_sub_ptr = new Sub2();"<<std::endl;
		Base2* base_sub_ptr = new Sub2(); 
		delete base_sub_ptr;// 只调用Base2的析构函数
	}

	{
		std::cout<<"\nSubSub2* sub_subsub_ptr = new SubSub2();"<<std::endl;
		SubSub2* sub_subsub_ptr = new SubSub2();
		delete ((Base2*)sub_subsub_ptr);// 只调用Base2析构
	}
	{
		std::cout<<"\nBase2* base_subsub_ptr = new Sub2();"<<std::endl;
		Base2* base_subsub_ptr = new SubSub2(); 
		delete (Sub2*)base_subsub_ptr;// 只调用Base2,Sub2的析构函数
	}
	{
		std::cout<<"\nBase2* base_subsub_ptr = new SubSub2();"<<std::endl;
		Base2* base_subsub_ptr = new SubSub2(); 
		delete base_subsub_ptr;// 只调用Base2的析构函数
	}
	{
		std::cout<<"\nBase2* base_base_ptr = new Base2();"<<std::endl;
		Base2* base_base_ptr = new Base2(); 
		delete (SubSub2*)base_base_ptr;// 调用SubSub2直到父类的析构函数
	}


	Sub2 sub;
	{
		std::cout<<"\nBase2 base_sub = Sub2();"<<std::endl;
		Base2 base_sub = sub;
	}
	{
		std::cout<<"\nBase2 base_sub = Sub2();"<<std::endl;
		Base2 base_sub = Sub2();
	}



	std::cout<<"Test Over!"<<std::endl;
	return 0;
}


### C++ 析构函数的使用方法 析构函数是在对象生命周期结束时自动调用的一个特殊成员函数,用于执行清理操作。它的功能主要包括释放动态分配的内存、关闭打开的文件以及释放其他资源等[^3]。 #### 定义方式 析构函数的名字由波浪号 `~` 加上类名组成,不接受任何参数也不返回任何值。例如: ```cpp class MyClass { public: ~MyClass(); // 析构函数声明 }; // 析构函数定义 MyClass::~MyClass() { // 清理逻辑 } ``` 如果未显式定义析构函数,则编译器会自动生成一个默认析构函数[^1]。默认析构函数通常适用于简单的数据结构,但对于复杂的数据结构(如包含动态分配的内存),则需要手动定义以确保资源被正确释放。 --- ### 默认析构函数与用户定义析构函数的区别 - **默认析构函数**: 编译器生成的默认析构函数仅负责销毁类中的非静态成员变量,并不会处理额外的清理任务。它适合于那些不需要特别清理工作的简单类[^4]。 - **用户定义析构函数**: 当类中存在动态分配的资源(如堆内存、文件句柄或其他外部资源)时,必须显式定义析构函数来完成这些资源的释放。例如: ```cpp class ResourceHandler { private: int* data; public: ResourceHandler(int size) : data(new int[size]) {} // 动态分配内存 ~ResourceHandler() { delete[] data; } // 显式释放内存 }; ``` 在此例子中,如果没有显式的析构函数,`data` 所指向的堆内存将无法得到释放,从而引发内存泄漏问题。 --- ### 需要显式定义析构函数的情况 以下是几种常见的场景,其中需要显式定义析构函数而不是依赖默认版本: 1. 类中有动态分配的内存; 2. 类管理了某些外部资源(如文件流、数据库连接等); 3. 存在继承关系且基类拥有虚函数的情况下,为了支持多态行为,应将基类的析构函数声明为虚拟函数。 示例代码如下所示: ```cpp class Base { public: virtual ~Base() {} // 虚拟析构函数 }; class Derived : public Base { public: ~Derived() override {} // 派生类析构函数 }; ``` --- ### 私有析构函数的应用 有时出于设计需求,可能希望限制某个类的对象只能通过特定的方式创建或销毁。此时可以通过将析构函数设为私有并提供友元函数或者嵌套类来进行间接访问[^2]。然而需要注意的是,在这种模式下容易遇到一些潜在风险,比如内存泄漏和继承相关的问题。 --- ### 注意事项 编写析构函数时需注意以下几点以保障程序的安全性和效率: 1. **避免重复删除同一块内存**:多次调用 `delete` 或者错误地释放已经释放过的指针可能导致未定义行为。 2. **防止异常抛出**:由于析构函数本身不应该失败,因此在其内部尽量减少可能会触发异常的操作。 3. **考虑线程安全因素**:对于共享资源而言,析构过程中应当同步多个线程之间的交互以防竞争条件发生。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值