类通过一个或几个特殊的成员函数来控制其对象的初始化过程,这些函数叫做构造函数。构造函数的作用是初始化类对象的数据成员。只要类的对象被创建,就会执行构造函数。
默认构造函数当没有声明任何构造函数时,编译器才会自动地生成默认构造函数。初始化规则为:
如果存在类内初始值,用它生成初始化成员。如果不存在类内初始值,执行默认初始化。
某些类不能依赖于合成的默认构造函数
有些默认初始值是未定义的(数组,指针)如果类内包括其他类,且这些类没有提供默认构造函数,则会产生错误
使用默认构造函数的另一个方法
person()=default;
=default 告诉编译器需要默认行为
构造函数和初始化
请注意初始化和赋值的区别。下面两段代码完成的功能一致,但含义不一致。
构造函数一(非初始化版本)
Sales_data::Sales_data(const string &s , unsigned cnt , double price ){
bookNo = s;
units_sold = cnt;
revenue = cnt * price;
}
构造函数二(初始化版本
Sales_data::Sales_data(const string &s , unsigned cnt , double price):bookNo(s) , units_sold(cnt) , revenue(cnt*price) {};
二者的结果没有区别。前者在构造函数体执行之前实行默认初始化,再进行赋值;后者直接进行初始化。
需要注意的是,如果类内成员包括常量或者引用,则必须进行初始化不能进行赋值,因此构造函数不能按照写法一进行
初始值列表的初始化顺序上,最好使构造函数初始值顺序和成员声明的顺序一致,以免出现未定义的情况。
默认构造函数
如果一个构造函数为所有参数都提供了默认实参,它实际上定义了默认的构造函数
委托构造函数
构造函数依赖于其他的构造函数来实现构造。
比如
class Person{
int height;
int weight;
public:
Person(int _height , int _weight): height(_height),weight(_weight) {};
//其余构造函数被委托给第一个构造函数
Person() : Person(0,0){};
Person(int height):Person(height,0);
}
当构造函数被委托给另一个构造函数时,其初始值列表和函数体依次执行。
转换构造函数/类的隐式转换
如果类提供了一个构造函数,且该构造函数只接受一个实参。例如某类的某个构造函数接受一个类型为string的实参,那么编译器可以完成string到该类型的隐式转换。
例如
class Sales_Data{
public:
Sales_Data(string s):bookNo(s) {};
}
Sales_Data combine(Sales_Data A,Sales_Data B);
...
Sales_Data Data1...
string item="sssssaaaa";
combine(Data1,item);
在最后一个combine函数中,编译器隐式的将item的string类转换为Sales_Data,因为其提供了相应的构造函数。
这种转换只能发生一次。
如果要抑制这种隐式转换,可以在相应的构造函数前加上explicit关键字。注意加上了explicit关键字的构造函数只能直接初始化,不能用于拷贝形式的构造。



