class CObject
{
public:
// Object model (types, destruction, allocation)
virtual CRuntimeClass* GetRuntimeClass() const;
virtual ~CObject(); //virtual destructors are necessary
...
...
};
为什么MFC的编写者认为virtual destructors are necessary
(虚拟的析构函数是必要的)?
在著名的VC教程 "精通Visual C++ for Windows 95/NT"(电子工业版,
1997年5月版,胡俭,丘宗明等著)第99页中有这样一段话:
“如果CObject的析构函数不是虚拟的,派生类就不会自动地得到虚拟的
析构函数,当对象撤消时就会带来问题——只有当前类的析构函数得到
调用而基类的析构函数就得不到调用。...”
我认为这段解释是这本很不错的书中一个不应出现的严重错误。其意思是说:
若:
class CBase
{
public:
~CBase() { ... };
...
};
class CChild : public CBase
{
public:
~CChild() { ... };
...
};
main()
{
Child c;
...
return 0;
}
上段代码在运行时,当栈框中的自动对象 c 被撤消时,只调用~CChild(),
而不调用~CBase()。
我想但凡对C++继承性理论有所了解的人都会立刻指出这是错误的。
由于在生成CChild对象c时,实际上在调用CChild类的构造函数之前必须首先
调用其基类CBase的构造函数,所以当撤消c时,也会在调用CChild类析构函数
之后,调用CBase类的析构函数(析构函数调用顺序与构造函数相反)。也就是说,
无论析构函数是不是虚函数,派生类对象被撤消时,肯定会依次上调其基类的
析构函数。
那么为什么CObject类要搞一个虚的析构函数呢?
仍以上面代码为例,如果main()中有如下代码:
...
CBase * pBase;
CChild c;
pBase = &c;
...
那么在、当pBase指针被撤消时,调用的是CBase的析构函数还是CChild的呢?
显然是CBase的(静态联编)。但如果把CBase类的析构函数改成virtual型,当
pBase指针被撤消时,就会先调用CChild类构造函数,再调用CBase类构造函数。
在这个例子里,所有对象都存在于栈框中,当离开其所处的作用域时,该对象
会被自动撤消,似乎看不出什么大问题。但是试想,如果CChild类的的构造函数
在堆中分配了内存,而其析构函数又不是virtual型的,那么撤消pBase时,将不
会
调用CChild::~CChild(), 从而不会释放CChild::CChild()占据的内存,造成内存
泄露。
而将CObject的析构函数设为virtual型,则所有CObject类的派生类的析构函数都
将
自动变为virtual型,这保证了在任何情况下,不会出现由于析构函数未被调用而
导致
的内存泄露。这才是MFC将CObject::~CObject()设为virtual型的真正原因。
分享到:
相关推荐
C++ 中基类的析构函数为什么要用 virtual 虚析构函数 C++ 中基类的析构函数为什么要用 virtual 虚析构函数?这是一个非常重要的问题。在 C++ 中,基类的析构函数如果不使用 virtual 虚析构函数,可能会导致内存泄漏...
如果这些类型还涉及多态,即作为其他类的基类,那么为了确保资源的正确释放,基类的析构函数应当声明为虚函数。 3. **一致性原则** 如果一个类提供了至少一个虚函数,意味着它可能用于多态性场景,此时其析构函数...
首先,我们来理解什么是析构函数。析构函数是C++中的一个特殊成员函数,其作用是在对象生命周期结束时自动调用,用于清理对象占用的资源,如释放内存或关闭文件等。析构函数的名称以波浪线(~)开头,后面跟类的名字...
构造函数不能声明为虚函数主要是因为构造过程中对象的动态类型尚未确定,而析构函数可以声明为虚函数以确保正确释放资源并避免内存泄漏等问题。理解这些基本原则对于编写高质量、健壮的C++程序至关重要。
virtual ~Base() { // 基类析构函数声明为虚函数 // ... } }; class Derived : public Base { public: ~Derived() override { // 派生类析构函数 // ... } }; ``` 总的来说,析构函数是C++中进行资源管理的...
在C++编程中,析构函数是一个特殊类型的成员函数,用于清理对象在创建时分配的资源。当对象的生命周期结束时,析构函数会被自动调用。标题“派生的析构函数被调用”涉及的是面向对象编程中的继承概念,特别是在多态...
3. **虚析构函数(Virtual Destructor)** 在销毁对象时,如果对象是通过基类指针或引用删除的,而对象实际上是派生类的实例,那么非虚析构函数只会调用基类的析构函数,派生类的析构函数不会被调用,可能会导致...
1. **动态类型与静态类型不匹配**:在析构函数中调用虚函数时,C++使用的是静态绑定,即根据对象的静态类型(即编译时已知的类型)来决定调用哪个函数版本,而不是动态类型(即运行时的实际类型)。因此,即使你期望...
对于基类,如果基类的析构函数声明为虚的(`virtual`),那么通过基类指针或引用删除派生类对象时,将调用正确的派生类析构函数,这是多态性的体现。不声明为虚析构函数可能导致只调用了基类的析构函数,而忽略了...
这意味着,如果你希望在基类析构函数中进行必要的清理,你需要声明它为虚函数(`virtual`)。这样,当通过指针或引用删除派生类对象时,基类的析构函数也会被调用。 例如,`Object.h`可能包含如下代码: ```cpp ...
在C++编程语言中,虚析构函数(Virtual Destructor)是一个非常关键的概念,它主要用于处理多态性(Polymorphism)和动态类型(Dynamic Type)。本文将深入探讨虚析构函数的作用,以及为何在基类中声明虚析构函数是...
本文将深入探讨为什么应该为多态基类声明虚析构函数以及不这样做可能带来的问题。 1. 虚析构函数的原因: 在C++中,如果基类的析构函数是非虚的,那么通过基类指针或引用删除派生类对象时,只会调用基类的析构函数...
但如果析构函数声明为虚的(`virtual ~ClassName()`),那么在删除对象时,会沿着继承链依次调用每个类的析构函数,确保所有资源都被正确释放。 总的来说,C++的动态和静态关联是实现多态性的重要机制,它们让代码...
在C++中,如果一个基类的析构函数声明为`virtual`,那么当使用基类指针或引用删除一个派生类对象时,会调用到派生类的析构函数,然后依次调用基类的析构函数。这是因为虚析构函数利用了C++的动态绑定(也称为运行时...
在C++编程语言中,析构函数是一种特殊类型的成员函数,它的主要职责是在对象生命周期结束时执行清理工作。析构函数没有返回值,也不接受任何参数,并且每个类只能有一个析构函数。当对象不再使用并即将被销毁时,...
5. 可以声明为`virtual`,以便在多态环境中正确调用对象的析构函数。 在上面的`String`类示例中,析构函数`~String()`使用`delete[]`运算符释放通过`new`运算符动态分配的内存。这是析构函数的一个典型应用,确保在...
析构函数被声明为`virtual`主要是为了确保在多态类型的指针或引用指向对象并调用析构函数时,能够正确调用到实际对象的析构函数,而不仅仅是基类的析构函数。这是因为如果没有`virtual`,只有基类的析构函数会被调用...
在类继承体系中,如果基类的析构函数是虚函数(`virtual`),则可以通过基类指针或引用来正确地销毁派生类对象,避免了内存泄漏的问题。这是因为虚析构函数允许动态绑定,使得在多态环境中,通过基类指针调用析构...
如果基类的析构函数是虚函数(使用`virtual`关键字声明),那么在多态上下文中,即使通过基类指针删除派生类对象,也会正确调用所有层次的析构函数。如果不声明为虚析构函数,只有基类的析构函数会被调用,这可能...
- 析构函数没有返回类型,也没有参数,并且不能被显式指定为`virtual`除非它是类层次结构的一部分。 - 析构函数可以被重载为用户自定义的行为,但这通常不推荐,因为这可能导致混乱。 2. **成员对象析构**: - ...