C++开发初级


878 浏览 6 years, 4 months

99.7 统一初始化

版权声明: 转载请注明出处 http://www.codingsoho.com/

统一初始化

C++之前的初始化语法很乱,有四种初始化方式,而且每种之前甚至不能相互转换。

1.小括号初始化方法:

    //小括号初始化方法
    int a = int(5);
    string str1 = string("hello");

2.赋值初始化方法:

    int a = 3;
    string str = "hello";

3.POD聚合,也就是经常使用的大括号初始分方法:

int arr[2] = {0,1};

4.构造函数初始化:

class test{
    test(int var1,int var2):a(var1),b(var2){};
private:
    int a;
    int b;
};

可以看到,初始化的类型并非总是统一的。

例如下面的两个SpreadsheetCell定义,一个是struct,一个是class

#include <iostream>
#include<vector>

using namespace std;

struct SpreadsheetCellStruct{
  double mValue;
  string mString;   
};

class SpreadsheetCell
{
 public:
  SpreadsheetCell(double initialValue, string initialString):mValue(initialValue), mString(initialString){
  }

 protected:
  double mValue;
  string mString;
};

int main(){
    SpreadsheetCellStruct ss = {10, "struct"};
    SpreadsheetCell sc(10, "class");
    return 0;
}

对于结构,可以使用{....}的语法,但是对于类版本,如果使用

SpreadsheetCell sc1 = {10, "class"};

则会报错

[Error] in C++98 'sc1' must be initialized by constructor, not by '{...}'

C++11中推出了统一初始化方法的新特性。无论是类的变量,数组,STL的容器,类的构造,都统一使用{},以后只要是初始化就首先考虑{}的初始化就可以了。

C11之后,下面的做法都是正确的。

int value[] {1,2,3};
std::vector<int> vi {2 , 3 , 4 , 56, 7};
std::vector<std::string> cities {"Berlin" , "New York " , "london " , "cairo"};
std::complex<double> c{4.0 , 3.0};  //相当于c(4.0 , 3.0);

回到我们前面的例子,下面代码在C11正常编译通过

SpreadsheetCell sc1 = {10, "class"};

甚至等号也是可以忽略的。

SpreadsheetCell sc2{10, "class"};
防止窄化

一个初始化列表强制使用赋值操作, 也就是意味着每个变量都是一个默认的初始化值,被初始化为0(NULL 或者是 nullptr) 如下:

int i; //这是一个未定义的行为
int j{} //j调用默认的构造函数为j赋值为0
int *p //这是一个未定义的行为
int *p{} ;// p被初始化为一个nullptr

然后,精确的初始化,它们减少精度,或者是一个补充值被修改,是不可能的。

使用统一初始化还可以阻止窄化。因为C++可以隐式执行窄化。

int x1(5.3); // 警告,x1为5
int x2=5.3; //警告,x2为5
int x2{4.5}; //错误,精确初始化,不允许窄化  narrowing conversion
int x2 = {4.5}; //错误,不允许窄化  narrowing conversion
char ci{7}; 
char c9{9999}; //error  9999不是一个char类型
// >

可以看到4.5被窄化成了4,但是利用统一初始化可以避免这种情况

初始化动态分配的数组

统一初始化还可以用来初始化动态分配的数组。

    int *array=new int[5]{1,2,3,4,5};
    cout << "array:";
    for(int i=0; i < 5; i++)
        cout << array[i] << " ";
    cout << endl;

输出

array:1 2 3 4 5

初始化类成员数组

统一初始化还可以在构造函数初始化器中初始化类成员数组。

class man{
private:
    int array[4];
public:
    man():array{1,2,3,4}{
    }
    void showArray()const{
        for(int i=0; i < 4; i++)
            cout << array[i] << endl;
    } 
};

int main(){
    man m;
    m.showArray();
    return 0;
}
初始化列表

为了支持初始化列表和用户自定义类型结合,c++11提供了类模板class templete std::initialize_list<> , 可以被用来初始化一个值或者在任何地方你想提供一组值。

void print(initializer_list<int> vars)
{
    for(auto p=vars.begin();
        p!=vars.end(); ++p)
    cout << *p << endl;
}
print({1,3,5,7,9});

当构造函数参数为一个特殊的成员或者是一个初始化列表,初始化列表是被作为首选的

class SpreadsheetCell
{
 public:
  SpreadsheetCell(double initialValue, string initialString):mValue(initialValue), mString(initialString){
  }
  SpreadsheetCell(int inValue1, int inValue2){
    cout << "SpreadsheetCell ctor 2 called!" << endl;
  }
  SpreadsheetCell(std::initializer_list<int> args){
    cout << "SpreadsheetCell ctor 3 called!" << endl;
  }

 protected:
  double mValue;
  string mString;
};



class man{
private:
    int array[4];
public:
    man():array{1,2,3,4}{
    }
    void showArray()const{
        for(int i=0; i < 4; i++)
            cout << array[i] << endl;
    } 
};

int main(){ 
    SpreadsheetCell scl1(77,5); //call (int, int);
    SpreadsheetCell scl2{77,5}; //call (std::initialize_liat<int>);
    SpreadsheetCell scl3{77,5,42}; //call (std::initialize_liat<int>);
    SpreadsheetCell scl4 = {77 , 5}; //call (std::initialize_liat<int>);
    return 0;
}

在构造函数中的用法可参考初始化列表构造函数(仅限C++11)

代码参考 Initialization.cpp
UniformInitialization.cpp

参考文档