【C++】-- 初始化列表(二)

简介: 【C++】-- 初始化列表


二、explicit关键字

1.内置类型的隐式转换

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

根据监视可以看出:

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

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

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

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

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

1. #include<iostream>
2. using namespace std;
3. 
4. class A
5. {
6. public :
7.  A(int a)
8.    :_a(a)
9.  {}
10. 
11. private:
12.   int _a;
13. };
14. 
15. int main()
16. {
17.   A aa1(1);//构造aa1对象
18.   A aa2(aa1);//拷贝构造,程序没写拷贝构造,编译器会自动生成拷贝构造函数,对内置类型完成浅拷贝
19. 
20.   A aa3 = 3;//单参数的构造函数,会发生隐式类型转换
21. 
22.   return 0;
23. }

那么

A aa3 = 3;

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

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

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

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

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

1. #include<iostream>
2. using namespace std;
3. 
4. class A
5. {
6. public:
7.  A(int a)
8.    :_a(a)
9.  {}
10. 
11. private:
12.   int _a;
13. };
14. 
15. int main()
16. {
17.   A aa1(1);//构造aa1对象
18.   A aa2(aa1);//拷贝构造,程序没写拷贝构造,编译器会自动生成拷贝构造函数,对内置类型完成浅拷贝
19. 
20.   A aa3 = 'x';//先拿A构造一个临时对象temp,字符x作为参数传给这个临时对象temp,会发生隐式类型转换,再拿aa3(temp)去拷贝构造
21. 
22.   return 0;
23. }

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

1. class A
2. {
3. public:
4.  explicit A(int a)
5.    :_a(a)
6.  {}
7. 
8. private:
9.  int _a;
10. };

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

 

三、匿名对象

1.匿名对象定义

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

1. #include<iostream>
2. using namespace std;
3. 
4. class A
5. {
6. public:
7.  explicit A(int a)
8.    :_a(a)
9.  {
10.     cout << "A(int a):"<< a << endl;
11.   }
12. 
13.   A(const A& aa)
14.   {
15.     cout << "A(const A&)" << endl;
16.   }
17. 
18.   ~A()
19.   {
20.     cout << "~A()" << endl;
21.   }
22. 
23. private:
24.   int _a;
25. };
26. 
27. int main()
28. {
29.   A aa1(1);//生命周期在main函数内
30.   A aa2(aa1);//生命周期在main函数内
31. 
32.   A(3);//构造匿名对象,生命周期只在这一行
33. 
34.   return 0;
35. }

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

 

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

2.匿名对象应用场景

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

1. #include<iostream>
2. using namespace std;
3. 
4. class A
5. {
6. public:
7.  A(int a = 0)//构造函数全缺省
8.    :_a(a)
9.  {
10.     cout << "A(int a):"<< a << endl;
11.   }
12. 
13.   A(const A& aa)
14.   {
15.     cout << "A(const A&)" << endl;
16.   }
17. 
18.   ~A()
19.   {
20.     cout << "~A()" << endl;
21.   }
22. 
23.   void f()//f函数
24.   {
25.     cout << "f()" << endl;
26.   }
27. 
28. private:
29.   int _a;
30. };
31. 
32. int main()
33. {
34.   A aa1(1);//生命周期在main函数内
35.   A aa2(aa1);//生命周期在main函数内
36. 
37.   A(3);//构造匿名对象,生命周期只在这一行
38. 
39.   return 0;
40. }

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

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

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

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

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

相关文章
|
16天前
|
C++
C++ 类的初始化列表与构造函数初始化的技术性探讨
C++ 类的初始化列表与构造函数初始化的技术性探讨
11 0
|
26天前
|
Java 编译器 C语言
从C语言到C++⑦(第二章_类和对象_下篇)初始化列表+explicit+static成员+友元+内部类+匿名对象(下)
从C语言到C++⑦(第二章_类和对象_下篇)初始化列表+explicit+static成员+友元+内部类+匿名对象
11 0
|
26天前
|
C语言 C++
从C语言到C++⑦(第二章_类和对象_下篇)初始化列表+explicit+static成员+友元+内部类+匿名对象(中)
从C语言到C++⑦(第二章_类和对象_下篇)初始化列表+explicit+static成员+友元+内部类+匿名对象
28 0
|
26天前
|
编译器 C语言 C++
从C语言到C++⑦(第二章_类和对象_下篇)初始化列表+explicit+static成员+友元+内部类+匿名对象(上)
从C语言到C++⑦(第二章_类和对象_下篇)初始化列表+explicit+static成员+友元+内部类+匿名对象
13 1
|
1月前
|
存储 程序员 编译器
C++11:声明 & 初始化
C++11:声明 & 初始化
13 0
|
1月前
|
安全 程序员 编译器
【C++类和对象】初始化列表与隐式类型转换
【C++类和对象】初始化列表与隐式类型转换
|
1月前
|
编译器 C++ 容器
【C++11(一)】右值引用以及列表初始化
【C++11(一)】右值引用以及列表初始化
|
1月前
|
编译器 C++
【C++基础(八)】类和对象(下)--初始化列表,友元,匿名对象
【C++基础(八)】类和对象(下)--初始化列表,友元,匿名对象
|
1月前
|
编译器 C++
C++编程之美:探索初始化之源、静态之恒、友情之桥与匿名之韵
C++编程之美:探索初始化之源、静态之恒、友情之桥与匿名之韵
29 0
|
1月前
|
编译器 C++
C++ 初始化列表
C++ 初始化列表