再说c++虚析构函数

news/2024/7/8 6:39:34

关于c++类中的虚析构函数。我写这篇博客是为了得出如下3点结论。

1.所有基类的析构函数,都应该声明为虚析构函数!这也是c++标准所指定的。
2.如果设计一个类,可能会被后来的其他类所继承,我们一定要将它的析构函数声明为虚析构。否则被继承会出现内存泄漏等意想不到的问题。
3.如果我们要去继承另外一个类,首先一点是要保证被继承的类的析构函数已经声明为了虚析构函数!
对以上3点,如果你深有理会,如下内容可以不看,不要浪费时间。如果没有很深的概念,可以参考一下。如有问题,希望在评论区或其他方式给我指点,谢谢。
 
 

非继承类的析构函数执行会有如下操作

1、执行析构函数中的代码

2、如果所在类有成员变量(类对象),再执行类成员变量的析构函数

普通析构函数:

以如下代码为例

class Student
{
public:
    Student()
    {
        name = new char[32];
        cout << "Student constructor new name char[32]" << endl;
    }
    ~Student()
    {
        delete name;
        cout << "Student destructor delete Student::name" << endl;
    }
private:
    char* name;
};
class Normal
{
public:
    Student stu;
    Normal(){cout << "Normal constructor" << endl;}
    ~Normal(){cout << "Normal destructor" << endl;}
};
int main()
{
    Normal* pn = new Normal();
    delete pn;
    return 0;
}
执行结果:

(表示很讨厌上面的水印)

析构顺序:

1、执行析构函数中的代码

2、如果所在类有成员变量(类对象),再执行类成员变量的析构函数



以上内容很好理解,因为只是单个类对象的析构,当然也关系到类的组合。
接下来我想讨论的是存在继承关系时,析构函数在做些什么?
#include <iostream>
using namespace std;
#include <string>
class Student
{
public:
    Student()
    {
        name = new char[32];
        cout << "Student constructor new name char[32]" << endl;
    }
    ~Student()
    {
        delete []name;
        cout << "Student destructor delete Student::name" << endl;
    }
private:
    char* name;
};
class Base
{
public:
    Base()
    {
        cout << "Base()" << endl;
    }
    ~Base()
    {
        cout << "~Base()" << endl;
    }
};
class Derived:public Base
{
public:
    Derived()
    {
        cout << "Derived()" << endl;
    }
    ~Derived()
    {
        cout << "~Derived()" << endl;
    }
    Student stu;
};
int main()
{
    Derived *pd = new Derived();
    delete pd;
    return 0;
}
执行结果:

派生类对象的析构过程:
1,调用派生类对象的析构函数,
2,调用派生类中成员对象的析构函数
3,调用基类的析构函数


-------------------------------------我是丑陋的分割线-------------------------------------------
 
在分割线的上方,一切正常,没有什么特别的,异样的地方。
接下来要讨论的是多态情况下的析构函数。
#include <iostream>
using namespace std;
#include <string>
class Student
{
public:
    Student()
    {
        name = new char[32];
        cout << "Student constructor new name char[32]" << endl;
    }
    ~Student()
    {
        delete []name;
        cout << "Student destructor delete Student::name" << endl;
    }
private:
    char* name;
};
class Base
{
public:
    Base()
    {
        cout << "Base()" << endl;
    }
    ~Base()
    {
        cout << "~Base()" << endl;
    }
};
class Derived:public Base
{
public:
    Derived()
    {
        cout << "Derived()" << endl;
    }
    ~Derived()
    {
        cout << "~Derived()" << endl;
    }
    Student stu;
};
int main()
{
    Base* pb = new Derived(); // 注意这一行,基类指针指向了派生类
    delete pb;  	      // delete 基类指针
    return 0;
}

执行结果:


只调用基类的析构函数,派生类的析构函数没有被调用,更要注意的是,因此派生类的成员对象也没有被析构,缺少了“Student destructor delete Student::name”
所以会有内存泄漏。
如下代码也会出现内存泄漏:
class Base
{
};
class Derived:public Base
{
public:
    string str; // 这种内存泄漏就不容易发现了!!!
};
int main()
{
    Base* pb = new Derived(); // 派生类的string对象会发生内存泄漏!
    delete pb;
    return 0;
}
如上类继承时发生的内存泄漏,如何解决?
只需要将基类的析构函数声明为虚析构函数,就是前面加上virtual.
#include <iostream>
using namespace std;
#include <string>
class Student
{
public:
    Student()
    {
        name = new char[32];
        cout << "Student constructor new name char[32]" << endl;
    }
    ~Student()
    {
        delete []name;
        cout << "Student destructor delete Student::name" << endl;
    }
private:
    char* name;
};
class Base
{
public:
    Base()
    {
        cout << "Base()" << endl;
    }
    virtual ~Base()
    {
        cout << "~Base()" << endl;
    }
};
class Derived:public Base
{
public:
    Derived()
    {
        cout << "Derived()" << endl;
    }
    ~Derived()
    {
        cout << "~Derived()" << endl;
    }
    Student stu;
};
int main()
{
    Base* pb = new Derived();
    delete pb;
    return 0;
}
运行结果:

发现一切OK了。
 
 
总结:
1.所有基类的析构函数,都应该声明为虚析构函数!这也是c++标准所指定的。
2.如果设计一个类,可能会被后来的其他类所继承,我们一定要将它的析构函数声明为虚析构。否则被继承会出现内存泄漏等意想不到的问题。
3.如果我们要去继承另外一个类,首先一点是要保证被继承的类的析构函数已经声明为了虚析构函数!
 
 
 


http://www.niftyadmin.cn/n/3652963.html

相关文章

修复损坏的VSS数据文件

今天遇到一个问题&#xff0c;使用VSS的时候&#xff0c;突然跳出一个错误&#xff1a;Error reading from file!发现有一个VSS上的目录出现了异常&#xff0c;只要鼠标点击&#xff0c;就跳出这个错误&#xff01;尝试如下动作&#xff1a;- 删除这个分支 ... 失败- 重新命名这…

FreeBSD中编译JDK

自从JDK 1.1.8后&#xff0c;FreeBSD 因为在 Java 授权上的种种问题&#xff0c;已经不能发布预编译的 JDK 软件包&#xff0c;也没有了所谓的 Official Release 了 。现在要在FreeBSD上使用最新的Java技术&#xff0c;需要通过下面的方式自己编译并进行安装&#xff1a;(1) F…

让Firefox的菜单具有Windows XP风格!

修改用户配置文件userChrome.css&#xff0c; 增加如下内容&#xff1a;/* Make menus XP style */menupopup, popup {border: 1px solid ThreeDShadow !important;-moz-border-left-colors: ThreeDShadow !important;-moz-border-top-colors: ThreeDShadow !important;-moz-bo…

windows上添加回环网卡配制网络,通过SecureCRT连接linux虚拟机

注&#xff0c;这大概是13年的这个时候我写的一点点笔记&#xff0c;刚刚需要时翻了出来。原样贴下面。转眼间3年过去了&#xff0c;哎&#xff0c;我的青春啊。 注意&#xff0c;其中的windows上添加回环网卡&#xff0c;和现在的win10有点不一样&#xff0c;我在最后&#x…

[收藏] FreeBSD ports中make可带有的参数

出自&#xff1a;http://blog.yarshure.com/archives/000072.htmlFreeBSD ports中make可带有的参数一直以来大家不知道在ports中的make还可以有很多的功能&#xff0c;今天我将它可以带有的参数一个个列出来。希望做为一个记录&#xff0c;还是比较有用的哟。fetch - Retrieves…

今天终于知道了什么是X-window中字体hinting的含义了!

字型描繪演算法往往相當複雜&#xff0c;因為他必須去決定哪一個字元組基於在.ttf檔內的演算法去使其顯眼。當你把字型大小決定在60~80的時候&#xff0c;這種字元演算法的一個或兩個位元”忘了”去放亮看起來是不會有太大差別&#xff0c;但如果你的字型大小在8到11位元組的時…

单链表反转的三种方法

Talk is cheap, show your code! struct Node { int data; Node* m_pNext; Node(int d):data(d){m_pNext NULL;} }; 1. 从前遍历到尾&#xff0c;依次反转指针的指向&#xff0c;原来的头指针的下一结点设置为空&#xff0c;原来的尾结点变成头结点返回。 Node* Reverse(Nod…

使用libevent编写Linux服务

本文转自&#xff1a;http://blog.chinaunix.net/uid-25885064-id-3399488.html 我在此代表广大网友对原作者表示感谢。 -------------------下面是正文--------------------------- 一、初始化事件 首先完成对libenvent的事件初始化和事件驱动模型的选择。在使用多线程的情…