Bootstrap

【C++】再探构造函数 - 初始化列表详解

📢博客主页:https://blog.csdn.net/2301_779549673
📢欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正!
📢本文由 JohnKi 原创,首发于 CSDN🙉
📢未来很长,值得我们全力奔赴更美好的生活✨

在这里插入图片描述

在这里插入图片描述


📢前言

C++ 构造函数

在 C++ 中,构造函数是一种特殊的成员函数,用于在创建对象时进行初始化操作。

构造函数具有与类名相同的名称,没有返回值类型。它可以有参数,以便在创建对象时传递必要的初始值。

例如,对于一个 Student 类,可能有一个构造函数 Student(int age, string name) 来初始化学生的年龄和姓名。

构造函数的主要作用是确保对象在创建时处于有效和合理的初始状态。这有助于提高程序的可靠性和可读性。

它可以自动被调用,无需显式调用。通过合理设计构造函数,可以避免对象处于未定义或错误的初始状态,从而减少潜在的错误和异常。


🏳️‍🌈 构造函数特点回顾

在上篇博客中,笔者介绍了构造函数的几种基本用法及特点,这里简单总结概括一下

构造函数的特点:

  1. 函数名与类名相同。
  2. 无返回值。(返回值啥都不需要给,也不需要写void,不要纠结,C++规定如此
  3. 对象实例化时系统会自动调用对应的构造函数。
  4. 构造函数可以重载
  5. 如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数,一旦用户显式定义编译器将不再生成。
  6. 无参构造函数、全缺省构造函数、我们不写构造时编译器默认生成的构造函数,都叫做默认构造函数。但是这三个函数有且只有一个存在,不能同时存在。无参构造函数和全缺省构造函数虽然构成函数重载,但是调用时会存在歧义。要注意很多同学会认为默认构造函数是编译器默认生成那个叫默认构造,实际上无参构造函数、全缺省构造函数也是默认构造,总结一下就是不传实参就可以调用的构造就叫默认构造。
  7. 我们不写,编译器默认生成的构造,对内置类型成员变量的初始化没有要求,也就是说是是否初始化是不确定的,看编译器。对于自定义类型成员变量,要求调用这个成员变量的默认构造函数初始化。如果这个成员变量,没有默认构造函数,那么就会报错,我们要初始化这个成员变量,需要用初始化列表才能解决。

说明:C++把类型分成内置类型(基本类型)和自定义类型。内置类型就是语言提供的原生数据类型,如:int/char/double/指针等,自定义类型就是我们使用class/struct等关键字自己定义的类型。

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
class Date
{
public:
	// 1.⽆参构造函数
	// 可以认为是默认构造
	Date()
	{
		_year = 1;
		_month = 1;
		_day = 1;
	}

	// 2.带参构造函数
	Date(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}

	// 3.全缺省构造函数
	// 可以认为是默认构造
	Date(int year = 1, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}

	void Print()
	{
		cout << _year << "/" << _month << "/" << _day << endl;
	}

	int GetYear()
	{
		return _year;
	}
private:
	int _year;
	int _month;
	int _day;
};

🏳️‍🌈 初始化列表

C++ 构造函数的初始化列表

在 C++ 中,构造函数的初始化列表是在构造函数的参数列表之后、函数体开始之前,用于对类的数据成员进行初始化的部分。

它以冒号开头,后面依次列出需要初始化的数据成员及其初始值。例如:Student::Student(int age, string name) : age_(age), name_(name) {}

初始化列表的主要优势在于效率更高,尤其对于引用和 const 成员,必须通过初始化列表进行初始化。

而且对于类成员对象,使用初始化列表能避免二次初始化。例如,一个类中包含另一个类的对象作为成员,通过初始化列表能确保其正确初始化。初始化列表使得初始化过程更加清晰和直观,有助于提高代码的可读性和可维护性。

❤️ 引用, const 成员和类类型对象

每个成员变量在初始化列表中只能出现一次,语法理解上初始化列表可以认为是每个成员变量定义初始化的地方。

其中引用成员变量const成员变量没有默认构造的类类型变量,必须放在初始化列表位置进行初始化,否则会编译报错。

以下是举例说明

class Date
{
public:
	//初始化列表
	Date(int& xx, int year, int month, int day)
		:_year(year)
		, _month(month)
		, _day(day)

		//, _year(year)  无法实现,只能初始化一次

		, _n(1)//const成员只能在列表初始化时初始化

		, _ref(xx)//引用成员只能在列表初始化时初始化

		, _t(1)

		, _ptr((int*)malloc(12))
	{

		//引用成员变量,const成员变量,没有默认构造的类类型变量,必须放在初始化列表位置进行初始
		//化,否则会编译报错。
		// error C2512: “Time”: 没有合适的默认构造函数可⽤
		// error C2530 : “Date::_ref” : 必须初始化引⽤
		// error C2789 : “Date::_n” : 必须初始化常量限定类型的对象

		if (_ptr == nullptr)
		{
			perror("malloc fail");
		}
		else
			memset(_ptr, 0, 12);
	}

	void Print() const
	{
		cout << _year << "/" << _month << "/" << _day << endl;
	}

private:
	//声明

	//普通成员可初始化,可不初始化
	int _year;
	int _month;
	int _day;

	//特殊成员必须初始化
	const int _n;		//const成员只能在列表初始化时初始化

	int& _ref;		//引用成员只能在列表初始化时初始化

	Time _t;	//假如没有合适的默认构造函数,就必须传值

	int* _ptr;
};

注意: 初始化列表的成员初始化顺序是按照成员变量在类中的声明顺序,而不是初始化列表中的顺序。

💛避免默认初始化

对于没有默认构造函数的成员变量,必须在初始化列表中进行初始化。

比如说前面代码中刚出现的

C++11支持在成员变量声明的位置给缺省值,这个缺省值主要是给没有显示在初始化列表初始化的成员使用的。

尽量使用初始化列表初始化,因为那些你不在初始化列表初始化的成员也会走初始化列表,如果这个成员在声明位置给了缺省值,初始化列表会用这个缺省值初始化。如果你没有给缺省值,对于没有显示在初始化列表初始化的内置类型成员是否初始化取决于编译器,C++并没有规定。对于没有显示在初始化列表初始化的自定义类型成员会调用这个成员类型的默认构造函数,如果没有默认构造会编译错误。


👥总结


本篇博文对 初始化列表 做了一个较为详细的介绍,不知道对你有没有帮助呢

觉得博主写得还不错的三连支持下吧!会继续努力的~

请添加图片描述

;