[关闭]
@CrazyHenry 2018-02-22T13:40:04.000000Z 字数 4482 阅读 1313

12.x 关于{}和初始化器

ccccC++Primer


  1. class Fuck
  2. {
  3. public:
  4. int a{};//使用1个初始化器,值初始化为0
  5. int b{};
  6. int c = {6};//等价于c{6},但是不推荐
  7. Fuck():a{5}{}//使用1个初始化器,a被值初始化为5,实际这里用()更好,a(5)
  8. };
  9. int main()
  10. {
  11. Fuck bitch;
  12. cout<<bitch.a<<" "<<bitch.b<<" "<<bitch.c<<endl;
  13. unique_ptr<int[]> up(new int[10]{0,5,int(),int(8)});//0,5,0,8
  14. //用int{8}也可,建议用int(8)
  15. unique_ptr<string[]> up2(new string[10]{"5",string(8,'8'),"Hello"});//10个初始化器,其余被值初始化为空串
  16. cout<<up2[1]<<endl;
  17. vector<string> vs(9,"Hi");
  18. return 0;
  19. }

一个初始化器用于创建一个符合=左侧对象类型的对象。初始化器可以完成隐式类型转换("Hello"转换为string("Hello")),值初始化构造函数调用(当没有显示构造,就调用值初始化构造函数),显式构造函数调用如 string(9,'c')
使用{}就是使用了初始化器。

需要调用单个对象的构造函数的时候,用(),比如:

  1. unique_ptr<string[]> up2(new string[10]{"5",string(8,'8'),"Hello"});//10个初始化器,其余被值初始化为空串
  2. //string(8,'8')调用构造函数,不能用string{8,'8'}替代
  3. //string s = {'H','i'};是使用类似vector的初始值列表所以,也可以用{}构造一个string对象
  4. //new string[10]{"5",string(8,'8'),"Hello"});改为new string[10]{"5",{'8','8','8'...},"Hello"});也等价
  5. //但是new string[10]{"5",{8,'8'},"Hello"});的意义就不一样了,编译器会把int型数据8理解为一个字符,和字符'8'一起,用于初始化一个string对象,8对于什么字符呢?
  1. shared_ptr<vector<int>> sp(new vector<int>());
  2. //初始化为空的vector<int>
  1. unique_ptr<string[]> up2(new string[10]{"5",string(),"Hello"});//10个初始化器,其余被值初始化为空串
  1. unique_ptr<string[]> up2(new string[10]{"5",string("Hello"),"Hello"});
  2. //虽然string{"Hello"}也可以,但是建议使用()
  1. class Fuck
  2. {
  3. public:
  4. int a{};//使用1个初始化器,值初始化为0
  5. int b{};
  6. int c = {6};//等价于c{6},但是不推荐
  7. Fuck():a(5){} //给单个对象初始化的时候,建议还是使用()而不是{}。除了是类内初始值的时候!
  8. };

类内初始值以及列表初始化时候,用{}

  1. //类内初始值
  2. class Fuck
  3. {
  4. public:
  5. int a{};//使用1个初始化器,值初始化为0
  6. int b{};
  7. int c = {6};//不好,用int c{6}更大气
  8. string s{8,'8'};//error,只能用string s{"88888888"}
  9. };
  1. vector<int> vi{1,2,3,4,5};

有时候迫不得已,也只能用{}:

  1. int a{};//值初始化为0,int a()会认为是函数声明
  2. class A;
  3. A a{};//等价于A a,但是用A a()就会认为是函数声明
  4. A a;
  5. vector<A> vA{A(),A(args)};//用A()显得比A{}好

赋值是一定有=号的:(){}只是用于初始化:

  1. int c = {6};
  2. //为什么说这句代码显得不大气,就是将赋值和初始化没有搞清楚
  3. //{}构造了一个初始化器,将字面值6转换为int对象6,然后赋值给c
  4. //显得很多余
  5. int c(6);//int型对象c初始化为6
  1. int ds{};//只能这样初始化
  2. ds{8};//error
  3. ds(8);//error
  4. ds = {8.8};//用8.8构造一个int对象8,赋值给ds
  1. vector<int> vi{};//vi为空vector
  2. //等价于vector<int> vi({});
  3. //等价于vector<int> vi={};
  4. vector<int> vi{1,2,3};//vi初始化为3个元素的vector
  5. string s{'H','i'};//s初始化为2个元素的vector<char>
  6. string s("Hello");//调用构造函数
  7. string s{};//一个初始化器,调用默认构造函数构造一个空串,拷贝给s;与string s;等价
  8. string as[10]{};//10个初始化器,调用10次默认构造函数,构造10个空串,拷贝给as
  9. //可见,数组的空{}和vector<int>的空{}意义是不一样的,虽然都是“数组”,但是vector只构造一个初始化器用于vector对象自身。而数组构建10个初始化器,用于初始化每个元素。
  10. //vector<int>的非空{}和数组的{}意义就是一样的了!都用于初始化元素。
  11. //数组的{}只用于初始化元素
  12. vector<int> vi({1,2,3,5,4});
  13. //等价于vector<int> vi{1,2,3,5,4};
  14. //等价于vector<int> vi={1,2,3,5,4};
  1. unique_ptr<string[]> up2(new string[10]{"5",string{'8','8'},"Hello"});//10个初始化器,其余被值初始化为空串 。等价于string({'8','8'})
  2. cout<<up2[1]<<endl;//输出88
  1. vector<int> vi{};
  2. //关于这句代码,总觉得有些别扭,主要在于{}里有值和无值时的动作似乎不一样。
  3. //在我看来,vector<int>的构造函数一定是重载了initialization_list的,所以,上面的代码应该进入了这个构造函数。而不像普通的类,默认{}应该用于初始化器,初始化=左侧的对象,比如:
  4. class Fuck
  5. {
  6. public:
  7. int a{};//使用1个初始化器,值初始化为0
  8. int b{};
  9. int c = {6};//不好
  10. Fuck():a{5},s(9,'c'){}//使用1个初始化器,a被值初始化为5
  11. string s;
  12. };
  13. int main()
  14. {
  15. Fuck am{};
  16. return 0;
  17. }
  18. //Fuck没有定义initialization_list的构造函数,但是Fuck am{}默认调用了默认构造函数!
  19. //所以我认为,vector<int> vi{}和new vector<int>()应该是调用了不同的构造函数。但结果都是空vector。vector<int> vi{}调用了重载initialization_list的构造函数;vector<int>()调用默认构造函数。
  20. //结论:{}对于容器,作用是构建每个元素的初始化器,而不是vector<T>对象本身。
  21. //所以,一般直接使用
  22. vector<int> vi;//调用默认构造函数,创建空vector

关于{}

{}有两个功能,其一是初始值列表,其二就是匹配构造函数

  1. #include <iostream>
  2. #include <string>
  3. #include <vector>
  4. #include <iterator>
  5. #include <algorithm>
  6. #include <typeinfo>
  7. #include <numeric>
  8. #include <memory>
  9. #include <initializer_list>
  10. using namespace std;
  11. //win+<-/-> 切换窗口位置
  12. //ctrl+win+<-/->切换桌面
  13. //ctrl+alt+上/下 改变显示器方向
  14. class Test
  15. {
  16. public:
  17. Test(initializer_list<int> il):vi(il){cout<<"list cons"<<endl;}
  18. vector<int> vi;
  19. explicit Test(string s):vi{1,20} {cout<<"string cons"<<endl;}
  20. };
  21. int main()
  22. {
  23. Test ta{};
  24. //这种空{}优先匹配默认构造函数,默认构造函数可以是explicit的,如果没有默认构造函数,则使用初始值列表构造函数
  25. Test tf{1,2,3};//非空优先考虑初始值列表构造函数,如果无法初始化所有成员,则匹配某一构造函数
  26. Test tb = {};
  27. //拷贝初始化,右侧的{}会优先匹配默认构造函数,但是由于右侧会隐式转换,所以默认构造函数不能是explicit的
  28. Test tb2 = {1,2,3};
  29. //拷贝初始化,右侧的{}会优先匹配初始值列表构造函数,但是初始值列表构造函数不能是explicit的
  30. Test tc({1,2,3});//等价于Test tf{1,2,3};并且初始值列表构造函数可以是explicit的
  31. Test td{"Hello"};
  32. //优先匹配初始值列表构造函数,如果成员无法转换为int,则匹配构造函数
  33. //Test tg({"Hello"});//一定会调用初始值列表构造函数,如果成员无法转换为int,则报错
  34. Test ti({});//一定会调用初始值列表构造函数
  35. //Test te = {"Hello"};
  36. //拷贝初始化,右侧用初始值列表构造函数构造临时量,如果"Hello"不能转换为int,将匹配构造函数(string s),而且(string s)构造函数不能是explicit的
  37. return 0;
  38. }

image.png-16.4kB

  1. class Test
  2. {
  3. public:
  4. Test(initializer_list<int> il):vi(il){cout<<"list cons"<<endl;}
  5. vector<int> vi;
  6. Test(string s):vi{1,20} {cout<<"string cons"<<endl;}
  7. };
  8. int main()
  9. {
  10. Test ta{};
  11. Test tf{1,2,3};
  12. Test tb = {};
  13. Test tc({1,2,3});
  14. Test td{"Hello"};
  15. //Test tg({"Hello"});
  16. Test te = {"Hello"};
  17. return 0;
  18. }

image.png-17.9kB

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注