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 @ 2021-08-11 19:44  cpp-muggle  阅读(1578)  评论(0)    收藏  举报