类对象初始化
共有数据成员初始化
如果一个类中所有的成员都是公用的,则可以在声明对象的同时对逐个数据成员进行初始化。
例:
class Box {
public:
int length; //长度
int width; //宽度
int height; //高度
};
Box B1 = {14,15,20}; //与结构体初始化类似
数据成员类内初始值
C++11新标准规定,可以为数据成员提供一个类内初始值(in-class initializer)。
创建对象时,类内初始值将用于初始化数据成员;没有初始值的成员将被默认初始化。
如我们之前所知的,类内初始值必须使用等号或者花括号括起来的直接初始化形式。
class Box {
public:
int length = 1; //长度
int width = 2; //宽度
int height = 3; //高度
};
构造函数
每个类都分别定义了它的对象被初始化的方式,类通过一个或几个特殊的成员函数来控制其对象的初始化过程,这些函数叫做构造函数(constructor)。
构造函数与类同名,我们以此标识构造函数。
- 构造函数是特殊的成员函数。
- 构造函数没有返回值。
- 构造函数在建立对象时由系统自动执行,而且只能执行一次;构造函数一般声明为 public。
- 构造函数不需要用户调用,也不能被用户调用。
- 构造函数的函数体中,可以包含对数据成员赋值以外的语句。
- 构造函数也可以重载。(即类能包含多个构造函数,不同的构造函数之间必须在参数数量或类型上有所区别。)
类外定义定义构造函数时,必须指明该构造函数是哪个类的成员。
无参的构造函数
class Box {
private:
int length; //长度
int width; //宽度
int height; //高度
public:
Box();
};
Box::Box() //无参的构造函数
{
length = 1;
width = 2;
height = 3;
}
带参数的构造函数
class Box {
private:
int length; //长度
int width; //宽度
int height; //高度
public:
Box(int H, int W, int L);
};
Box::Box(int H, int W, int L)//带参数的构造函数
{
length = H;
width = W;
height = L;
}
调用有参数的构造函数
Box B1{2,3,4}; //列表初始化
Box B2(1,2,3);
带默认实参的构造函数
应在声明构造函数时指定默认值。
调用含有默认实参的构造函数时,可以包含该实参,也可以省略该实参,但只能省略尾部的默认实参。
默认实参作为形参的初始值出现在形参列表中。
我们可以为一个或多个形参定义默认值。
一旦某个形参被赋予了默认值,它后面右侧的所有形参都必须有默认值。
需要在函数声明时指定默认实参;在多次函数声明时,只能赋予一次默认实参。
class Box {
private:
int length; //长度
int width; //宽度
int height; //高度
public:
Box(int H = 1, int W = 2, int L = 3);
};
Box::Box(int H, int W, int L)//带默认实参的构造函数
{
length = H;
width = W;
height = L;
}
构造函数初始值列表
构造函数初始值列表(constructor initialize list)负责为新创建的对象的一个或几个数据成员赋初值。
例:
Box::Box() :height{ 2 }, width{ 2 }, length{ 3 } {}
Box::Box(int H, int W, int L) :height{ H }, width{ W }, length{ L } {}
构造函数初始值是成员名字的一个列表,每个名字后面紧跟括号(或花括号)括起来的成员值。
不同成员的初始化通过逗号分隔开来。
class Box {
private:
int length; //长度
int width; //宽度
int height; //高度
public:
Box(); //无参的构造函数
Box(int H, int W , int L); //带参数的构造函数
};
Box::Box() :height{ 2 }, width{ 2 }, length{ 3 } {}
Box::Box(int H, int W, int L) :height{ H }, width{ W }, length{ L } {}
没有出现在构造函数初始值列表中的成员将通过相应的类内初始值初始化,或者执行默认初始化。
默认构造函数
无参的构造函数被称为默认构造函数(default constructor)。
属于默认的构造函数有:
- 无参的构造函数
- 为所有参数都提供默认值的构造函数
- 合成的默认构造函数
一个类中只能有一个默认构造函数。
如果构造对象时未指定参数或提供了一个空初始化器列表,则会调用默认构造函数。
Box B1; //调用默认构造函数
Box B2{}; //调用默认构造函数
合成的默认构造函数
如果我们没有显示地定义构造函数,那么编译器就会为我们隐式地定义一个默认构造函数。
编译器创建的构造函数又被称为合成的默认构造函数(syntheized default constructor)。
对于大多数类,这个合成的默认构造函数将按照如下规则初始化类的数据成员:
- 如果存在类内初始值,用它来初始化成员。
- 否则,默认初始化该成员。
在C++11标准中,如果我们需要默认的行为,那么可以通过在参数列表后面写上= default
来要求编译器生成构造函数。
class Box {
private:
int length = 1; //长度
int width = 2; //宽度
int height = 3; //高度
public:
Box() = default; //合成的默认构造函数
};
只有当类没有声明任何构造函数时,编译器才会自动地生成默认构造函数。
使用合成的默认构造函数,应将数据成员赋予类内初始值。
默认构造函数的作用
当对象被默认初始化或值初始化时自动执行默认构造函数。
默认初始化在以下情况下发生:
- 当我们在块作用域内不使用任何初始值定义一个非静态变量。
- 当一个类本身含有类类型的成员且使用合成的默认构造函数时。
- 当类类型的成员没有在构造函数初始值列表中显示地初始化时。
值初始化在以下情况发生:
- 在数组初始化的过程中如果我们提供的初始值数量少于数组的大小时。
- 当我们不使用初始值定义一个局部静态变量时。
- 当我们通过书写形如
T()
的表达式显式地请求值初始化时,其中T
是类型名。
成员初始化顺序
构造函数初始值列表只说明用于初始化成员的值,而不限定初始化的具体执行顺序。
成员的初始化顺序与它们在类定义中的出现顺序一致。
委托构造函数
C++11新标准扩展了构造函数初始值的功能,使得我们可以定义所谓的委托构造函数(delegating constructor)。
一个委托构造函数使用它所属类的其他构造函数执行它自己的初始化过程,或者它把自己的一些(或者全部)职责委托给了其他构造函数。
在委托构造函数内,成员初始值列表只有一个唯一的入口,就是类名本身。
class Box {
private:
int length; //长度
int width; //宽度
int height; //高度
public:
Box();
Box(int H,int W);
Box(int H, int W , int L);
};
Box::Box(int H, int W, int L) : height{ H }, width{ W }, length{ L } {}
//委托三参的构造函数初始化成员
Box::Box() :Box{1,2,3} {}
Box::Box(int H, int W) :Box{H,W,10} {}