欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 健康 > 养生 > CD24.【C++ Dev】类和对象(15)初始化列表(下)和对象隐式类型转换

CD24.【C++ Dev】类和对象(15)初始化列表(下)和对象隐式类型转换

2025/4/18 23:09:19 来源:https://blog.csdn.net/2401_85828611/article/details/146989201  浏览:    关键词:CD24.【C++ Dev】类和对象(15)初始化列表(下)和对象隐式类型转换

目录

1.练习题1:初始化列表中的初始化顺序

2.练习题2:成员变量声明时的缺省参数和初始化列表

问题1.下面代码的运行结果是什么?

分析

问题2:修改上方代码的Myclass带参的构造函数为下方代码,执行myobj2的构造函数时,在执行 _val = 10;前_val的值是多少?

分析

3.类型转换

观察下列代码,问myobj1和myobj2初始化有什么区别?

分析

临时对象具有常性

★结论:对象(初始值)和对象 = 初始值的区别 

4.用好隐式类型转换可以简化代码

5.explicit关键字


承接CD23.【C++ Dev】类和对象(14) 取地址重载函数和初始化列表(上)文章

1.练习题1:初始化列表中的初始化顺序

下面代码的运行结果是什么?

#include <iostream>
using namespace std;
class Myclass
{
public:Myclass(int data):_val1(data), _val2(_val1){}void Print() {cout << "_val1:"<<_val1<<endl<< "_val2:" << _val2 << endl;}
private:int _val2;int _val1;
};int main() 
{Myclass myobj(123);myobj.Print();
}

分析:

先看答案再推原因:

_val2是随机值,说明myobj中先初始化_val2,再初始化_val1,1._val2(_val1)可以看出,_val2初始化的是_val1的值,但_val1此时还没有初始化,操作系统为_val1内存空间赋的是随机值,导致_val2是随机值

2._val1(data)可以看出:_val1初始化的值是data,而data值为123,因此正常打印"_val1:123"

从反汇编也可以看出:

结论:成员变量在类中声明次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后次序无关

建议:声明的顺序和定义的顺序保持一致

2.练习题2:成员变量声明时的缺省参数和初始化列表

问题1.下面代码的运行结果是什么?

#include <iostream>
using namespace std;
class Myclass
{
public:Myclass(){}Myclass(int data):_val(10){}int GetVal(){return _val;}
private:int _val = 0;
};int main()
{Myclass myobj1;Myclass myobj2(1);cout << myobj1.GetVal() << endl;cout << myobj2.GetVal() << endl;return 0;
}

分析

显然构造函数Myclass被重载了,Myclass()是默认构造函数(没有传任何参数),Myclass(int data)

默认构造函数(传了参数data)

则Myclass myobj1;初始化时会调用Myclass(),初始化成员变量val时会使用int _val = 0的缺省参数0,

Myclass myobj2(1)传了一个参数,因此初始化时会调用Myclass(int data),导致_va的值l被初始化为10

运行结果:

问题2:修改上方代码的Myclass带参的构造函数为下方代码,执行myobj2的构造函数时,在执行 _val = 10;前_val的值是多少?

Myclass(int data)
{_val = 10;
}

分析

由CD23对缺省参数和初始化列表的关系的说明可知:所有成员变量在初始化时都要走初始化列表,_val给了缺省参数0,则在执行_val = 10前,_val的值应该为缺省参数0

可以下断点调试看看,如下:

再看看反汇编代码,更明确:

3.类型转换

观察下列代码,问myobj1和myobj2初始化有什么区别?

#include <iostream>
using namespace std;
class Myclass
{
public:Myclass(int data):_val(data){}
private:int _val;
};int main()
{Myclass myobj1(1);Myclass myobj2 = 2;return 0;
}

分析

Myclass myobj1(1)是正常初始化,向构造函数Myclass正常传参数,用参数初始化_val的值:

 Myclass myobj2 = 2;在写法上与myobj1不同,2是一个整型,却要赋值给自定义类型myobj2,编译器会做如下处理:隐式类型转换,将整型转换为自定义类型,即用2去构造一个临时对象,再将这个临时对象拷贝构造给myobj2,其实是构造函数+拷贝构造函数(这个是未优化的情况)

画示意图为:

实际上较新的编译器不允许这样做,编译器会进行优化,将连续的构造转换为用2直接构造,不使用拷贝构造函数,可以手动写一个拷贝构造函数看看是否会调用:

#include <iostream>
using namespace std;
class Myclass
{
public:Myclass(int data)//构造函数:_val(data){cout << "Myclass(int data)" << endl;}Myclass(const Myclass& myobj)//拷贝构造函数:_val(myobj._val){cout << "Myclass(const Myclass& myobj)" << endl;}
private:int _val;
};int main()
{Myclass myobj2 = 2;return 0;
}

VS2022上的运行结果: 只调用构造函数,属于优化后的

没有优化的运行结果:使用Linux g++关闭优化的指令:

g++ -fno-elide-constructors test.cpp

 (未优化:构造+拷贝构造)

临时对象具有常性

如果改成:

Myclass& myobj2 = 2;

 编译出错:

分析:用2构造临时对象,再对临时对象引用,这里需要注意:临时对象具有常性,引用需要用const修饰,改成下面这样就行了:

const Myclass& myobj2 = 2;

★结论:对象(初始值)和对象 = 初始值的区别 

之前在CC12.【C++ Cont】string类字符串的创建、输入、访问和size函数文章中提到过string类对象的两种初始化方式,如下:

string str1="hello world";
string str2("hello world");

在那篇文章中认为:两种初始化的效果一样,其实初始化的方式是有区别的

1. string str2("hello world");只调用构造函数

2. string str1="hello world";调用了构造函数拷贝构造函数,可能会被编译器优化成直接构造

4.用好隐式类型转换可以简化代码

例如写一个List类,实现向链表中尾插字符串:

#include <iostream>
#include <string>
using namespace std;
class List
{
public:void push_back(const string& s){//省略具体实现代码}
};int main()
{List ls;//发生隐式类型转换//因为push_back接收的是const string& s,是string对象的引用//用const修饰原因:临时对象具有常性ls.push_back("teststring1");string s("teststring2");ls.push_back(s);return 0;
}

显然ls.push_back("teststring1")利用了隐式类型转换,只需写一行,在写法上比string s("teststring2");和ls.push_back(s);简洁

5.explicit关键字

如果不想发生隐式类型转换,可以使用explicit关键字,加在构造函数的前面

explicit表明该构造函数是显式的,而非隐式的.当使用explicit修饰构造函数时,它将禁止类对象之间的隐式转换,以及禁止隐式调用拷贝构造函数

代码如下:

#include <iostream>
using namespace std;
class Myclass
{
public://explicit修饰explicit Myclass(int data):_val(data){cout << "Myclass(int data)" << endl;}Myclass(const Myclass& myobj):_val(myobj._val){cout << "Myclass(const Myclass& myobj)" << endl;}
private:int _val;
};int main()
{Myclass myobj2 = 2;const Myclass& myobj3 = 2;return 0;
}

这样编译就会报错,myobj2和myobj3都会报错,都无法创建临时对象

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词