C++中的构造函数是一种特殊的成员函数,用于在创建对象时初始化对象。构造函数在对象被创建时自动调用,确保对象在使用前处于有效状态。C++支持多种形式的构造函数,以满足不同的初始化需求。以下是一些常见的构造函数类型:
默认构造函数
默认构造函数是没有参数的构造函数,或者所有参数都有默认值的构造函数。如果类中没有显式定义任何构造函数,编译器会自动生成一个默认构造函数。
class MyClass {
public:
MyClass() { // 默认构造函数
// 初始化代码
}
};参数化构造函数
参数化构造函数是带有参数的构造函数,允许在创建对象时传递初始化值。
class MyClass {
public:
MyClass(int value) { // 参数化构造函数
// 使用value初始化成员变量
}
};拷贝构造函数
拷贝构造函数用于创建一个新对象,作为另一个同类型对象的副本。拷贝构造函数的参数是同类型的引用。
class MyClass {
public:
MyClass(const MyClass& other) { // 拷贝构造函数
// 从other对象复制数据
}
};移动构造函数
移动构造函数用于转移另一个同类型对象的资源,而不是复制资源。它通常用于优化包含动态内存分配的类的性能。
class MyClass {
public:
MyClass(MyClass&& other) noexcept { // 移动构造函数
// 转移other对象的数据
}
};委托构造函数
委托构造函数是一种构造函数,它调用同一个类中的另一个构造函数来初始化对象。
class MyClass {
public:
MyClass() : MyClass(0) { // 委托给参数化构造函数
// 其他初始化代码
}
MyClass(int value) {
// 使用value初始化成员变量
}
};列表初始化构造函数
列表初始化构造函数使用初始化列表来初始化成员变量,这通常比在构造函数体内赋值更高效。
class MyClass {
private:
int a;
double b;
public:
MyClass(int aVal, double bVal) : a(aVal), b(bVal) { // 列表初始化
// 构造函数体可以留空或包含其他代码
}
};显式构造函数
显式构造函数使用explicit关键字,防止构造函数被用于隐式类型转换。
class MyClass {
public:
explicit MyClass(int value) { // 显式构造函数
// 使用value初始化成员变量
}
};虚构造函数
在C++中,构造函数不能是虚函数,但可以调用虚函数。虚构造函数的概念通常用于指代在构造函数中调用虚函数的情况,这可能导致未定义行为,因为对象在构造过程中其虚表可能尚未完全初始化。
禁止赋值和按值传递
在C++中,如果你想要禁止一个类的对象被复制或按值传递,你需要删除拷贝构造函数和拷贝赋值运算符。此外,为了防止移动操作,你还可能需要删除移动构造函数和移动赋值运算符。这样可以确保类的对象不会被复制或移动。 以下是如何实现这一点的示例:
class NonCopyable {
public:
// 删除拷贝构造函数
NonCopyable(const NonCopyable&) = delete;
// 删除拷贝赋值运算符
NonCopyable& operator=(const NonCopyable&) = delete;
// 删除移动构造函数(可选,但推荐)
NonCopyable(NonCopyable&&) = delete;
// 删除移动赋值运算符(可选,但推荐)
NonCopyable& operator=(NonCopyable&&) = delete;
// 默认构造函数
NonCopyable() {
// 初始化代码
}
// 其他成员函数
// ...
};
// 使用示例
int main() {
NonCopyable obj1; // 正确:调用默认构造函数
// NonCopyable obj2 = obj1; // 错误:拷贝构造函数被删除
// NonCopyable obj3(obj1); // 错误:拷贝构造函数被删除
// obj1 = obj2; // 错误:拷贝赋值运算符被删除
return 0;
}在这个示例中,NonCopyable类的拷贝构造函数和拷贝赋值运算符都被标记为delete,这意味着它们不能被使用,尝试复制NonCopyable对象将会导致编译错误。 同样,移动构造函数和移动赋值运算符也被删除,这是为了防止通过移动语义来复制对象。虽然移动操作通常不涉及深拷贝,但在某些情况下,你可能仍然想要禁止移动以保持对象的唯一性。 通过这种方式,你可以确保类的对象不会被复制或按值传递,这对于管理资源(如文件句柄、网络连接等)的类来说是非常有用的,因为这些资源通常不应该被共享或复制。
如果继承自一个已经删除了拷贝构造函数和拷贝赋值运算符的类(如NonCopyable),那么派生类也会继承这些特性,即派生类的对象同样不能被复制和按值传递。 在C++中,如果基类中的某个成员函数被删除或声明为私有,那么在派生类中也无法使用该成员函数,除非派生类提供了自己的实现。由于拷贝构造函数和拷贝赋值运算符是特殊的成员函数,它们遵循同样的规则。 以下是一个示例,展示了如何继承NonCopyable类,并说明派生类的对象也不能被复制和按值传递:
class NonCopyable {
public:
// 删除拷贝构造函数
NonCopyable(const NonCopyable&) = delete;
// 删除拷贝赋值运算符
NonCopyable& operator=(const NonCopyable&) = delete;
// 默认构造函数
NonCopyable() {
// 初始化代码
}
// 其他成员函数
// ...
};
// 派生类
class Derived : public NonCopyable {
public:
// Derived类的构造函数
Derived() {
// 初始化代码
}
// 其他成员函数
// ...
};
// 使用示例
int main() {
Derived obj1; // 正确:调用默认构造函数
// Derived obj2 = obj1; // 错误:拷贝构造函数被删除
// obj1 = obj2; // 错误:拷贝赋值运算符被删除
return 0;
}在这个示例中,Derived类继承自NonCopyable类。由于NonCopyable类已经删除了拷贝构造函数和拷贝赋值运算符,因此Derived类的对象也不能被复制和按值传递。尝试这样做会导致编译错误。 这种设计模式在C++中很常见,特别是当你想要确保类的对象是唯一的,或者当你想要防止资源的意外复制时。通过继承一个非拷贝able的基类,你可以很容易地传播这个特性到整个类层次结构中。
注意事项:
- 构造函数不能有返回类型,即使是
void也不行。 - 构造函数可以重载,即可以定义多个构造函数,只要它们的参数列表不同。
- 如果类中没有显式定义构造函数,编译器会提供默认的构造函数、拷贝构造函数和移动构造函数(如果适用)。
- 如果类中定义了任何构造函数,编译器就不会提供默认构造函数。 了解和正确使用这些构造函数类型是C++编程中的重要部分,它们有助于创建灵活、高效且易于维护的类。

