文章目录
- 一、构造函数 和 析构函数 调用顺序 说明
- 1、构造函数调用顺序
- 2、析构函数调用顺序
- 3、拷贝构造函数也可以定义初始化列表
- 二、构造函数 和 析构函数 调用顺序 代码分析
- 1、构造函数调用顺序
- 2、代码示例 - 构造 / 析构 函数调用顺序分析
构造函数初始化列表 总结 :
- 初始化列表 可以 为 类的 成员变量 提供初始值 ;
- 初始化列表 可以 调用 类的 成员变量 类型的 构造函数 进行成员变量初始化操作 ;
- 初始化列表 可以 使用 构造函数 中传入的 参数 ;
- 类初始化时 , 根据定义顺序 , 先调用 成员变量的 构造函数 , 然后调用外部类构造函数 , 析构函数正好相反 ;
- 实例对象 的 const 成员变量 必须只能在 初始化列表 中进行 初始化 , 所有的构造函数都要进行初始化操作 ;
一、构造函数 和 析构函数 调用顺序 说明
1、构造函数调用顺序
在一个类 C 中 , 嵌套了 A 和 B 两个类类型的 对象 作为 成员变量 ;
构造函数的 调用顺序如下 :
- 先调用 被嵌套类 A 和 B 的构造函数 , 再调用外部 C 类的构造函数 ;
- A 和 B 构造函数 , 成员变量 中 谁先声明 , 就先调用谁的 构造函数 ;
- 注意 : A 和 B 在 构造函数 初始化列表 中的顺序 , 与先调用谁的构造函数无关 ;
2、析构函数调用顺序
析构函数调用顺序 与 构造函数调用顺序相反 , 直接 将 构造函数 调用顺序 倒序排列即可 ;
3、拷贝构造函数也可以定义初始化列表
如果一个类 没有定义 无参构造函数 , 只有一个 有参的构造函数 ,
此时 , C++ 编译器 不会为其 生成 默认的无参构造函数 ;
这种场景下 涉及到了 构造函数 的类型 :
- 强制在初始化列表中调用构造函数 : 如果类中定义了 有参构造函数 , 导致 无参构造函数 被屏蔽 , 那么 在 所有的构造函数的 初始化列表中 , 都必须强制调用 子对象 的 构造函数 ;
- 不强制在初始化列表中调用构造函数 : 如果类中定义了 无参构造函数 , 或者 有默认的 无参构造函数 , 那么在 初始化列表 中不强制调用 子对象 的构造函数 ;
使用如下方式 , 声明 A 和 B 类型的成员变量 , 会自动调用 默认的无参构造函数 初始化对象 , 但是由于 A 和 B 中定义了 有参构造函数 , 无参构造函数 被屏蔽了 ;
A m_a; // A 类型成员变量
B m_b; // B 类型成员变量
没有 无参构造函数 , 上面声明的 A 和 B 两个对象便无法创建成功 ;
此时 , 只能在 构造函数的 初始化列表 中 , 调用 A 和 B 的 有参构造函数 创建 A B 两个成员变量 ;
拷贝构造函数 也是 构造函数 , 也必须在 初始化列表 中 调用 构造函数 , 对子对象进行初始化操作 ;
二、构造函数 和 析构函数 调用顺序 代码分析
1、构造函数调用顺序
在下面的代码中 ,
定义了 类 A , 该类实现了 有参构造函数 , 其 无参构造函数 被屏蔽 , 如果要初始化 A 类型的对象 , 必须使用有参构造函数 , 使用 A a
的形式定义的变量 , 无法进行初始化 ;
class A
{
public:
// 带参构造函数
A(int age, int height)
{
m_age = age;
m_height = height;
cout << "执行 A 的构造函数" << endl;
}
~A()
{
cout << "执行 A 的析构函数" << endl;
}
public:
int m_age; // 年龄
int m_height; // 身高
};
定义了 类 B 与 上述 类 A 基本一致 , 也是无法使用 默认的无参构造函数 , 必须调用有参构造函数 ;
定义 类 C , 其中维护了 A 和 B 两个子对象 ,
public:
int m_age; // 年龄
A m_a; // A 类型成员变量
B m_b; // B 类型成员变量
const int m_const_int; // 常量成员
由于 A 和 B 都无法使用 无参构造函数 , 因此在 类 C 的所有构造函数 ( 包括 拷贝构造函数 ) 的 初始化列表中 , 必须强制调用 A 和 B 的 有参构造函数 ;
- 此外由于 还定义了
const int m_const_int
常量成员 , 类 C 的 所有构造函数 ( 包括 拷贝构造函数 ) 的 初始化列表中 , 同时也必须强制对 常量成员进行初始化 ;
C() : m_age(10), m_b(5, 110), m_a(10, 150), m_const_int(888)
{}
最终的 构造函数 执行顺序是 :
执行 A 的构造函数
执行 B 的构造函数
执行 C 的构造函数
执行 A 的构造函数
执行 B 的构造函数
执行 C 的 拷贝构造函数
执行
// 通过 C 的有参构造函数
// 其中 构造函数中的参数 作为 参数列表 中的参数值
C c(10, 10, 150, 18, 180);
代码时 , 先后执行 A -> B -> C 类的构造函数 ;
执行
// 调用 C 的拷贝构造函数
C c2 = c;
代码时 , 先执行 A -> B 的构造函数 , 然后执行 C 的拷贝构造函数 ;
2、代码示例 - 构造 / 析构 函数调用顺序分析
#include "iostream"
using namespace std;
class A
{
public:
// 带参构造函数
A(int age, int height)
{
m_age = age;
m_height = height;
cout << "执行 A 的构造函数" << endl;
}
~A()
{
cout << "执行 A 的析构函数" << endl;
}
public:
int m_age; // 年龄
int m_height; // 身高
};
class B
{
public:
// 带参构造函数
B(int age, int height)
{
m_age = age;
m_height = height;
cout << "执行 B 的构造函数" << endl;
}
~B()
{
cout << "执行 B 的析构函数" << endl;
}
public:
int m_age; // 年龄
int m_height; // 身高
};
class C
{
public:
C() : m_age(10), m_b(5, 110), m_a(10, 150), m_const_int(888)
{}
// 构造函数中的参数可以作为 参数列表 中的参数值
C(int age, int ageOfA, int heightOfA, int ageOfB, int heightOfB)
: m_age(age),m_b(ageOfB, heightOfB), m_a(ageOfA, heightOfA), m_const_int(888)
{
cout << "执行 C 的构造函数" << endl;
}
C(const C& c) : m_b(5, 110), m_a(10, 150), m_const_int(888)
{
// 上面的 3 个变量 , 必须在初始化列表中初始化
// m_age 可以在后续进行单独赋值 , 可以不在初始化列表中进行初始化
// 由于 A 和 B 都没有默认构造函数 , 必须在初始化列表中调用 有参构造函数
// m_const_int 成员是常量 , 只能初始化一次 , 不能赋值 ,
// 因此也必须在初始化列表中进行初始化
cout << "执行 C 的 拷贝构造函数" << endl;
}
~C()
{
cout << "执行 C 的析构函数" << endl;
}
public:
int m_age; // 年龄
A m_a; // A 类型成员变量
B m_b; // B 类型成员变量
const int m_const_int; // 常量成员
};
int main()
{
// 通过 C 的有参构造函数
// 其中 构造函数中的参数 作为 参数列表 中的参数值
C c(10, 10, 150, 18, 180);
// 调用 C 的拷贝构造函数
C c2 = c;
// 控制台暂停 , 按任意键继续向后执行
system("pause");
return 0;
}
执行结果 :
执行 A 的构造函数
执行 B 的构造函数
执行 C 的构造函数
执行 A 的构造函数
执行 B 的构造函数
执行 C 的 拷贝构造函数
Press any key to continue . . .
执行 C 的析构函数
执行 B 的析构函数
执行 A 的析构函数
执行 C 的析构函数
执行 B 的析构函数
执行 A 的析构函数
D:\002_Project\006_Visual_Studio\HelloWorld\HelloWorld\Debug\HelloWorld.exe (进程 19692)已退出,代码为 0。
要在调试停止时自动关闭控制台,请启用“工具”->“选项”->“调试”->“调试停止时自动关闭控制台”。
按任意键关闭此窗口. . .