C++构造函数和析构函数中千万别去碰虚函数

本文探讨了C++中在构造函数和析构函数内调用虚函数的问题。由于此时虚函数表尚未正确设置,调用会退化为基类的普通函数,而非期望的派生类函数,这可能导致程序行为不符合预期。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在C++的构造函数和析构函数中,千万别去调用类中的虚函数,因为此刻虚函数的虚表指针还没有在内存中安置妥当,是无法被调用的。如果你一定要冒天下之大不韪的话,那么你调用到的函数将是一个普通函数,而不是虚函数,不具有多态等特性。也就是说,此刻你明明是要调用派生类的A函数,但实际的结果却是调用到了基类的A函数。

也就是说,在构造函数和析构函数中,虚函数将退化为普通函数。代码示例如下:

class CBaseObject
{
public:
	CBaseObject() 
	{
		_DoIt();
	}

	void DoIt()
	{
		_DoIt();
	}

protected:
	virtual void _DoIt()
	{
		cout<<"Base do it!"<<endl;
	}
};

class CDerivedObject : public CBaseObject
{
protected:
	virtual void _DoIt()
	{
		cout<<"Derived do it!"<<endl;
	}
};


int main()
{
	CDerivedObject derivedObj;
	derivedObj.DoIt();

	return 0;
}

运行结果:


### C++析构函数虚函数的关系及用法 在 C++ 编程语言中,析构函数虚函数之间的关系主要体现在多态性内存管理上。以下是关于它们之间关系及其使用的详细说明: #### 1. 析构函数为何需要声明为虚函数 如果一个类设计成基类并期望其派生类能够通过基类指针销毁,则应该将基类的析构函数定义为虚函数。这是因为,在使用多态的情况下,如果没有将基类的析构函数设置为虚函数,当通过基类指针删除派生类对象时,只会调用基类的析构函数而不会调用派生类的析构函数[^1]。 这种行为可能导致资源未释放或内存泄漏等问题。因此,为了确保程序的安全性以及正确清理派生类中的资源,应将基类的析构函数声明为虚函数。 ```cpp class Base { public: virtual ~Base() {} // 基类的虚析构函数 }; class Derived : public Base { public: ~Derived() { /* 清理派生类特有的资源 */ } }; ``` #### 2. 虚析构函数的意义 虚析构函数的主要作用在于支持动态绑定机制下的安全销毁操作。即使存在复杂的继承层次结构,只要基类提供了虚析构函数,那么无论何时通过基类类型的指针或者引用去销毁实际类型为派生类的对象时,都能按照正确的顺序依次调用各个级别的析构函数[^3]。 例如下面的例子展示了不加虚析构函数可能带来的问题: ```cpp // 错误示范:缺少虚析构函数 #include <iostream> using namespace std; class A { public: ~A() { cout << "~A()" << endl; } // 非虚析构函数 }; class B : public A { public: int* ptr; B() { ptr = new int(10); } ~B() { delete ptr; cout << "~B()" << endl; } }; int main(){ A *a = new B(); delete a; // 只会调用~A(), 导致B的部分未能完全清除 } ``` 上述代码由于 `A` 的析构函数不是虚函数,所以尽管我们创建了一个 `B` 类型的对象并通过 `A` 指针访问它,但在删除这个对象的时候只触发了 `A::operator delete()` `A::~A()` ,并未触及到 `B::~B()` 。这会造成严重的后果——这里分配给整数数组的空间从未被回收! 相反地,如果我们把上面例子中的 `~A()` 改写成虚拟版本的话,整个过程就会按预期那样顺利进行了: ```cpp // 正确做法:加入虚析构函数 class A { public: virtual ~A() { cout << "~A()" << endl; } // 添加virtual关键字 }; ... delete a; // 将先后调用~B() -> ~A() ``` 此时不仅实现了对底层数据的有效处理,还维持了良好的封装原则。 #### 3. 私有析构函数的应用场景 除了公开可见的标准形式之外,有时也会遇到某些特殊需求使得开发者希望限制外部实例化某个特定类别之实体的能力;这时便可以通过设定私有的构造/析构方法达成目的。不过需要注意的是,一旦采用了这种方式就必须提供额外途径让内部逻辑得以正常运作,比如借助友元函数或是静态工厂模式等等[^2]^。 另外值得注意的一点是,虽然理论上讲任何拥有至少一个纯虚成员函数(包括但不限于析构函数本身)都可以成为抽象类从而阻止直接构建其实例副本的行为发生,但实际上这样做往往显得不够直观简洁明了,因而更多时候还是倾向于单纯隐藏默认实现路径而已。 --- ### 总结 综上所述,C++析构函数虚函数紧密相连,特别是在涉及到继承体系的设计时尤为重要。合理运用这些特性可以帮助程序员更好地控制应用程序生命周期内的各种状态转换流程,并有效规避潜在风险隐患。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值