返回顶部
首页 > 资讯 > 后端开发 > 其他教程 >C++之谈谈构造函数的初始化列表
  • 102
分享到

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

C++构造函数C++构造函数初始化列表C++初始化列表 2023-05-15 05:05:57 102人浏览 独家记忆
摘要

目录一、引入二、初始化的概念区分三、语法格式及使用四、注意事项【⭐】五、总结与提炼一、引入 我们知道,对于下面这个类A的成员变量_a1和_a2属于【声明】,还没有在内存中为其开辟出一

一、引入

  • 我们知道,对于下面这个类A的成员变量_a1_a2属于【声明】,还没有在内存中为其开辟出一块空间以供存放,真正开出空间则是在【定义】的时候,那何时定义呢?也就是使用这个类A去实例化出对象的时候
  • 这个对象的空间被开出来了,难道里面的成员变量就一定开出空间了吗?这一点我们很难去通过调试观察
class A {
public:
	int _a1;	//声明
	int _a2;
};
int main(void)
{
	A aa;	//	对象整体的定义,每个成员什么时候定义?
	return 0;
}

如果现在我在类A中加上一个const成员变量的话,初始化的时候似乎就出现了问题

const int _x;	

在这里插入图片描述

  • 在搞清楚上面的问题之前你要明白const修饰的变量有哪些特点
const int i;
  • 可以看到我在这里定义了一个整型变量i,它前面是用const进行修饰的,不过编译后报出了错误说【必须初始化常量对象】,因为对于const修饰的变量在声明的时候是必须要去进行初始化的,也就是要给到一个值

在这里插入图片描述

现在我们就可以来聊聊有关上面的成员变量_x为什么没有被初始化的原因了👇

  • 之前有讲过,若是我们自己不去实现构造函数的话,类中会默认提供一个构造函数来初始化成员变量,对于【内置类型】的变量不会处理,对【自定义类型】的变量会去调用它的构造函数。那么对于这里的_a1_a2_x都属于内置类型的数据,所以编译器不会理睬,可是呢const修饰的变量又必须要初始化,这个时候该怎么办呢╮(╯▽╰)╭

💬有同学说:这还不简单,给个缺省值不就好了

这位同学说的不错,这个办法确实是可以解决我们现在的问题,因为c++11里面为内置类型不初始化打了一个补丁,在声明的位置给到一个初始化值,就可以很好地防止编译器不处理的问题

在这里插入图片描述

但是现在我想问一个问题:如果不使用这个办法呢?你有其他方法吗?难道C++11以前就那它没办法了吗?

底下的同学确实想不出什么很好的解决办法,于是这个时候就要使用到本模块要学习的【初始化列表】了

二、初始化的概念区分

  • 在了解【初始化列表】前,你要先知道初始化的真正含义是什么

概念:在创建对象时,编译器通过调用构造函数,给对象中各个成员变量一个合适的初始值。

class Date
{
public:
	Date(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
private:
	int _year;
	int _month;
	int _day;
};
  • 上面这个Date类是我们之前写过的,这里有一个它的有参构造函数,虽然在这个构造函数调用之后,对象中已经有了一个初始值,但是不能将其称为对对象中成员变量的初始化。构造函数体中的语句只能将其称为【赋初值】,而不能称作初始化。因为初始化只能初始化一次,而构造函数体内可以多次赋值

三、语法格式及使用

【初始化列表】:以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个"成员变量"后面跟一个放在括号中的初始值或表达式

  • 下面就是它的具体用法,这样便可以通过外界传入一些参数对年、月、日进行初始化
class Date
{
public:
	Date(int year, int month, int day)
		:_year(year)
		, _month(month)
		, _day(day)
	{}
private:
	int _year;
	int _month;
	int _day;
};
int main(void)
{
	Date d(2023, 3, 30);
	return 0;
}

可以通过调试来观察一下它到底是怎么走的

在这里插入图片描述

接下去我再来说说这一块的难点所在,准备好头脑风暴🌊

  • 还是看回到我们上面的这个类A,知道了【初始化列表】这个东西,此时就不需要再声明的部分给缺省值了,直接使用初始化列表即可。不过可以看到,对于_a1_a2我给到了缺省值,写了初始化列表后,它们还会被初始化吗?
class A {
public:
	A()
		:_x(1)
	{}
private:
	int _a1 = 1;	//声明
	int _a2 = 1;

	const int _x;
};

也通过调试来看一下

在这里插入图片描述

  • 可以看到,即使在初始化列表没有给到_a1_a2的初始化,还是会通过给到的默认缺省值去进行一个初始化。根据上面所学,我给出以下的结论
  • 哪个对象调用构造函数,初始化列表是它所有成员变量定义的位置
  • 不管是否显式在初始化列表写,编译器都会为每个变量在初始化列表进行初始化

好,接下去难度升级,请问初始化列表修改成这样后三个成员变量初始化后的结果会是什么呢? 会是1、2、1吗?

class A {
public:
	A()
		:_x(1)
		,_a2(1)
	{}
private:
	int _a1 = 1;	//声明
	int _a2 = 2;

	const int _x;
};

一样通过调试来看看

在这里插入图片描述

  • 可以观察到,最后初始化完后的结果为1、1、1,最令你困惑的应该就是这个_a2了,因为我在声明的时候给到了缺省值,然后初始化列表去进行定义的时候又去进行了一次初始化,最后的结果以初始化列表的方式为主

这里要明确的一个概念是,缺省参数只是一个备份,若是我们没有去给到值初始化的话,编译器就会使用这个初始值,若是我们自己给到了明确的值的话,不会去使用这个缺省值了【如果不清楚看看C++缺省参数】

接下去难度继续升级,请问下面这样初始化后的结果是多少?

  • 可以看到对于构造函数我不仅写了【初始化列表】,而且在函数体内部还对_a1_a2进行了++和- -,那此时会有什么变化呢?
class A {
public:
	A()
		:_x(1)
		,_a2(1)
	{
		_a1++;
		_a2--;
	}
private:
	int _a1 = 1;	//声明
	int _a2 = 2;

	const int _x;
};

如果对于上面的原理搞清楚了,那看这个就相当于是再巩固了一遍。也是一样,无论是否给到缺省值都会去初始化列表走一遍,若是构造函数内部有语句的话就会执行

在这里插入图片描述

四、注意事项【⭐】

清楚了初始化列表该如何使用,接下去我们来说说其相关的注意事项

  • 每个成员变量在初始化列表中只能出现一次(初始化只能初始化一次)
    • 可以看到,若是一个成员变量在初始化列表的地方出现了两次,编译器在编译的时候就会报出【xxx已初始化

在这里插入图片描述

  • 类中包含以下成员,必须放在初始化列表位置进行初始化:

const成员变量

  • 这个在前面已经说到过了,const修饰的成员变量和构造函数对于内置类型不做处理产生了一个冲突,因此祖师爷就提出了【初始化列表】这个概念

引用成员变量

  • 第二点就是对于引用成员变量,如果有点忘记了看看C++引用通过编译可以看出,这个引用型成员变量_z需要被初始化,它必须要引用一个值

在这里插入图片描述

没有默认构造的自定义类型成员(写了有参构造编译器就不会提供默认构造)

  • 此时,我又写了一个类B,将它定义出的对象作为类A的成员变量,在类B中,有一个无参的默认构造,也写了相关的初始化列表去初始化_b
class B {
public:
	B()
		:_b(0)
	{}
private:
	int _b;
};
class A {
public:
	A()
		:_x(1)
		,_a1(3)
		,_a2(1)
		,_z(_a1)
	{
		_a1++;
		_a2--;
	}
private:
	int _a1 = 1;	//声明
	int _a2 = 2;

	const int _x;
	int& _z;
	B _bb;
};
  • 通过调试来观察就可以看到,完全符合我们前面所学的知识,若是当前类中有自定义类型的成员变量,那在为其进行初始化的时候会去调用它的默认构造函数

在这里插入图片描述

  • 但是现在我对这个构造函数做了一些改动,将其变为了有参的构造函数,此时编译时就报出了【没有合适的默认构造函数可用
  • 我们知道默认构造有:无参、全缺省和编译器自动生成的,都是不需要我们手动去调的。可以看到若是我在这里将其改为全缺省的话,就不会出问题了,因为它属于默认构造函数

在这里插入图片描述

💬那对于有参构造该如何去初始化呢?

还是可以利用到我们的【初始化列表】

在这里插入图片描述
通过调试来看看编译器是如何走的

在这里插入图片描述

  • 尽量使用初始化列表初始化,因为不管你是否使用初始化列表,对于自定义类型成员变量,一定会先使用初始化列表初始化

看完了上面这一种,我们再来看看稍微复杂一些的自定义类型是否也遵循这个规则

  • 也就是我们之前写过的Stack和MyQueue类
typedef int DataType;
class Stack
{
public:
	Stack(size_t capacity = 10) 	//全缺省构造
	{
		cout << "Stack()构造函数调用" << endl;
		_array = (DataType*)malloc(capacity * sizeof(DataType));
		if (nullptr == _array)
		{
			perror("malloc申请空间失败");
			return;
		}
		_size = 0;
		_capacity = capacity;
	}
	//....
private:
	DataType* _array;
	size_t _size;
	size_t _capacity;
};
  • 此处我们主要观察Stack类的构造函数,因为在MyQueue中我没有写构造函数,为的就是使用它默认生成的构造函数去进行初始化。对于【内置类型】不做处理,不过我这里给到了一个缺省值,对于【自定义类型】会去调用它的默认构造

class MyQueue{
public:
	//默认生成构造函数

private:
	Stack _pushST;
	Stack _popST;
	size_t _t = 1;
};

int main(void)
{
	MyQueue MQ;
	
	return 0;
}

可能读者有所忘却,我们再通过调试来看一下

在这里插入图片描述

  • 可以观察到在初始化MyQueue类的对象时,因为内部有两个Stack类型的对象,所以就会去调用两次Stack类默认构造来进行初始化
  • 那此时我若是将这个默认构造(全缺省构造)改为有参构造吗,它还调得动吗?
Stack(size_t capacity)
  • 可以看到,此时就报出了我们上面有类似遇到过的【无法引用默认构造函数】,为什么呢?原因就在于我们写了,编译器自动生成的也就不存在了,但是我又没有传入对应的参数

在这里插入图片描述

  • 此时就可以使用到我们本模块所学习的【初始化列表】了,将需要定义的值放在初始化列表,相当于就是为Stack类传入了一个有参构造的参数,不过对于没有写在这里的_t,依旧会使用我给到的初始值1
MyQueue()
	:_pushST(10)
	,_popST(10)
{}

可以通过调试再来看看

在这里插入图片描述

  • 当然,如果你觉得不想要这个固定的10作为栈容量的话,也可以将这个MyQueue的构造函数设定为有参,自己传递进去也是可以的

在这里插入图片描述

  • 最后再来看一下无参构造,也是默认构造的一种,在这里编译器也会去走MyQueue的初始化列表进行初始化
//无参构造MyQueue(){<!--{cke_protected}{C}%3C!%2D%2D%20%2D%2D%3E-->}

所以可以看出,对于【内置类型】不做处理,【自定义类型】会调用它的默认构造可以看出其实就是当前类构造函数的初始化列表在起作用

在这里插入图片描述

在看了MyQueue类各种初始化列表的方式后,其实也可以总结出一点,无论如何不管有没有给到缺省值,只要是显式地写了一个构造函数,就可以通过调试去看出编译器都会通过【初始化列表】去进行一个初始化

  • 成员变量在类中声明次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后次序无关
  • 最后再来看第四点,你认为下面这段代码最后打印的结果会是多少呢?1 1 吗?
class A
{
public:
    A(int a)
       :_a1(a)
       ,_a2(_a1)
   {}
    
    void Print() {
        cout<<_a1<<" "<<_a2<<endl;
   }
private:
    int _a2;
    int _a1;
};
int main() {
    A aa(1);
    aa.Print();
}

但结果却和我们想象的不一样,_a1是1,_a2却是一个随机值,这是为什么呢?

在这里插入图片描述

  • 通过调试可以发现,似乎是先初始化的_a2再去初始化的_a1,对于【内置类型】我们可以知道是编译器是不会去进行初始化的,那若是一开始使用_a1去初始化_a2的时候,那_a2就会是一个随机值,但是_a1却使用传入进来的形参a进行了初始化,那它的值就是1

在这里插入图片描述

  • 此时我们只需要让_a1先进行初始化即可,就不会造成随机值的现象了

在这里插入图片描述

现在你在翻上去把所有的调试图一幅幅看下来就可以发现出初始化列表是存在顺序的,它的顺序不是在列表中谁先谁后的顺序,而是类的成员变量声明的顺序

五、总结与提炼

最后来总结一下本文所学习的内容📖

面对必须在声明时期初始化的成员函数,我们引入了初始化列表这个东西,知道了祖师爷在构造函数中还做了这么个小文章😄。有了它,我们就再也不用担心成员变量不会被初始化的问题了,无论是你是否给到缺省值,编译器都会去走一遍构造函数的初始化列表,若是没有在定义处给到初始值,就会采用缺省值;若是给到了初始值就会采用这个值

不仅如此,初始化列表还有很多的注意事项:

  • 首先就是每个成员只能初始化一次,可以不要初始化多次哦
  • 其次就是对于三类成员一定要在初始化列表进行初始化:包括const修饰的成员变量、引用类型成员、无默认构造函数的自定义成员变量
  • 然后尽量使用初始化列表初始化,因为无论如何编译器一定会走初始化列表,声明时期的缺省值其实就是给到初始化列表使用的
  • 最后就是初始化列表中的初始化顺序,与定义处的顺序是无关的,和声明处的顺序有关

初始化列表是构造函数这一块的难点,也是祖师爷面对C++某些地方缺陷设计出来的,搞懂之后就会豁然开朗了

以上就是C++之谈谈构造函数的初始化列表的详细内容,更多关于C++构造函数的资料请关注编程网其它相关文章!

--结束END--

本文标题: C++之谈谈构造函数的初始化列表

本文链接: https://lsjlt.com/news/208455.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • C++之谈谈构造函数的初始化列表
    目录一、引入二、初始化的概念区分三、语法格式及使用四、注意事项【⭐】五、总结与提炼一、引入 我们知道,对于下面这个类A的成员变量_a1和_a2属于【声明】,还没有在内存中为其开辟出一...
    99+
    2023-05-15
    C++构造函数 C++构造函数初始化列表 C++初始化列表
  • C++构造函数的初始化列表详解
    目录1.问题2.解决方法(初始化列表)3.顺序问题总结 1.问题 class A { private: int m_a; public: A(int a) { cout ...
    99+
    2024-04-02
  • C++是怎么构造函数的初始化列表
    C++是怎么构造函数的初始化列表,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。1.问题class A {private:int m_a;publi...
    99+
    2023-06-22
  • 怎么使用c#构造函数初始化列表
    在C#中,可以使用构造函数初始化列表来初始化类的成员变量。构造函数初始化列表是在构造函数的参数列表后面使用冒号来定义的。下面是一个示...
    99+
    2023-09-28
    c#
  • c++基础语法:构造函数初始化列表
    C++为类中提供类成员的初始化列表 类对象的构造 顺序是这样的:1.分配内存,调用构造函数 时,隐式/显示的初始化各数据 成员2.进入构造函数后在构造函数中执行一般计算 使用初始化...
    99+
    2022-11-15
    构造函数 初始化列表
  • C++构造函数初始化列表的实现详解
    目录1.前言2.初始化列表3.注意事项1.前言 初始化就是给变量一个初始值。 初始化的目的是为了让变量有值,防止使用时出现异常。 在构造函数中,有一项重要功能就是对成员变量进行初始化...
    99+
    2024-04-02
  • c++构造函数初始化列表的方法是什么
    在C++中,构造函数初始化列表是一种在构造函数中初始化成员变量的方法。它使得在对象创建时直接初始化成员变量,而不是在构造函数体中进行...
    99+
    2024-03-12
    c++
  • C++类初探常量成员函数与构造函数初始值列表分析
    这篇文章主要讲解了“C++类初探常量成员函数与构造函数初始值列表分析”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“C++类初探常量成员函数与构造函数初始值列表分析”吧!头文件mycpp.h#...
    99+
    2023-06-04
  • C++初始化函数列表详细解析
    在以下三种情况下需要使用初始化成员列表: 一,需要初始化的数据成员是对象的情况; 二,需要初始化const修饰的类成员; 三,需要初始化引用成员数据; 原因:C++可以定义引用类型的...
    99+
    2022-11-15
    初始化函数列表
  • C++11系列学习之列表初始化
    目录前言:旧标准初始化方式C++11标准初始化方式初始化列表技术细节总结前言: 由于旧标准初始化方式太过繁杂,限制偏多,因此在新标准中统一了初始化方式,为了让初始化具有确定的效果,于...
    99+
    2024-04-02
  • 解析之C++的列表初始化语法
    目录聚合初始化大括号省略(brace elision)std::initializer_list的另一个故事连《Effective Modern C++》都弄错了的规则构造函数的两步...
    99+
    2024-04-02
  • C++学习之初始化列表详解
    目录前言一、类的初始化表二、initializer_list前言 本文主要介绍C++中地初始化列表 目前对初始化列表应该有两个方面的定义,一个是类的构造函数中使用的那个初始化表,另一...
    99+
    2023-03-19
    C++初始化列表 C++ 列表
  • C++学习笔记之初始化列表
    目录一、用初始化列表初始化对象1.初始化列表用法2.初始化列表特性二、explicit关键字1.内置类型的隐式转换2.如何避免单参构造函数初始化发生隐式类型转换三、匿名对象1.匿名对...
    99+
    2023-05-17
    c++ 初始化列表 如何初始化列表 c++ 初始化
  • 老生常谈java中的数组初始化
    数组的初始化可以分为两种:静态初始化动态初始化静态初始化:例:String[] str = new String[]{"A","B","C"};String str[] = new String[]{"A","B","C"};String ...
    99+
    2023-05-31
    java 数组 初始化
  • 浅谈JVM之类的加载链接和初始化
    目录加载运行时常量池类加载器链接验证准备解析初始化总结加载 JVM可以分为三大部分,五大空间和三大引擎,要讲起来也不是特别复杂,先看下面的总体的JVM架构图。 从上面的图中,我们可...
    99+
    2024-04-02
  • C++11列表初始化是怎样的
    这篇文章主要介绍“C++11列表初始化是怎样的”,在日常操作中,相信很多人在C++11列表初始化是怎样的问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”C++11列表初始化是怎样的”的疑惑有所帮助!接下来,请跟...
    99+
    2023-06-19
  • c++中类的构造函数可以初始化静态成员变量吗
    c++ 中类的构造函数可以初始化静态成员变量。初始化语法如下:在构造函数中使用 staticmembervariable = ...; 声明。静态成员变量仅在第一次执行构造函数时初始化一...
    99+
    2024-05-09
    c++
  • C++11新特性之列表初始化的具体使用
    目录统一的初始化方法列表初始化的一些使用细节初始化列表1、任何长度的初始化列表2、std::initialzer-list的使用细节列表初始化防止类型收窄在我们实际编程中,我们经常会...
    99+
    2024-04-02
  • C++初始化列表的方法有哪些
    本篇内容介绍了“C++初始化列表的方法有哪些”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!一、类的初始化表首先是类中使用构造函数时的初始化表...
    99+
    2023-07-05
  • 浅谈Python类里的__init__方法函数,Python类的构造函数
    如果某类里没有__init__方法函数,通过类名字创建的实例对象为空,切没有初始化;如果有此方法函数,通常作为类的第一个方法函数,有点像C++等语言里的构造函数。 class Ca: def __ini...
    99+
    2022-06-04
    函数 浅谈 方法
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作