引入
- 要创建一个对象,需要进行两步操作
- 为该对象分配一块内存
- 调用该对象的构造函数,对这块内存进行初始化操作
- 所以构造函数时创建对象的基础
默认构造函数
- 不需要参数、或者为所有参数都提供了默认实参的构造函数,都可以被称为默认构造函数
- 一个类可以拥有多个默认构造函数,但此时也就意味着不能通过默认构造函数构造该对象
class message { public: message() {} message(std::string msg = "") {} }; int main() { message msg_1; //error 重载message()调用不明显 message msg_2(""); //能成功匹配 message(std::string); return 0; }
合成构造函数
-
当一个类没有显示定义默认构造函数,且这个类需要默认构造函数,那么编译器将为其合成一个默认构造函数;如下面这个类
class message { private: std::string msg; }; int main() { message msg; return 0; }
其汇编代码如下,只截取重要部分
不懂汇编也没关系,只要知道汇编中的call等价函数调用即可
main: ...... leaq -32(%rbp), %rax movq %rax, %rcx call _ZN7messageC1Ev #调用构造函数 movl $0, %ebx leaq -32(%rbp), %rax movq %rax, %rcx call _ZN7messageD1Ev #调用析构函数 movl %ebx, %eax addq $72, %rsp ......
asm高亮不出来,将就看一下
-
在以下几种情况,编译器认为这个类需要有默认构造函数(前置条件是程序中使用了隐式构造的方式构造这个类1)
- 含有类对象成员,且该类具有非合成默认构造函数
类对象成员的初始化需要调用构造函数,如果这个类没有实现默认构造函数,编译器将为其合成,且在初始化列表调用其类成员对象的默认构造函数
- 派生自定义了非合成默认构造函数的基类
继承中,基类的构造部分需要放在初始值列表中,如果其基类具有默认构造,且这个派生类没有实现默认构造,编译器将为其合成,且在合成的构造函数初始化列表中调用其基类的默认构造函数
- 类中具有虚函数
存在虚函数就需要初始化虚指针,而虚指针则是在构造函数体前进行隐式初始化,所以编译器需要为其合成默认构造函数
- 继承链中具有虚继承
- 含有类对象成员,且该类具有非合成默认构造函数
-
可以将
= default
显示让编译器合成默认构造函数 -
合成的构造函数不会初始化基本内置类型
初始化列表
class message
{
message() : initialize_list
{
}
};
- 只有在初始化列表中的操作才能算初始化,而在构造函数中进行的是初始化之后的操作,所以类常量成员只能在初始化列表中进行初始化赋值
- 初始化列表只指定初始化对象的值,其初始化顺序与在类中定义的顺序一致
委托构造
- 在初始化列表中调用自身的其他构造函数,将产生委托构造
- 在完成被委托构造函数的初始化列表2和函数体后,将回到委托调用处
- 初始值列表中只能存在一个委托构造
转换构造函数
- 如果一个构造函数,可以只支持一个参数,那么实际上也就定义了将其他类型转换为该类类型的机制
class message { public: message(int data_) : data(data_) {} private: int data; }; int main() { message msg = 1; return 0; }
- 默认其支持隐式转换,使用
explicit
关键字修饰后,其只支持显示转换class message { public: explicit message(int data_) : data(data_) {} private: int data; }; int main() { message msg_1 = 1; //erro 不能将int转化为message message msg_2 = static_cast<message>(1); return 0; }
- 对于隐式转换,编译器只会进行一步转换3
结语
- 构造函数内容不好线性总结,写的比较混乱
- 本文只涉及一些基础内容
- 有任何错误评论区指正,共同进步