C++初始化
C++初始化
C++11之前的初始化
T var;
// 构造
T var(val);
T var= val;
// 列表
T var{val};
T var={val};
C++11统一了初始化方法:列表初始化 brace-initialization
消除了之前初始化基本类型、聚合类型和非聚合类型、及数组和标准容器之间的区别
int i{3}; // 基本类型
// 自定义类型
foo f1{};
foo f2{1,2};
// POD类型
struct bar{
int a;
double b;
};
bar b{32, 3.1};
// 数组
int arr[3]{1,2,3};
int* arr= new int[]{1,2,3}; // 动态数组分配
// 标准库容器
std::vector<int> v{1,2,3};
std::map<int,std::string> mp{{1,"one"},{2,"two"}};
// 对于存在参数为`std::initializer_list`的容器类型
std::vector<int> v{10, 20}; // 2 elements: 10,20
std::vector<int> w(10, 20); // 10 elements: all 20
列表初始化--模板
template<typename T>
T copy(T const& val){
return T{val};
}
auto a= copy(std::string{});
auto b= copy(std::vector<int>{});
auto c= copy(std::vector<std::any>{});
a
为空字符串的拷贝
b
为空向量的拷贝
c
类型为std::vector<std::any>
,但是size为1,这也是std::any
可以是任意类型变量的原因
template<typename T>
std::vector<T> create(){
return std::vector<T>{10};
}
int main(){
auto a= create<std::string>();
auto b= create<int>();
auto c= create<char>();
auto d= create<std::vector<int>>();
std::cout<<a.size()<<std::endl; // 10 调用了 std::vector<std::string>(10);
std::cout<<b.size()<<std::endl; // 1
std::cout<<c.size()<<std::endl; // 1
std::cout<<d.size()<<std::endl; // 10
return 0;
}
类型推导
std::vector<int> v{1,2,3};
std::vector<int> w{v.begin()+1, v.end()};
std::vector w2{v.begin()+1, v.end()};
w2的类型是std::vector<std::vector<int>::iterator>
std::vector v2{std::vector{1,2}};
按理解v2的类型应该是:std::vector<std::vector<int>>
但是,其实v2类型为std::vector<int>
此时必须显式指定类型
std::vector<std::vector<int>> v2{std::vector{1,2}};
对象初始化
数据成员初始化
class MyClass{
public:
MyClass(){}
private:
std::vector<int> v(1,2); // error
};
报错,可以采用初始化列表方式
class MyClass{
public:
MyClass():v(1,2){}
private:
std::vector<int> v;
};
或者
class MyClass{
public:
MyClass(){}
private:
std::vector<int> v= std::vector<int>(1,2);
};
对象初始化
class MyClass{};
MyClass f();
编译报错,提示remove parentheses to default-initialize a variable
因为C++无法区分是函数声明还是对象创建
可以使用统一初始化方式:列表初始化
但需要注意,列表是以std::initializer_list
方式存在,若构造函数中存在std::initializer_list
为参数的构造函数,则优先调用
class MyClass{
public:
MyClass(){} // 1
MyClass(int x, int y){} // 2
MyClass(std::initializer_list<bool> z){} // 3
};
int main(){
MyClass obj2{}; // call 1
MyClass obj(5, 1.0); // call 2
MyClass obj{0, 1}; // call 3
MyClass obj3({}); // call 3
return 0;
}
若MyClass
不存在含有initializer_list
参数的构造函数,则MyClass obj{5, 1.0};
会调用构造函数MyClass(int x, int y){}
explicit
显式声明关键字
若类的构造函数是单形参的,则应该将的构造函数声明为显式的
以防止隐式的类型转换behind-the-scenes type conversions
如下示例
class IntCell{
public:
IntCell(){}
IntCell(int tmp){}
};
正常情况下,C++宽松的法则,会允许下面的隐式转换执行成功
IntCell obj;
obj=37;
等价于下面的伪代码
IntCell obj;
IntCell temp=37;
obj= temp;
单参数构造函数定义了一个隐式的类型转换,在转换时会创建一个临时对象,使得赋值(或函数参数)兼容
对于obj=37;
语句,由于类型的不匹配,程序员通常是不希望执行成功的
此时应将单参数构造函数声明为显式的,即explicit IntCell(int tmp){}
,以避免隐式转换
IntCell obj1; // 默认构造函数
IntCell obj2(12); // 单参数构造函数
IntCell obj3=12; // 不合法,explicit
IntCell obj4(); // 函数声明
obj4
的混乱现象是使用花括号统一初始化语法的一个原因
IntCell obj4; // 默认构造函数
IntCell obj4{}; // 默认构造函数
IntCell obj2{3}; // 单参数构造函数