c++11 类成员初始化(C++之列表初始化详解)
导读:1、列表初始化的规则...
1 、列表初始化的规则
在C++11中可以直接在变量名后面加上初始化列表来进行对象的初始化 。
struct A { public: A(int) {} }; int main() { A a(123); A c = { 123 }; A d{123}; // c++11 int e = {123}; int f{123}; // c++11 return 0; }聚合类型可以进行直接列表初始化 ,那么什么是聚合类呢?
(1)类型是一个普通数组 ,如int[5],char[] ,double[]等
(2)类型是一个类 ,且满足以下条件:
没有用户声明的构造函数 没有用户提供的构造函数(允许显示预置或弃置的构造函数) 没有私有或保护的非静态数据成员 没有基类 没有虚函数 没有{}和=直接初始化的非静态数据成员 没有默认成员初始化器文字难以琢磨可以看下面例子帮助理解:
// 有自定义的构造函数 ,不能列表初始化 class A { public: A(int, int){} int a; int b; int c; }; // 含有虚函数 ,不是聚合类 class B { public: virtual void func() {} int a; int b; }; // 有基类 ,不是聚合类 class Base {}; class C : public Base { public: int a; int b; }; // 有等号初始化 ,不是聚合类 class D { public: int a; int b = 10; }; // 含有私有的非静态数据成员 ,不是聚合类 class E { public: int a; int b; private: int c; }; // 含有默认成员初始化器 ,不是聚合类 class F { public: F() : a(0), b(0) {} int a; int b; };2 、列表初始化的优点
(1)高效 ,减少调用构造函数的次数
#include<iostream> using namespace std; class Data { public: // 无参构造函数 Data() {cout<<"This is Data constructor1"<<endl;} // 拷贝构造函数 Data(const Data&) {cout<<"This is Data constructor2"<<endl;} // 拷贝赋值构造函数 Data& operator=(const Data&) {cout<<"This is Data constructor3"<<endl;} }; // 低效写法 class Test1 { public: Test1(Data data) {m_data = data;} private: Data m_data; }; // 高效写法 class Test2 { public: Test2(Data data) : m_data(data){} private: Data m_data; }; // 更高效的写法 class Test3 { public: Test3(Data& data) : m_data(data){} private: Data m_data; }; int main() { Data a; cout<<"---------------THIS IS TEST1---------------"<<endl; Test1 t1(a); cout<<"---------------THIS IS TEST2---------------"<<endl; Test2 t2(a); cout<<"---------------THIS IS TEST3---------------"<<endl; Test3 t3(a); return 0; } /* 输出结果: This is Data constructor1 ---------------THIS IS TEST1--------------- This is Data constructor2 This is Data constructor1 This is Data constructor3 ---------------THIS IS TEST2--------------- This is Data constructor2 This is Data constructor2 ---------------THIS IS TEST3--------------- This is Data constructor2 (1)对于TEST1,没有使用列表初始化 ,所以其私有变量m_data是通过调用Data()定义的 ,所以会出现“This is Data constructor1 ”,而“This is Data constructor2 ”是在发生在参数传递调用的拷贝构造 ,最 后“m_data = data”会发生拷贝赋值 ,从而调用“This is Data constructor3 ” (2)对于TEST2,“This is Data constructor2 ”也是在发生在参数传递调用的拷贝构造 ,而另外一次调用是 发生在列表初始化“Test2(Data data) : m_data(data){} ” (3)对于TEST3 ,则是TEST3的构造函数中参数使用了引用 ,就避免了参数传递而调用的拷贝构造 ,所以只有一次 调用拷贝构造是发生在列表初始化的时候 */(2) 防止类型窄化 ,避免精度丢失的隐式类型转化
C++11可使用 explicit 关键字对单参数的构造函数进行声明 ,让编译器无法进行隐式类型转换 ,但仅限于单参数或者其他参数有默认值的情况下使用 。而列表初始化是禁止避免精度丢失的隐式类型转化 ,不像 explicit 禁止所有的隐式类型转换
int main() { // 浮点型到整型的转换 int a = 1.2; // ok int b = {1.2}; // error // 整型到浮点型的转换 float c = 1e70; // ok float d = {1e70}; // error // char的枚举类型 const int i = 1000; const int j = 65; char k = i; // ok char l = {i}; // error char m = j; // ok ,m为A char m = {j}; // ok,因为是const类型 ,这里如果去掉const属性 ,也会报错 }(3)可以使用初始化列表接受任意长度的参数
std::initializer_list,它可以接收任意长度的初始化列表 ,但是里面必须是相同类型T ,或者都可以转换为T
class Test { public: Test(std::initializer_list<int> list) { for (auto iter = list.begin(); iter != list.end(); ++iter) { vec.push_back(*iter); } } private: std::vector<int> vec; };创心域SEO版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!