001_c++类成员变量初始化顺序及类析构顺序
1. 当调用构造函数时发生了什么事?
先上结论:
当调用构造函数时,首先调用父类的构造函数,其次调用类成员变量的构造函数,最后调用当前类自身的构造函数。
当调用析构函数时,首先调用当前类对象的析构函数,其次调用类成员变量的析构函数,最后调用父类的析构函数(使用到面向对象的继承和多态时,析构函数前应当添加 virtual 关键字,否则使用多态机制无法得到预期的结果)。
测试代码如下:
// 头文件 #ifndef _TEST_NEW_DELETE_ORDER_H_ #define _TEST_NEW_DELETE_ORDER_H_ #include <iostream> using namespace std; class People { public: People() { cout << "on people construct" << endl; } virtual ~People() { cout << "on people destruct." << endl; } string name; }; class Book { public: Book() { cout << "on book construct." << endl; } ~Book() { cout << "on book destruct." << endl; } }; class Student : public People { public: Student() :m_book(new Book()) { cout << "on student construct." << endl; cout << name.c_str(); } virtual ~Student() { cout << "on student destruct." << endl; if (m_book) { delete m_book; m_book = nullptr; } } private: Book *m_book; }; #endif //_TEST_NEW_DELETE_ORDER_H_ // 测试 main 函数 int main() { People *stu = new Student(); delete stu; getchar(); }
打印结果如下:
on people construct on book construct. on student construct. on student destruct. on book destruct. on people destruct.
2. 成员变量的初始化顺序跟什么有关?
先上结论:
当调用构造函数时,类成员变量的初始化顺序只跟其在类中声明的顺序有关系。换句话说,先声明的先初始化,后声明的后初始化。
问题2.1 成员变量的初始化顺序能否在成员初值列中修改?
经测试,类成员变量的初始化顺序只与其在头文件中声明的顺序有关系,即使在成员初值列中并非按照头文件中声明的顺序排列,其初始化还是按照声明的顺序进行。
问题2.2 当调用类的析构函数时,类成员变量是按照什么样的顺序进行析构的呢?
当 delete 一个对象时,其析构顺序如上一节中所述,先调用类的析构函数,其次调用成员变量的析构函数,最后调用父类的析构函数。针对类成员变量而言,会按照声明顺序的逆序进行析构。
测试代码如下
// 测试头文件 #ifndef _TEST_NEW_DELETE_ORDER_H_ #define _TEST_NEW_DELETE_ORDER_H_ #include <iostream> using namespace std; class People { public: People() { cout << "on people construct" << endl; } virtual ~People() { cout << "on people destruct." << endl; } string name; }; class Book { public: Book() { cout << "on book construct." << endl; } ~Book() { cout << "on book destruct." << endl; } }; class SchoolUniform { public: SchoolUniform() { cout << "on SchoolUniform construct." << endl; } ~SchoolUniform() { cout << "on SchoolUniform destruct." << endl; } }; class Student : public People { public: Student() { cout << "on student construct." << endl; cout << name.c_str(); } virtual ~Student() { cout << "on student destruct." << endl; } private: Book m_book; SchoolUniform m_uniform; }; #endif //_TEST_NEW_DELETE_ORDER_H_ // 测试 main 函数 int main() { People *stu = new Student(); delete stu; getchar(); }
输出结果:
on people construct on book construct. on SchoolUniform construct. on student construct. on student destruct. on SchoolUniform destruct. on book destruct. on people destruct.
3. virtual 关键字对析构顺序有什么影响?
先上总结:
当使用多态时,如果析构函数前不添加 virtual 关键字,析构将不会按照正确顺序执行。测试代码和 2 中的基本一致,只需要将 People 类中析构函数前的 virtual 关键字去掉即可测试结论。测试输出结果如下:
on people construct on book construct. on SchoolUniform construct. on student construct. on people destruct.
可以看出上述输出结果根本没有调用子类的析构函数,也没有调用子类成员变量的析构函数。但是构造函数还是按照正常顺序执行的。
拓展问题: 构造函数可以声明为 virtual 吗?如果可以会对其造成什么影响?
经测试,构造函数无法声明为 virtual。为什么不行呢?我觉得这个问题不难理解,因为如果给 People 加上 virtual 没有任何意义,如果给析构函数加上 virtual 关键字,会调用父类指针实际所指子类对象的析构函数,如果给成员函数加上 virtual 关键字,父类指针会找到实际所指子类对象的成员函数调用,如果子类没有重写,就调用父类自己的成员函数。但是如果给构造函数加上 virtual 关键字就会变得很奇怪,比如我们想创建一个 People 对象,那么我们不会期望调用到别的构造函数中去。
virtual 关键字相关的用法及测试将在新的一篇详细阐述。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix