构造函数是一种可初始化其类的实例的成员函数。构造函数具有与类相同的名称,没有返回值。构造函数可以具有任意数量的参数,类可以具有任意数量的重载构造函数。构造函数可以具有任何可访问性(公共、受保护或私有)。如果未定义任何构造函数,则编译器会生成不采用任何参数的默认构造函数;可以通过将默认构造函数声明为已删除来重写此行为。
构造函数顺序
构造函数按此顺序执行工作:
按声明顺序调用基类和成员构造函数。
如果类派生自虚拟基类,则会将对象的虚拟基指针初始化。
如果类具有或继承了虚函数,则会将对象的虚函数指针初始化。虚函数指针指向类中的虚函数表,确保虚函数正确地调用绑定代码。
它执行自己函数体中的所有代码。
下面的示例显示,在派生类的构造函数中,基类和成员构造函数的调用顺序。首先,调用基构造函数,然后按照基类成员在类声明中出现的顺序对这些成员进行初始化,然后,调用派生构造函数。
#include <iostream>
using namespace std;
class Contained1 {
public:
Contained1() {
cout << "Contained1 constructor." << endl;
}
};
class Contained2 {
public:
Contained2() {
cout << "Contained2 constructor." << endl;
}
};
class Contained3 {
public:
Contained3() {
cout << "Contained3 constructor." << endl;
}
};
class BaseContainer {
public:
BaseContainer() {
cout << "BaseContainer constructor." << endl;
}
private:
Contained1 c1;
Contained2 c2;
};
class DerivedContainer : public BaseContainer {
public:
DerivedContainer() : BaseContainer() {
cout << "DerivedContainer constructor." << endl;
}
private:
Contained3 c3;
};
int main() {
DerivedContainer dc;
int x = 3;
}
</div>
这是输出:
Contained1 constructor. Contained2 constructor. BaseContainer constructor. Contained3 constructor. DerivedContainer constructor.</div>
如果构造函数引发异常,析构的顺序与构造的顺序相反:
构造函数主体中的代码将展开。
基类和成员对象将被销毁,顺序与声明顺序相反。
如果是非委托构造函数,所有完全构造的基类对象和成员均将被销毁。但是,对象本身不是完全构造的,因此析构函数不会运行。
成员列表
使用成员初始值设定项列表从构造函数参数初始化类成员。此方法使用直接初始化,这比在构造函数体内使用赋值运算符更高效。
class Box {
public:
Box(int width, int length, int height)
: m_width(width), m_length(length), m_height(height) // member init list
{}
int Volume() {return m_width * m_length * m_height; }
private:
int m_width;
int m_length;
int m_height;
};
</div>
创建 Box 对象:
Box b(42, 21, 12); cout << "The volume is " << b.Volume();</div>
显式构造函数
如果类具有带一个参数的构造函数,或是如果除了一个参数之外的所有参数都具有默认值,则参数类型可以隐式转换为类类型。例如,如果 Box 类具有一个类似于下面这样的构造函数:
Box(int size): m_width(size), m_length(size), m_height(size){}
</div>
可以初始化 Box,如下所示:
Box b = 42;</div>
或将一个 int 传递给采用 Box 的函数:
class ShippingOrder
{
public:
ShippingOrder(Box b, double postage) : m_box(b), m_postage(postage){}
private:
Box m_box;
double m_postage;
}
//elsewhere...
ShippingOrder so(42, 10.8);
</div>
这类转换可能在某些情况下很有用,但更常见的是,它们可能会导致代码中发生细微但严重的错误。作为一般规则,应对构造函数使用 explicit 关键字(和用户定义的运算符)以防止出现这种隐式类型转换:
explicit Box(int size): m_width(size), m_length(size), m_height(size){}
</div>
构造函数是显式函数时,此行会导致编译器错误:ShippingOrder so(42, 10.8);。
默认构造函数
默认构造函数没有参数;它们遵循略有不同的规则:
默认构造函数是一个特殊成员函数;如果没有在类中声明构造函数,则编译器会提供默认构造函数:
class Box {
Box(int width, int length, int height)
: m_width(width), m_length(length), m_height(height){}
};
int main(){
Box box1{}; // call compiler-generated default ctor
Box box2; // call compiler-generated default ctor
}
</div>
当你调用默认构造函数并尝试使用括号时,系统将发出警告:
class myclass{};
int main(){
myclass mc(); // warning C4930: prototyped function not called (was a variable definition intended?)
}
</div>
这是“最棘手的解析”问题的示例。这种示例表达式既可以解释为函数的声明,也可以解释为对默认构造函数的调用,而且 C++ 分析器更偏向于声明,因此表达式会被视为函数声明。
如果声明了任何非默认构造函数,编译器不会提供默认构造函数:
class Box {
Box(int width, int length, int height)
: m_width(width), m_length(length), m_height(height){}
};
private:
int m_width;
int m_length;
int m_height;
};
int main(){
Box box1(1, 2, 3);
Box box2{ 2, 3, 4 };
Box box4; // compiler error C2512: no appropriate default constructor available
}
</div>
如果类没有默认构造函数,将无法通过单独使用方括号语法来构造该类的对象数组。例如,在前面提到的代码块中,框的数组无法进行如下声明:
Box boxes[3]; // compiler error C2512: no appropriate default constructor available</div>
但是,你可以使用初始值设定项列表将框的数组初始化:
Box boxes[3]{ { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 } };
</div>
复制和移动构造函数
复制构造函数是特殊成员函数,它采用对相同类型对象的引用作为输入,并创建它的副本。移动也是特殊成员函数构造函数,它将现有对象的所有权移交给新变量,而不复制原始数据。
显式默认构造函数和已删除构造函数
你可以显式设置默认复制构造函数、设置默认构造函数、移动构造函数、复制赋值运算符、移动赋值运算符和析构函数。你可以显式删除所有特殊成员函数。
派生类中的构造函数
派生类构造函数始终调用基类构造函数,因此,在完成任何额外任务之前,它可以依赖于完全构造的基类。调用基类构造函数进行派生,例如,如果 ClassA 派生自 ClassB,ClassB 派生自 ClassC,那么首先调用 ClassC 构造函数,然后调用 ClassB 构造函数,最后调用 ClassA 构造函数。
如果基类没有默认构造函数,则必须在派生类构造函数中提供基类构造函数参数:
class Box {
public:
Box(int width, int length, int height){
m_width = width;
m_length = length;
m_height = height;
}
private:
int m_width;
int m_length;
int m_height;
};
class StorageBox : public Box {
public:
StorageBox(int width, int length, int height, const string label&) : Box(width, length, height){
m_label = label;
}
private:
string m_label;
};
int main(){
const string aLabel = "aLabel";
StorageBox sb(1, 2, 3, aLabel);
}
</div>
具有多重继承的类的构造函数
如果类从多个基类派生,那么将按照派生类声明中列出的顺序调用基类构造函数:
#include <iostream>
using namespace std;
class BaseClass1 {
public:
BaseClass1() {
cout << "BaseClass1 constructor." << endl;
}
};
class BaseClass2 {
public:
B

