iis服务器助手广告广告
返回顶部
首页 > 资讯 > 后端开发 > 其他教程 >C++学习笔记之初始化列表
  • 520
分享到

C++学习笔记之初始化列表

c++ 初始化列表如何初始化列表c++ 初始化 2023-05-17 08:05:46 520人浏览 薄情痞子
摘要

目录一、用初始化列表初始化对象1.初始化列表用法2.初始化列表特性二、explicit关键字1.内置类型的隐式转换2.如何避免单参构造函数初始化发生隐式类型转换三、匿名对象1.匿名对

创建一个类对象时,编译器通过调用构造函数,给类对象中各个成员变量赋初值:

class Date
{
public:
	//构造函数
	Date(int year = 2022, int month = 4, int day = 19)
	{
		_year = year;
		_month = month;
		_day = day;
	}
 
private:
	int _year;
	int _month;
	int _day;
};

但上述赋初值不能称作类对象成员的初始化,因为构造函数体内可以多次赋值:

class Date
{
public:
	//构造函数
	Date(int year = 2022, int month = 4, int day = 19)
	{
		_year = year;
		_month = month;
		_day = day;
        _year = 2023;//构造函数体内允许对成员变量进行多次赋值
	}
 
private:
	int _year;
	int _month;
	int _day;
};

而初始化列表能只能初始化一次。

一、用初始化列表初始化对象

1.初始化列表用法

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

class Date
{
public:
	//构造函数
	Date(int year = 2022, int month = 4, int day = 19)
		:_year(year) //初始化列表初始化
		,_month(month)
		,_day(day)
	{}
 
private:
	int _year;
	int _month;
	int _day;
};

2.初始化列表特性

(1)初始化列表能只能初始化一次,多次初始化会报错:

class Date
{
public:
	//构造函数
	Date(int year = 2022, int month = 4, int day = 19)
		:_year(year) 
		,_month(month)
		,_day(day)
        ,_month(month) //初始化列表多次初始化
	{}
 
private:
	int _year;
	int _month;
	int _day;
};

编译器也允许构造函数赋初值和初始化列表初始化混用:

class Date
{
public:
	//构造函数
	Date(int year = 2022, int month = 4, int day = 19)
		:_year(year)  //两者混用
		,_month(month)
	{
		_day = day;
	}
 
private:
	int _year;
	int _month;
	int _day;
};

混用时初始化列表初始化和构造函数赋初值不冲突:

class Date
{
public:
	//构造函数
	Date(int year = 2022, int month = 4, int day = 19)
		: _year(year) //两者不冲突
		, _month(month)
	{
		_day = day;
        _year = 2023;
	}
 
private:
	int _year;
	int _month;
	int _day;
};

但混用时初始化列表初始化还是要遵循只能初始化一次成员变量的原则:

class Date
{
public:
	//构造函数
	Date(int year = 2022, int month = 4, int day = 19)
		: _year(year) //初始化列表初始化
		, _month(month)
		, _year(2023)  //_year在初始化列表里被初始化了两次,不允许
	{
		_day = day;
	}
 
private:
	int _year;
	int _month;
	int _day;
};

(2)const成员变量、引用成员变量、没有默认构造函数的自定义类型成员只能在初始化列表初始化。

①const成员变量必须在定义的时候初始化

class Date
{
public:
	//构造函数
	Date(int year = 2022, int month = 4, int day = 19)
		: _year(year) 
		, _month(month)
		, _n(2) //const成员变量必须使用初始化列表进行初始化
	{
		_day = day;
		//_n = 2; //const成员变量不能在函数体内初始化
	}
 
private:
	int _year;
	int _month;
	int _day;
 
	const int _n = 1;
};

②引用成员变量必须在定义的时候初始化

class Date
{
public:
	//构造函数
	Date(int year = 2022, int month = 4, int day = 19)
		: _year(year) 
		, _month(month)
		,_ref(year)//引用成员变量要在初始化列表初始化
	{
		_day = day;
		//_ref = year; //引用成员变量不能在函数体内初始化
	}
 
private:
	int _year;
	int _month;
	int _day;
 
	int& _ref;
};

③没有默认构造函数的自定义类型成员变量

#include <iOStream>
using namespace std;
 
class A
{
public:
	
	//默认构造函数是不用传参就可以调用的构造函数,有3种:
	//1.无参默认构造函数
	//2.带参全缺省的默认构造函数
	//3.我们不写,编译器自动生成的默认构造函数
 
	A(int x)//不属于以上任何一种,所以A类的对象没有默认构造函数
	{
		cout << "A(int x)" << endl;
		_x = x;
	}
 
private:
	int _x;
};
 
class Date
{
public:
	//构造函数
	Date(int year = 2022, int month = 4, int day = 19)
		: _year(year) 
		, _month(month)
		, _a(20)//没有默认构造函数的自定义类型成员变量必须在初始化列表进行初始化
	{
		_day = day;
	}
 
private:
	int _year;
	int _month;
	int _day;
 
	A _a;
};

const成员变量、引用成员变量、没有默认构造函数的自定义类型成员变量必须在初始化列表内初始化的原因:

①初始化列表是对象的成员变量定义的地方。

②对象的内置类型成员变量在初始化列表定义时没有要求必须初始化,因此既可以在初始化列表进行初始化,也可以在构造函数体内初始化。

③而const成员变量、引用成员变量、没有默认构造函数的自定义类型成员变量不能先定义再初始化,它们在初始化列表内定义,并且必须在定义时就初始化,因此必须在初始化列表内初始化。

(3) 尽量使用初始化列表初始化,因为不管是否使用初始化列表,虽然对于内置类型没有差别,但是对于自定义类型成员变量,一定会先使用初始化列表初始化。

为什么会先使用初始化列表初始化?

如下,Date类没有默认构造函数,因为26行的构造函数不属于默认构造函数中的任意一种,在对Date类的对象d进行初始化时,会调用Date类的默认构造函数,所以对象d的day实参12和hour实参12都没有被传进去,_t作为Date类的自定义类型成员变量会调用Time类的默认构造函数,_hour默认传参为0,因此打印_hour的值也为0,d的参数没有传成功:

#include<iostream>
using namespace std;
 
class Date; // 前置声明
class Time
{
	friend class Date; // 声明日期类为时间类的友元类,则在日期类中就直接访问Time类中的私有成员变量
public:
	Time(int hour = 0)
		: _hour(hour)
	{
		cout << _hour << endl;
	}
 
private:
	int _hour;
};
 
class Date
{
public:
	Date(int day, int hour)
	{}
 
private:
	int _day;
	Time _t;
};
 
int main()
{
	Date d(12, 12);
	return 0;
}

假如Date类的构造函数不使用初始化列表进行初始化,使用函数体内初始化时,要把Date类的构造函数的形参hour的值给d,那么就必须构造一个Time类对象t,对该对象传参传hour,再使用赋值运算符重载函数将对象t拷贝给_t:

#include<iostream>
using namespace std;
 
class Date; // 前置声明
class Time
{
	friend class Date; // 声明日期类为时间类的友元类,则在日期类中就直接访问Time类中的私有成员变量
public:
	Time(int hour = 0)
		: _hour(hour)
	{
		cout << _hour << endl;
	}
 
private:
	int _hour;
};
 
class Date
{
public:
    //自定义类型,不使用初始化列表,就需要使用构造函数 + operator=
	Date(int day, int hour)
	{
		//函数体内初始化
		Time t(hour);//调用Time类的构造函数
		_t = t;
 
		_day = day;
	}
 
private:
	int _day;
	Time _t;
};
 
int main()
{
	Date d(12, 12);
	cout << 4 << endl;
	return 0;
}

这还不如直接使用使用初始化列表初始化呢,还不需要赋值运算符重载函数:

class Date
{
public:
    //自定义类型,使用初始化列表,只需要构造函数
	Date(int day, int hour)
        :_t(hour)
	{
		_day = day;
	}
 
private:
	int _day;
	Time _t;
};

因此,建议尽量直接使用初始化列表进行初始化。 

(4)成员变量初始化的顺序就是成员变量在类中的声明次序,与初始化列表中的先后次序无关。

如下代码,类成员变量中先声明了_a2,再声明了_a1,因此初始化的顺序是先初始化_a2,再初始化_a1:

#include <iostream>
using namespace std;
class A
{
public:
	A(int a)
		: _a1(a)
		, _a2(_a1)
	{}
 
	void Print()
	{
		cout << _a1 << " " << _a2 << endl;
	}
private:
	int _a2;//先声明_a2
	int _a1;//后声明_a1
};
 
int main() {
	A aa(1);
	aa.Print();
}

先声明_a2就会先初始化_a2,用_a1初始化_a2,由于此时_a1还是随机值,因此_a2的值也是随机值,_a1使用a的值1进行初始化,因此,_a1的值为1:

所以,建议类中的成员变量声明的顺序和初始化列表中初始化的顺序一致。 

二、explicit关键字

1.内置类型的隐式转换

int i = 0;
double d = i;//隐式类型转换

根据监视可以看出: 

double d = i;并不是将i直接赋值给d,而是用i创建一个临时变量,再把临时变量的值给d,那么d改变的是临时变量的值,而不是i的值,因为程序执行完毕后,i的值并未发生改变。

如果d作为引用,那么必须加上const关键字进行修饰,因为d不是i的引用,是临时变量的引用,而临时变量具有常性,不允许引用权限放大。

int i = 0;
const double& d = i;//d引用了临时变量,临时变量具有常性,所以d也必须具有常性

2.如何避免单参构造函数初始化发生隐式类型转换

正常的类对象初始化如下面的aa1,也可以使用拷贝构造初始化,如aa2。由于c++支持隐式类型转换,因此也支持单参数构造函数初始化,如aa3:

#include<iostream>
using namespace std;
 
class A
{
public :
	A(int a)
		:_a(a)
	{}
 
private:
	int _a;
};
 
int main()
{
	A aa1(1);//构造aa1对象
	A aa2(aa1);//拷贝构造,程序没写拷贝构造,编译器会自动生成拷贝构造函数,对内置类型完成浅拷贝
 
	A aa3 = 3;//单参数的构造函数,会发生隐式类型转换
 
	return 0;
}

那么

A aa3 = 3;

是如何支持类型转换的呢?

对于自定义类型A,aa3是A类型,3是整形。编译器会先拿A构造一个临时对象temp,3作为参数传给这个临时对象temp,再拿aa3(temp)去拷贝构造,发生隐式类型转换,即先构造,再拷贝构造:

//A aa3 = 3;  
A temp(3);   //先构造
A aa3(temp); //再拷贝构造

不过现在的编译器已经优化过了,会直接调用构造函数A aa(3)。

如果不想让单参数的构造函数发生隐式类型转换,可以使用explicit关键字修饰构造函数,表明该构造函数是显式的,而不是隐式的,就会避免发生不期望的类型转换,使用场景如下:

#include<iostream>
using namespace std;
 
class A
{
public:
	A(int a)
		:_a(a)
	{}
 
private:
	int _a;
};
 
int main()
{
	A aa1(1);//构造aa1对象
	A aa2(aa1);//拷贝构造,程序没写拷贝构造,编译器会自动生成拷贝构造函数,对内置类型完成浅拷贝
 
	A aa3 = 'x';//先拿A构造一个临时对象temp,字符x作为参数传给这个临时对象temp,会发生隐式类型转换,再拿aa3(temp)去拷贝构造
 
	return 0;
}

aa3作为A类的对象,构造时传参应该传int型,但却传了char型,由于发生隐式类型转换,因此编译也没毛病,但是它传参就是不伦不类。这时候可以给A的构造函数加上explicit声明不让该单参构造函数发生隐式类型转换,编译就会报错:

class A
{
public:
	explicit A(int a)
		:_a(a)
	{}
 
private:
	int _a;
};

 这时候只能乖乖给aa3传int型参数了。

三、匿名对象

1.匿名对象定义 

没有名字的对象叫做匿名对象,A(3)跟aa1和aa2相比少了个对象名,没有名字,aa1和aa2的生命周期在main函数内,A(3)的生命周期只在当前行:

#include<iostream>
using namespace std;
 
class A
{
public:
	explicit A(int a)
		:_a(a)
	{
		cout << "A(int a):"<< a << endl;
	}
 
	A(const A& aa)
	{
		cout << "A(const A&)" << endl;
	}
 
	~A()
	{
		cout << "~A()" << endl;
	}
 
private:
	int _a;
};
 
int main()
{
	A aa1(1);//生命周期在main函数内
	A aa2(aa1);//生命周期在main函数内
 
	A(3);//构造匿名对象,生命周期只在这一行
 
	return 0;
}

F10调试:当执行完A(3)还没执行return 0时,aa1和aa2的生命周期还没有结束,不会调用析构函数,此时打印的析构函数只能是匿名对象A(3)的析构函数:

所以A(3)这一行执行完就调析构函数了。

2.匿名对象应用场景

假设有一个函数f,且A类的构造函数全缺省:

#include<iostream>
using namespace std;
 
class A
{
public:
	A(int a = 0)//构造函数全缺省
		:_a(a)
	{
		cout << "A(int a):"<< a << endl;
	}
 
	A(const A& aa)
	{
		cout << "A(const A&)" << endl;
	}
 
	~A()
	{
		cout << "~A()" << endl;
	}
 
	void f()//f函数
	{
		cout << "f()" << endl;
	}
 
private:
	int _a;
};
 
int main()
{
	A aa1(1);//生命周期在main函数内
	A aa2(aa1);//生命周期在main函数内
 
	A(3);//构造匿名对象,生命周期只在这一行
 
	return 0;
}

调用f()函数时,需要定义一个A类对象,才能调用A类函数f,这就需要写两行:

int main()
{
	A aa1(1);//生命周期在main函数内
	A aa2(aa1);//生命周期在main函数内
 
	A(3);//构造匿名对象,生命周期只在这一行
 
	A aa4;//需要定义一个A类对象,才能调用f
	aa4.f();
 
	return 0;
}

对象aa4 在main函数结束后才会销毁。如果定义对象只是为了调用函数,那么可以考虑直接定义一个匿名对象:

int main()
{
	A aa1(1);//生命周期在main函数内
	A aa2(aa1);//生命周期在main函数内
 
	A(3);//构造匿名对象,生命周期只在这一行
 
	A aa4;//需要定义一个A类对象,才能调用f
	aa4.f();
 
    A().f();//定义匿名对象来调用函数f()
 
	return 0;
}

这个匿名对象就是为了调用函数f,这个匿名对象后边也没人用它,在当前行调用完f()函数就销毁了。

总结

到此这篇关于C++学习笔记之初始化列表的文章就介绍到这了,更多相关C++初始化列表内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: C++学习笔记之初始化列表

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

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

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • C++学习笔记之初始化列表
    目录一、用初始化列表初始化对象1.初始化列表用法2.初始化列表特性二、explicit关键字1.内置类型的隐式转换2.如何避免单参构造函数初始化发生隐式类型转换三、匿名对象1.匿名对...
    99+
    2023-05-17
    c++ 初始化列表 如何初始化列表 c++ 初始化
  • C++11系列学习之列表初始化
    目录前言:旧标准初始化方式C++11标准初始化方式初始化列表技术细节总结前言: 由于旧标准初始化方式太过繁杂,限制偏多,因此在新标准中统一了初始化方式,为了让初始化具有确定的效果,于...
    99+
    2024-04-02
  • C++学习之初始化列表详解
    目录前言一、类的初始化表二、initializer_list前言 本文主要介绍C++中地初始化列表 目前对初始化列表应该有两个方面的定义,一个是类的构造函数中使用的那个初始化表,另一...
    99+
    2023-03-19
    C++初始化列表 C++ 列表
  • python学习笔记---列表
    列表属于python序列中的一种,属于可变序列,支持插入、删除和替换操作。1、定义列表:2、列表中元素的操作:    获取索引的元素 l[index]:    删除列表中指定索引的元素 del(l[index]):    修改指定元素l[i...
    99+
    2023-01-31
    学习笔记 列表 python
  • 「学习笔记」循环、列表
    while循环与for循环    (一)while循环 结构: while: 循环体    (二)for循环 for 目标 in 表达式: 循环体  实例: favourite = 'fish' for i ...
    99+
    2023-01-30
    学习笔记 列表
  • C++成员初始化列表
    文章转自: 公众号:Coder梁(ID:Coder_LT) 除了可以使用构造函数Classy::Classy(int n, int m): mem1(n), mem2(0), mem...
    99+
    2024-04-02
  • 解析之C++的列表初始化语法
    目录聚合初始化大括号省略(brace elision)std::initializer_list的另一个故事连《Effective Modern C++》都弄错了的规则构造函数的两步...
    99+
    2024-04-02
  • Python学习笔记 --- 序列化Se
    序列化 (serialization) 序列化是将对象状态转换为可保持或传输的格式的过程。与序列化相对的是反序列化, 它将流转换为对象。这两个过程结合起来,可以轻松地存储和传输数据。 序列化和反序列化的目的   1、以某种存储形式使自定义...
    99+
    2023-01-31
    学习笔记 序列化 Python
  • python初学者学习笔记
    python开发:   a.Python基础   b.网络编程   c.web框架 —用于写网站   d.设计阶段+算法   e.项目阶段 开发:   开发语言:高级语言:python/Java/PHP/C#/Go/ruby/c++......
    99+
    2023-01-30
    初学者 学习笔记 python
  • Python学习笔记(1)---列表和元
    什么是列表?列表是一种数据项构成的有限序列,即按照一定的线性顺序,排列而成的数据项的集合。列表的介绍一、更新列表1.元素赋值>>> a=[1,3,4,5]>>> a[1]=10 #改变a中第二个值为10&...
    99+
    2023-01-31
    学习笔记 列表 Python
  • Vue源码学习之数据初始化
    目录初始化数据创建Vue实例构造函数扩展方法初始化状态调用initData方法对数据进行代理初始化数据 环境搭建:菜鸟学Vue源码第一步之rollup环境搭建步 响应式数据的核心就是...
    99+
    2024-04-02
  • C++之谈谈构造函数的初始化列表
    目录一、引入二、初始化的概念区分三、语法格式及使用四、注意事项【⭐】五、总结与提炼一、引入 我们知道,对于下面这个类A的成员变量_a1和_a2属于【声明】,还没有在内存中为其开辟出一...
    99+
    2023-05-15
    C++构造函数 C++构造函数初始化列表 C++初始化列表
  • Python学习笔记8——列表、字典、元
    参考书籍:《Learning_Python_5th_Edition.pdf》,一本英文书呢,我上传到百度网盘吧,请点击这里,密码是:kym3 Lists 列表 The Python list object is the most gene...
    99+
    2023-01-30
    字典 学习笔记 列表
  • Python3学习笔记06——列表创建及
    在Python中使用for循环是,只要作用于一个可迭代对象,for循环就可以正常运行,而我们不太关心该对象究竟是list还是其他数据类型。 那么,如何判断一个对象是可迭代对象呢?方法是通过collections模块的Iterabl...
    99+
    2023-01-31
    学习笔记 列表
  • C++11列表初始化是怎样的
    这篇文章主要介绍“C++11列表初始化是怎样的”,在日常操作中,相信很多人在C++11列表初始化是怎样的问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”C++11列表初始化是怎样的”的疑惑有所帮助!接下来,请跟...
    99+
    2023-06-19
  • TypeScript学习笔记之类型窄化篇
    目录前言类型推论真值窄化相等性窄化in操作符窄化instanceof窄化窄化的本质联合类型的窄化总结前言 TS最好用的地方就是强类型,随之而来的就是类型窄化,摸鱼的时候顺道总结下. ...
    99+
    2024-04-02
  • C++11新特性之列表初始化的具体使用
    目录统一的初始化方法列表初始化的一些使用细节初始化列表1、任何长度的初始化列表2、std::initialzer-list的使用细节列表初始化防止类型收窄在我们实际编程中,我们经常会...
    99+
    2024-04-02
  • C++学习笔记之类成员指针
    C++的类成员指针是一种奇葩的指针。 假设现在我们要表示一个三维的点,现在有两种定义方式: struct point1{ int x, y, z; }; struct point...
    99+
    2024-04-02
  • python3 数组(列表)初始化
    在Python中,可以使用以下方法初始化一个数组(列表):1. 使用方括号 [] 来创建一个空的数组:```my_list = []...
    99+
    2023-10-11
    python
  • 初学者如何开始学习C#
    如果你是初学者,以下是一些开始学习C#的方法:了解基础知识:首先,你需要学习C#的基础知识,包括语法、数据类型、控制流等。可以通过阅读书籍或看视频教程来学习。使用集成开发环境:推荐使用Visual Studio或Visual Studio ...
    99+
    2023-10-22
    C#使用
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作