携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第6天,点击查看活动详情
一、继承中的析构函数
✔ 测试用例一:
#include<iostream>
#include<string>
using namespace std;
class Person
{
public:
Person(const char* name = "dancebit")
: _name(name)
{
cout << "Person(const char* name = \"dancebit\")" << endl;
}
Person(const Person& p)
: _name(p._name)
{
cout << "Person(const Person& p)" << endl;
}
Person& operator=(const Person& p)
{
cout << "Person& operator=(cconst Person& p)" << endl;
if (this != &p)
_name = p._name;
return *this;
}
~Person()
{
cout << "~Person()" << endl;
}
protected:
string _name;
int a;
};
class Student : public Person
{
public:
Student(const char* name, int id, const char* address)//推荐
//: _name(name)//err,父类继承下来是一个整体
: Person(name)
, _id(id)
, _address(address)
{}
~Student()
{
//~Person();//err
//Person::~Person();//析构+1
//清理自己的资源
//...
}
private:
int _id;
string _address;
};
int main()
{
Student s1("DANCEBIT", 1, "贵阳市");
Student s2(s1);
Student s3("DanceBit", 2, "北京市");
s2 = s3;
return 0;
}
-
对于析构函数,它和构造函数类似,我们不写,编译器会默认生成,它针对 a) 内置类型不处理;b) 自定义类型调用它的析构函数;c) 父类作为一个整体调用父类的析构函数;
-
如果显示的写, 发现,~Person(); 会报错,这里很多书上都没解释清楚,书上说:子类的析构函数和父类的析构函数构成隐藏关系,所以要指定域。但其实你会发现与上面说的父子类的函数名相同就构造隐藏关系矛盾,这里父子类的析构函数名并不相同。其实本质是因为编译器会对析构函数名做特殊处理,所有类的析构函数名都会被处理成统一名字 destructor(),所以父子类都是 destructor() 就构成隐藏关系。所以说为什么有很多人说 C++ 难学,如果没有一本好书或学习的路径不对 (就像 Primer 如果按着顺序学下去,很容易从入门到放弃)。
为什么编译器要把所有类的析构函数名统一成 destructor ❓
因为析构函数要构成多态重写,重写是多态的一个条件,它要求函数名相同, 若不构成重写的话,在某些情况下会出现资源泄漏的问题,具体细节在多态在谈。
这里就算指定域了,我们发现本应该析构 3 次的,却析构了 6 次 ❓
我们这里只有 3 个对象,但是却析构了 6 次,因为 Person::~Person(); 所以每一个对象里调用了两次析构,所以注释掉它即可。
-
调试发现子类的析构函数在执行结束后,会再自动调用父类的析构函数 ❓
扫描二维码关注公众号,回复: 14425251 查看本文章C++ 中的对象要保证先定义的后析构,可以想象一下父子对象在栈中的结构,所以这里是先构造父对象,再构造子对象,先析构子对象,再析构父对象。如果显示的写,并显示的调用,就有可能不符合这种特性,所以干脆这里规范不要自己显示调用了。注意如果有对为什么先构造父对象有疑问,可以理解为,初始化列表中出现的先后顺序不重要,重要的是声明的顺序,你可以认为编译器在界定时是认为父类的声明顺序是在最前面的。