析构函数和虚函数

且看下面的例子

Class Deriver: public Base
{

…;

};

Base *pb = new Deriver;
Delete pb;              //子类中含有需要释放的资源时,
//如果没有声明virtual ~Base();则会造成内存泄漏.


    在代码设计的时候,如果派生类有自己在heap中申请的空间,而且会用到派生类向基类的转化,那么一定要声明虚析构函数。若果没有同时满足上述两个条件, 则不要声明虚析构函数(申明虚析构函数和申明虚成员函数一样,都会占用一个virtual pointer)。

    在代码设计的过程中,最好是显式的同时声明基类和派生类的析构函数为虚函数。

    确保使用使用指向派生类对象的基类指针释放派生类对象的内存空间时,
派生类的析构函数可以正常的调用。

    就是所谓的多态吧

    如果你用基类的指针指向一个派生类对象。再删除这个对象的时候,只会执行基类的析构函数而不会执行派生类的析构。只有把基类的析构设为虚函数的时候才会执行派生类的析构。
    如果需要从基类来删除对象的话,对象的析构函数需要为virtual

<think> 我们正在讨论析构函数虚函数的区别。根据提供的引用内容,我们可以总结如下: 1. 析构函数: - 特殊成员函数,用于对象销毁时自动调用,执行资源清理。 - 命名规则:类名前加`~`,无返回类型,无参数,能重载。 - 若未定义,编译器会自动生成默认析构函数。 - 通常应抛出异常(否则可能导致程序终止)[^1][^2]。 - 当类作为基类时,应将析构函数声明为虚函数,以确保正确调用派生类的析构函数(多态销毁)[^1][^4]。 2. 虚函数: - 用于实现运行时多态,允许派生类重写基类行为。 - 通过`virtual`关键字声明(析构函数也可声明为虚函数)。 - 基类指针/引用指向派生类对象时,调用实际对象的函数版本[^4]。 - 纯虚函数:`virtual 返回类型 函数名(参数) = 0;`,使类成为抽象类(能实例化)[^2]。 区别总结: - **目的**: - 析构函数:对象生命周期结束时释放资源。 - 虚函数:实现多态,允许派生类自定义行为。 - **声明方式**: - 析构函数:`~类名()`,无参数,无返回类型。 - 虚函数:`virtual 返回类型 函数名(参数)`。 - **多态性**: - 析构函数:若基类析构函数非虚,则通过基类指针删除派生类对象会导致未定义行为(派生类析构函数被调用)。因此,基类析构函数应声明为虚函数[^1][^4]。 - 虚函数:支持运行时多态,派生类可重写。 - **特殊限制**: - 析构函数能重载,每个类仅有一个析构函数。 - 虚函数可被重写,且派生类中可用`override`关键字显式指明。 示例说明: ```cpp class Base { public: virtual void show() { cout << "Base show\n"; } // 虚函数 virtual ~Base() { cout << "Base destroyed\n"; } // 虚析构函数 }; class Derived : public Base { public: void show() override { cout << "Derived show\n"; } ~Derived() { cout << "Derived destroyed\n"; } }; int main() { Base* obj = new Derived(); obj->show(); // 输出"Derived show"(多态) delete obj; // 正确调用DerivedBase的析构函数(因为基类析构函数虚函数) return 0; } ``` 输出: ``` Derived show Derived destroyed Base destroyed ``` 如果基类析构函数虚函数,则上述`delete obj`仅调用基类析构函数,导致派生类资源泄漏。 结论: - 析构函数关注对象销毁时的资源清理。 - 虚函数关注运行时的动态绑定。 - 当类被设计为基类时,必须将析构函数声明为虚函数,以确保多态对象的正确销毁[^1][^2][^4]。 §§相关问题§§ 1. 为什么基类析构函数需要声明为虚函数? 2. 虚函数表(vtable)在虚函数机制中起什么作用? 3. 纯虚函数与普通虚函数有何区别? </think> ### 析构函数虚函数的区别 #### 1. **核心功能同** - **析构函数** 对象生命周期结束时自动调用,负责资源清理(如释放内存、关闭文件等)。 示例: ```cpp class ResourceHolder { int* data; public: ResourceHolder() : data(new int[100]) {} ~ResourceHolder() { delete[] data; } // 析构函数释放资源 }; ``` - **虚函数** 实现运行时多态,允许派生类重写基类行为。 示例: ```cpp class Shape { public: virtual void draw() { // 虚函数 cout << "绘制基础形状" << endl; } }; class Circle : public Shape { public: void draw() override { // 重写虚函数 cout << "绘制圆形" << endl; } }; ``` --- #### 2. **使用场景差异** | 特性 | 析构函数 | 虚函数 | |--------------|-----------------------------|----------------------------| | **必要性** | 每个类必有(可隐式生成) | 非必须,需显式声明`virtual` | | **调用时机** | 对象销毁时自动调用 | 通过基类指针/引用动态调用 | | **多态支持** | 需显式声明为虚析构函数才支持 | 天然支持运行时多态 | --- #### 3. **虚析构函数的关键作用** 当基类指针指向派生类对象时: - **若析构函数非虚**:仅调用基类析构函数 → 派生类资源泄漏 - **若析构函数为虚**:正确调用整个继承链的析构函数 ```cpp class Base { public: virtual ~Base() { cout << "~Base\n"; } // 虚析构函数 }; class Derived : public Base { int* extra_data; public: Derived() : extra_data(new int[100]) {} ~Derived() override { delete[] extra_data; // 释放派生类资源 cout << "~Derived\n"; } }; int main() { Base* obj = new Derived(); delete obj; // 输出: ~Derived → ~Base } ``` --- #### 4. **语法特性对比** | 特性 | 析构函数 | 虚函数 | |--------------------|-----------------------------|----------------------------| | **声明方式** | `~ClassName()` | `virtual ReturnType func()`| | **可重载** | ❌ 每个类唯一 | ✅ 可被派生类重写 | | **纯虚版本** | 无 | `virtual func() = 0` | | **与类关系** | 必须与类同名 | 普通成员函数 | --- #### 5. **设计意义** - **析构函数** 保障资源安全释放,是RAII(资源获取即初始化)的核心[^2][^3]。 - **虚函数** 实现面向对象的开闭原则(对扩展开放,对修改关闭): ```cpp void render(Shape* s) { s->draw(); // 无需修改代码即可支持新形状 } ``` --- #### 6. **性能影响** - **虚函数**:引入虚表指针(vptr)虚表(vtable),增加内存开销(每对象约8字节)间接调用成本[^1][^4] - **析构函数**:虚析构函数同样有上述开销,但非虚析构函数无额外成本 > 💡 最佳实践:**仅当类会被继承且需多态删除时,才将析构函数设为虚函数**。例如Qt中所有QObject的析构函数均为虚函数
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值