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