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 关键字相关的用法及测试将在新的一篇详细阐述。

posted @   cpp-muggle  阅读(1260)  评论(0编辑  收藏  举报
编辑推荐:
· 开发者必知的日志记录最佳实践
· 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
点击右上角即可分享
微信分享提示