个人主页:Jason_from_China-CSDN博客
所属栏目:C++系统性学习_Jason_from_China的博客-CSDN博客
所属栏目:C++知识点的补充_Jason_from_China的博客-CSDN博客
前言
来到类和对象最后一个章节,这里的难度已经极大程度的降低了
再探构造函数
概念概述
- 之前我们实现构造函数时,初始化成员变量主要使用函数体内赋值,构造函数初始化还有一种方式,就是初始化列表,初始化列表的使用方式是以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个 “成员变量” 后面跟一个放在括号中的初始值或表达式。
- 每个成员变量在初始化列表中只能出现一次,语法理解上初始化列表可以认为是每个成员变量定义初始化的地方。
- 引用成员变量,const 成员变量,没有默认构造的类类型变量,必须放在初始化列表位置进行初始化,否则会编译报错。
- C++11 支持在成员变量声明的位置给缺省值,这个缺省值主要是给没有显示在初始化列表初始化的成员使用的。
- 尽量使用初始化列表初始化,因为那些你不在初始化列表初始化的成员也会走初始化列表,如果这个成员在声明位置给了缺省值,初始化列表会用这个缺省值初始化。如果你没有给缺省值,对于没有显示在初始化列表初始化的内置类型成员是否初始化取决于编译器,C++ 并没有规定。对于没有显示在初始化列表初始化的自定义类型成员会调用这个成员类型的默认构造函数,如果没有默认构造会编译错误。
- 初始化列表中按照成员变量在类中声明顺序进行初始化,跟成员在初始化列表出现的的先后顺序无关。建议声明顺序和初始化列表顺序保持一致。
- 初始化列表总结:无论是否显式写初始化列表,每个构造函数都有初始化列表;无论是否在初始化列表显示初始化,每个成员变量都要走初始化列表初始化。
成员变量初始化列表逻辑
再探构造函数存在的意义
//比如我们写一个栈,这里就不需要写构造函数,因为编译器默认生成队列的构造函数,调用了stack的默认构造,完成了两个成员函数的初始化
//但是如果栈的默认构造是没有的情况下,此时怎么处理?所以就需要延伸到再探构造函数
初始化列表的格式
之前我们进行初始化是函数体的初始化,现在称之为初始化列表的初始化
初始化列表的使用方式是以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个 “成员变量” 后面跟一个放在括号中的初始值或表达式。
使用的格式记住就可以
哪些成员必须采用初始化列表(没有默认构造函数的,需要使用初始化列表)
//构造函数 Date(int year = 1901, int month = 1, int day = 1); 构造函数 //Date::Date(int year, int month, int day) //{ // _year = year; // _month = month; // _day = day; //} //构造函数,初始化列表的实现 Date::Date(int year, int month, int day) :_year(111) , _month(111) , _day(111) {} int main() { // 明确传递参数,使用带参数的构造函数和初始化列表进行初始化 Date d1(2024, 9, 27); // 不传递参数,使用默认参数值和初始化列表进行初始化 Date d2; return 0; } //不是单纯地调用默认构造函数,而是根据是否传递参数来决定使用带参数的构造函数(带默认参数),并且始终会使用初始化列表进行初始化。 //如果传递参数调用构造函数,如Date d1(2024, 9, 27);,会优先使用初始化列表,按照给定的参数值初始化成员变量_year、_month和_day。 //如果不传递参数,如Date d2;,则会使用构造函数中的缺省参数值,并同样通过初始化列表来初始化成员变量。
哪些成员必须采用初始化列表(const修饰的成员变成必须使用初始化列表)
一,const 成员函数的特性
const 成员函数承诺不修改对象的数据成员,即它保证了在函数执行期间,对象的状态不会被改变。这意味着 const 成员函数应该只能读取对象的状态,而不能进行任何可能改变对象状态的操作。
二,函数体初始化与初始化列表的区别
- 函数体初始化:在函数体内部进行成员变量的初始化是通过赋值操作来完成的。这意味着先调用默认构造函数(如果有)创建成员变量,然后再对其进行赋值。这种方式可能会导致对象被临时创建和赋值两次,增加了不必要的开销,并且在一些情况下可能违反 const 成员函数的不修改对象状态的承诺。
- 初始化列表:初始化列表在对象创建时直接初始化成员变量,避免了先调用默认构造函数再赋值的过程。对于 const 成员变量和引用成员变量,必须在初始化列表中进行初始化,因为它们不能在创建后被重新赋值。对于普通成员变量,使用初始化列表也可以提高效率,并且更符合 C++ 的初始化语义。
代码实现:这里主要看成员变量_y
#include<iostream> using namespace std; class Date { public: Date(int& z); Date(int x, int& z); Date(int x, int y, int& z); void _print() { cout << _x << "/" << _y << "/" << _z << endl << endl; } private: int _x; const int _y; int& _z; }; //再探构造函数1 Date::Date(int& z) :_x(111) , _y(111) , _z(z) {} //再探构造函数2 Date::Date(int x, int& z) :_x(222) , _y(222) , _z(z) {} //再探构造函数3 Date::Date(int x, int y, int& z) : _y(333) ,_z(z) { _x = x; } int main() { //再探构造函数1 int i1 = 0; Date d1(i1); d1._print(); cout << i1 << endl << endl; //再探构造函数2 int i2 = 0; Date d2(1, i1); d2._print(); cout << i2 << endl << endl; //再探构造函数3 int i3= 0; Date d3(1, 1, i3); d3._print(); cout << i3 << endl; return 0; }
哪些成员必须采用初始化列表(引用成员变量的初始化)
一、引用的特性
引用在 C++ 中必须在定义时被初始化,并且一旦初始化后就不能再绑定到其他对象。引用本质上是一个对象的别名,它总是指向一个特定的对象,不能被重新赋值为指向另一个不同的对象。
二、构造函数的执行过程
- 当一个对象被创建时,构造函数首先执行初始化列表来初始化成员变量。如果不在初始化列表中初始化引用成员变量,那么在构造函数体中就没有机会再对其进行初始化了,因为此时引用必须已经被绑定到一个对象。
- 如果试图在构造函数体中对引用成员变量进行赋值,这会被编译器视为重新绑定引用,而这是不允许的,会导致编译错误。
三、左值和右值的概念
- 左值(lvalue):代表一个有明确内存地址、可以取地址且有可持久性的值。通常可以出现在赋值语句的左侧,例如变量名、解引用的指针等。例如,
int a = 10;
中的a
就是一个左值,因为它有明确的内存地址,可以通过&a
取地址,并且在程序的生命周期内持续存在。引用成员变量的初始化就是不修改的左值。- 右值(rvalue):通常是临时的值,没有明确的内存地址可获取,或者是即将被销毁的值。例如,字面常量、临时对象、函数返回的临时值等。例如,
int b = 15 + 20;
这里15 + 20
的结果就是一个右值,它是一个临时值,没有独立的内存地址可获取,在表达式结束后可能就会被销毁。代码实现
#include<iostream> using namespace std; class Date { public: Date(int& z); Date(int x, int& z); Date(int x, int y, int& z); void _print() { cout << _x << "/" << _y << "/" << _z << endl << endl; } private: int _x; const int _y; int& _z; }; //再探构造函数1 Date::Date(int& z) :_x(111) , _y(111) , _z(z) {} //再探构造函数2 Date::Date(int x, int& z) :_x(222) , _y(222) , _z(z) {} //再探构造函数3 Date::Date(int x, int y, int& z) : _y(333) ,_z(z) { _x = x; } int main() { //再探构造函数1 int i1 = 0; Date d1(i1); d1._print(); cout << i1 << endl << endl; //再探构造函数2 int i2 = 0; Date d2(1, i1); d2._print(); cout << i2 << endl << endl; //再探构造函数3 int i3= 0; Date d3(1, 1, i3); d3._print(); cout << i3 << endl; return 0; }
初始化列表的注意事项(初始化的顺序)
初始化列表的注意事项(尽量都使用初始化列表进行初始化)
这里建议尽量使用初始化列表进行初始化,因为在C++里面,他是有一套构造流程的,而且对于C++来讲,为什么比其他语言快,不仅仅的这个语言本身的特点,还是在书写的时候,我们会更加注意程序的设计
初始化列表的注意事项(建议声明和顺序保持一致)
这里也是一个需要注意的点
因为编译器进行初始化的时候,是按照头文件进行初始化的,不是你实现的顺序来进行初始化的