【C++】再识构造函数:初始化列表新方式

2024-05-14 13:28

本文主要是介绍【C++】再识构造函数:初始化列表新方式,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

欢迎来到CILMY23的博客

🏆本篇主题为: 再识构造函数:初始化列表新方式

🏆个人主页:CILMY23-CSDN博客

🏆系列专栏:Python | C++ | C语言 | 数据结构与算法 | 贪心算法 | Linux

🏆感谢观看,支持的可以给个一键三连,点赞关注+收藏。


写在前头:

在之前我们花了大篇幅了解了构造函数,包括但不限于构造函数的详解,构造函数和析构函数的顺序,以及构造函数中特殊的拷贝构造函数。那这次的构造函数又会带给我什么惊喜呢?


目录

再见构造函数

1️⃣ 构造函数赋值

2️⃣ 初始化列表的概念

3️⃣ 为什么会有初始化列表? 

4️⃣ 初始化列表的特点 

5️⃣ 构造函数新的写法

6️⃣ explicit关键字 

7️⃣ 多参数类型的隐式转换 


再见构造函数

1.1  构造函数赋值

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

class Student
{
public://无参构造函数Student(){strcpy(_name, "xxxxxx");_age = 0;strcpy(_ID, "xxxxxxx");}void Print(){cout << "学生姓名:" << _name << endl;cout << "学生年龄:" << _age << endl;cout << "学生学号:" << _ID << endl;}
private:char _name[20];int _age;char _ID[20];
};int main()
{Student stu1;//调用了无参的构造函数stu1.Print();return 0;
}

虽然上述构造函数调用之后,对象中已经有了一个初始值,但是不能将其称为对对象中成员变量 的初始化,构造函数体中的语句只能将其称为赋初值,而不能称作初始化。因为初始化只能初始 化一次,而构造函数体内可以多次赋值

1.2  初始化列表的概念

初始化列表是C++中的一个功能,允许在构造函数中直接初始化成员变量,而非在构造函数体内赋值。初始化列表直接跟在构造函数的参数列表后面,以冒号开始后面紧跟一个或多个用逗号分隔的初始化表达式。这些表达式直接对成员变量或自定义类型进行初始化。

例如:这里有个学生类,我们要用初始化列表。(在这里对字符的拷贝我们仍然采用C的方法来实现,利用strncpy或者strcpy,在之后我们会用string来实现这些)

class Student {
public:// 使用初始化列表来初始化_ageStudent(const char* name, int age, const char* ID) : _age(age) {// 为_name和_ID赋值strncpy(_name, name, sizeof(_name) - 1);_name[sizeof(_name) - 1] = '\0'; strncpy(_ID, ID, sizeof(_ID) - 1);_ID[sizeof(_ID) - 1] = '\0'; }private:char _name[20];int _age;char _ID[20];
};int main()
{Student student1("Alice", 20, "1234567890");student1.Print();return 0;
}

1.3 为什么会有初始化列表? 

 例如:就像下面这段代码一样,有些类的成员是必须要在初始化的时候定义的,这里的_n = -1;是会报错的。就比如const成员是这样的。那哪个地方是初始化的呢?那它就需要去构造函数去找空间给它初始化,但也会有安全性的问题,所以有了初始化列表这个概念。初始化列表是每个成员变量定义初始化的位置。

顺序是先走上面的初始化列表,然后再走函数体内的赋值修改,能用初始化列表就用初始化列表。

class Student 
{
public://Student(const char* name, int age, const char* ID) //    : _age(age) //{//    //赋值修改//    strncpy(_name, name, sizeof(_name) - 1);//    _name[sizeof(_name) - 1] = '\0'; //}//构造函数Student(const char* name, int age){strncpy(_name, name, sizeof(_name) - 1);_name[sizeof(_name) - 1] = '\0';_age = age;_n = -1; // 无法修改}private://声明char _name[20];int _age = 1; //缺省值const int _n;
};int main() 
{//对象实例化Student student1("Alice", 20);return 0;
}

1.4 初始化列表的特点 

  1. 每个成员变量在初始化列表中只能出现一次(初始化只能初始化一次)
  2. 类中包含以下成员,必须放在初始化列表位置进行初始化,这三个在函数体内不能初始化

    ✔️引用成员变量
    ✔️const成员变量
    ✔️自定义类型成员(且该类没有默认构造函数时)

  3. 尽量使用初始化列表初始化,因为不管你是否使用初始化列表,对于自定义类型成员变量, 一定会先使用初始化列表初始化。
  4. 成员变量在类中声明次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后 次序无关 

这些特点如下所示: 

class A
{
public:A(int x)//初始化列表:_a(1){//赋值修改_a = x;}private:int _a;
};class Student
{
public://初始化列表是每个成员变量定义初始化的位置//能用初始化列表就用初始化列表Student(const char* name, int age, int &x): _age(age), _n(1), _aa(1)//显式调用构造函数, ref(x){strncpy(_name, name, sizeof(_name) - 1);_name[sizeof(_name) - 1] = '\0'; }private://声明char _name[20];int _age = 1; //缺省值//必须走初始化列表//1. const 成员 //2. 引用成员变量//3. 自定义类型成员const int _n;int& ref;A _aa;
};int main()
{//对象实例化int x = 0;Student student1("Alice", 20,x);return 0;
}

 1.5 构造函数新的写法

构造函数结合初始化列表后,我们可以写成main函数中的另外两种cc的形式。 

class C
{
public:C(int x = 0):_x(x){}
private:int _x;
};class B
{
public:B():_p(2),_p1((int*)malloc(sizeof(4)*10)){if (_p1 == nullptr){perror("malloc fail");}}
private://缺省值是给初始化列表的int _p = 1;int* _p1 = (int*)malloc(sizeof(4));
};int main()
{B bb;//构造函数的写法C cc1(1);//2构造一个临时对象,再拷贝构造C cc2 = 2;return 0;
}

 其实通过这个例子我们可以发现,单参数构造函数支持隐式类型的转换,2构造一个临时对象,然后再拷贝构造

如果遇到同一个表达式连续步骤的构造,一般会被编译器优化。 

隐式类型转换是有好处的,例如我们在栈传参的时候,可以直接将int类型的4转换成自定义类型。

1.6 explicit关键字 

如果你不想让上述这种隐式类型转换发生,那你就可以加一个关键字 - explicit。 

  

1.7 多参数类型的隐式转换 

在目前的C++11中,支持多参数类型的隐式转换,C++98还不支持,用花括号括起来。


总结:

  • 初始化列表直接跟在构造函数的参数列表后面,以冒号开始后面紧跟一个或多个用逗号分隔的初始化表达式
  • 顺序是先走上面的初始化列表,然后再走函数体内的赋值修改
  • 当出现函数赋值修改缺省值,和初始化列表的时候,推荐是能用初始化列表就用初始化列表。
  • 有些成员必须走初始化列表,1. const 成员 2. 引用成员变量 3. 自定义类型成员(没有默认构造的自定义类型成员)
  • 成员变量在类中声明次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后 次序无关
  • 缺省值是给初始化列表的
  • 单参数构造函数支持隐式类型的转换
  • 临时变量具有常性,需要加const修饰。
  • 构造函数不仅可以构造与初始化对象,对于单个参数或者除第一个参数无默认值其余均有默认值的构造函数,还具有类型转换的作用。
  • explicit 可以限制隐式类型的转换
  • C++11 支持多参数类型的隐式类型转换

感谢各位同伴的支持,本期C++就讲解到这啦,如果你觉得写的不错的话,可以给个一键三连,点赞,关注+收藏,若有不足,欢迎各位在评论区讨论。  

这篇关于【C++】再识构造函数:初始化列表新方式的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/988855

相关文章

python logging模块详解及其日志定时清理方式

《pythonlogging模块详解及其日志定时清理方式》:本文主要介绍pythonlogging模块详解及其日志定时清理方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地... 目录python logging模块及日志定时清理1.创建logger对象2.logging.basicCo

C++中初始化二维数组的几种常见方法

《C++中初始化二维数组的几种常见方法》本文详细介绍了在C++中初始化二维数组的不同方式,包括静态初始化、循环、全部为零、部分初始化、std::array和std::vector,以及std::vec... 目录1. 静态初始化2. 使用循环初始化3. 全部初始化为零4. 部分初始化5. 使用 std::a

C#TextBox设置提示文本方式(SetHintText)

《C#TextBox设置提示文本方式(SetHintText)》:本文主要介绍C#TextBox设置提示文本方式(SetHintText),具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑... 目录C#TextBox设置提示文本效果展示核心代码总结C#TextBox设置提示文本效果展示核心代

SpringValidation数据校验之约束注解与分组校验方式

《SpringValidation数据校验之约束注解与分组校验方式》本文将深入探讨SpringValidation的核心功能,帮助开发者掌握约束注解的使用技巧和分组校验的高级应用,从而构建更加健壮和可... 目录引言一、Spring Validation基础架构1.1 jsR-380标准与Spring整合1

Android实现打开本地pdf文件的两种方式

《Android实现打开本地pdf文件的两种方式》在现代应用中,PDF格式因其跨平台、稳定性好、展示内容一致等特点,在Android平台上,如何高效地打开本地PDF文件,不仅关系到用户体验,也直接影响... 目录一、项目概述二、相关知识2.1 PDF文件基本概述2.2 android 文件访问与存储权限2.

C++ vector的常见用法超详细讲解

《C++vector的常见用法超详细讲解》:本文主要介绍C++vector的常见用法,包括C++中vector容器的定义、初始化方法、访问元素、常用函数及其时间复杂度,通过代码介绍的非常详细,... 目录1、vector的定义2、vector常用初始化方法1、使编程用花括号直接赋值2、使用圆括号赋值3、ve

如何高效移除C++关联容器中的元素

《如何高效移除C++关联容器中的元素》关联容器和顺序容器有着很大不同,关联容器中的元素是按照关键字来保存和访问的,而顺序容器中的元素是按它们在容器中的位置来顺序保存和访问的,本文介绍了如何高效移除C+... 目录一、简介二、移除给定位置的元素三、移除与特定键值等价的元素四、移除满足特android定条件的元

Spring中配置ContextLoaderListener方式

《Spring中配置ContextLoaderListener方式》:本文主要介绍Spring中配置ContextLoaderListener方式,具有很好的参考价值,希望对大家有所帮助,如有错误... 目录Spring中配置ContextLoaderLishttp://www.chinasem.cntene

Python获取C++中返回的char*字段的两种思路

《Python获取C++中返回的char*字段的两种思路》有时候需要获取C++函数中返回来的不定长的char*字符串,本文小编为大家找到了两种解决问题的思路,感兴趣的小伙伴可以跟随小编一起学习一下... 有时候需要获取C++函数中返回来的不定长的char*字符串,目前我找到两种解决问题的思路,具体实现如下:

C++ Sort函数使用场景分析

《C++Sort函数使用场景分析》sort函数是algorithm库下的一个函数,sort函数是不稳定的,即大小相同的元素在排序后相对顺序可能发生改变,如果某些场景需要保持相同元素间的相对顺序,可使... 目录C++ Sort函数详解一、sort函数调用的两种方式二、sort函数使用场景三、sort函数排序