(42)构造函数

本文详细介绍了构造函数的概念,包括默认构造函数、委托构造函数等,并解释了构造函数在对象初始化过程中的作用,还讨论了如何使用=default来让编译器自动生成构造函数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

每个类都定义了它的对象被初始化的方式,类通过一个或几个特殊的成员函数来控制其对象的初始化过程,这些函数叫做构造函数。构造函数的人物是初始化类对象的数据成员,无论何时只要类的对象被创建,就会执行构造函数。

构造函数的名字和类名相同。和其他函数不一样的是,构造函数没有返回类型,除此之外类似于其他的函数,构造函数也有一个(可能为空的)参数列表和一个(可能为空的)函数体。类可以包含多个构造函数,和其他重载函数差不多,不同的构造函数之间必须在参数数量或参数类型上有所区别。

不同于其他成员函数,构造函数不能被声明成const的。当我们创建类的一个const对象时,直到构造函数完成初始化过程,对象才能真正取得其“常量”属性。因此,构造函数在const对象构造过程中可以向其写值。


合成的默认构造函数

类通过一个特殊的构造函数来控制默认初始化过程,这个函数叫做默认构造函数。默认构造函数无须任何实参。
默认构造函数在很多方面有其特殊性:其中之一是如果我们的类没有显示的定义构造函数没那么编译器会为我们隐式地定义一个默认构造函数。编译器创建的构造函数又被称为合成的默认构造函数。

合成的默认构造函数初始化类的数据成员的规则:
一.如果存在类内的初始值,用它来初始化成员
二.否则默认初始化该成员

对于一个普通的类来说,必须定义它自己的默认构造函数,原因有三:
第一是编译器只有在发现类不包含任何构造函数的情况下才会替我们生成一个默认的构造函数。
第二是对于某些类来说,合成的默认构造函数可能执行错误的操作。(默认初始化将产生未定义值)
第三是有的时候编译器不能为某些类合成默认的构造函数。

=default的含义

Student()=default;

在参数列表后面写上=default来要求编译器生成构造函数。其中,=default既可以和声明一起出现在类的内部,也可以作为定义出现在类的外部。和其他函数一样,如果=default在类的内部,则默认构造函数是内联的,如果在类的外部,则该成员默认情况下不是内联的。


构造函数初始值列表

就对象的数据成员而言,初始化和赋值也有类似的区别。如果没有在构造函数的初始值列表中显示地初始化成员,则该成员将在构造函数体之前执行默认初始化,

有时我们可以忽略数据成员初始化和赋值之间的差异,但并非总能这样。如果成员是const或者引用的话,必须将其初始化。而且我们初始化const或者引用类型的唯一机会是通过构造函数初始值。

成员的初始化顺序与它们在类定义中的顺序一致:第一个成员先被初始化,然后第二个,以此类推。构造函数初始值列表中初始值的前后关系不会影响实际的初始化顺序。

委托构造函数

一个委托构造函数使用它所属类的其他构造函数执行它自己的初始化过程,或者说它把自己的一些(或者全部)职责委托给了其他构造函数。如:
#include<iostream>
using namespace std;
class Student {
	int id;
public:
	Student(int ID) :id(ID) {}
	Student(string s) :Student(2) {}
	void get() {
		cout << id << endl;
	}
};
void main() {
	Student stu1(1);
	Student stu2("s");
	stu1.get();
	stu2.get();
	system("pause");
}

默认构造函数的作用

当对象被默认初始化或值初始化时自动执行默认构造函数。默认初始化在以下情况下发生:
一.当我们在块作用域内不使用任何初始化值定义一个非静态变量或者数组时
二.当一个类本身含有类类型的成员且使用合成的默认构造函数时
三.当类类型的成员没有在构造函数初始值列表中显示地初始化时

值初始化在以下情况发生:
一.在数组初始化的过程中如果我们提供的初始值数量少于数组的大小时
二.当我们不使用初始值定义一个局部静态变量时
三.当我们通过书写形式如T()的表达式显式地请求值初始化时,其中T是类型名

如果想定义一个使用默认构造函数进行初始化对象,正确的方法是去掉对象名之后的空的括号对。

隐式的类类型转换

如果构造函数只接受一个实参,则它实际上定义了转换为此类类型的隐式机制,有时我们把这种构造函数称作转换构造函数,编译器只会自动地执行一步类型转换,而且类类型转换不是总有效。

抑制构造函数定义的隐式转换

在要求隐式转换程序上下文中,我们可以通过将构造函数声明为explicit加以阻止。

关键字explicit只对一个实参的构造函数有效。需要多个实参的构造函数不能用于执行隐式转换,所以无须将这些构造函数指定为explicit的。只能在类内声明构造函数时使用explicit关键字,在类外部定义时不应重复

explicit构造函数只能用于直接初始化。尽管编译器不会将explicit的构造函数用于隐式转换过程,但是我们可以使用这样的构造函数显式地强制进行转换。


标准库中含有显式构造函数的类

标准库类中含有单参数的构造函数:
一.接收一个单参数的const char*的string构造函数不是explicit的
二.接收一个容量参数的vector构造函数是explicit的
### **构造函数委托** #### **1. 什么是构造函数委托?** 构造函数委托是 C++11 引入的一项特性,允许一个类的构造函数调用同一个类中的其他构造函数。通过这种方式,可以减少代码重复,提高代码复用性。 --- ### **2. 示例代码** 以下代码展示了如何使用构造函数委托: ```cpp #include <iostream> using namespace std; class MyClass { private: int x; double y; public: // 主构造函数 MyClass(int a, double b) : x(a), y(b) { cout << "Main Constructor called." << endl; } // 委托构造函数 1:调用主构造函数 MyClass(int a) : MyClass(a, 0.0) { // 委托给 MyClass(int, double) cout << "Delegating Constructor 1 called." << endl; } // 委托构造函数 2:调用另一个委托构造函数 MyClass() : MyClass(42) { // 委托给 MyClass(int) cout << "Delegating Constructor 2 called." << endl; } void display() const { cout << "x: " << x << ", y: " << y << endl; } }; int main() { cout << "Creating object with main constructor:" << endl; MyClass obj1(10, 3.14); obj1.display(); cout << "\nCreating object with delegating constructor 1:" << endl; MyClass obj2(20); obj2.display(); cout << "\nCreating object with delegating constructor 2:" << endl; MyClass obj3; obj3.display(); return 0; } ``` #### **输出结果** ``` Creating object with main constructor: Main Constructor called. x: 10, y: 3.14 Creating object with delegating constructor 1: Main Constructor called. Delegating Constructor 1 called. x: 20, y: 0 Creating object with delegating constructor 2: Main Constructor called. Delegating Constructor 1 called. Delegating Constructor 2 called. x: 42, y: 0 ``` --- ### **3. 解释** #### **(1) 构造函数委托的基本概念** - 在 C++ 中,构造函数不能直接互相调用(如 `this->Constructor()`)。 - 构造函数委托通过在初始化列表中显式调用其他构造函数来实现代码复用。 #### **(2) 委托链的构建** - 构造函数委托支持链式调用。例如,在上述代码中: - `MyClass()` 委托给 `MyClass(int)`。 - `MyClass(int)` 委托给 `MyClass(int, double)`。 - 注意:委托链必须最终指向一个非委托构造函数(即实际完成初始化的构造函数)。 #### **(3) 初始化顺序** - 构造函数委托不会改变对象的初始化顺序。委托构造函数的初始化列表会在被委托构造函数之后执行。 - 示例:`MyClass(int)` 的初始化列表会先执行,然后才是 `MyClass(int)` 的函数体。 #### **(4) 使用场景** - 当多个构造函数共享相同的初始化逻辑时,可以使用构造函数委托来避免重复代码。 - 示例:在上述代码中,`MyClass(int)` 和 `MyClass()` 都依赖于 `MyClass(int, double)` 来完成初始化。 --- ### **4. 注意事项** 1. **委托构造函数的限制**: - 委托构造函数必须出现在初始化列表的第一项。 ```cpp MyClass(int a) : MyClass(a, 0.0), x(a) {} // 错误:委托构造函数必须在初始化列表的第一项 ``` 2. **递归委托**: - 构造函数不能直接或间接地递归委托自身,否则会导致编译错误。 ```cpp MyClass() : MyClass() {} // 错误:递归委托 ``` 3. **与继承的关系**: - 构造函数委托仅限于同一个类内的构造函数之间,不能跨类调用基类或派生类的构造函数。 --- ### **5. 总结** 构造函数委托是 C++11 提供的一项重要特性,用于减少代码重复并提高代码复用性。通过在初始化列表中调用其他构造函数,可以在多个构造函数之间共享初始化逻辑。这种机制不仅简化了代码结构,还增强了程序的可维护性。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值