1、创建对象时,使用小括号( )和大括号{ }的区别
1)内置类型的初始值,以下三种方法没有区别
int x(0);
int y = 0;
int z{0};
2)自定义类型的赋值
Widget w1; 调用默认构造函数
Widget w2 = w1; 调用拷贝构造函数,不是赋值操作
w1 = w2; 调用operator=函数,是赋值操作
3)类内成员的默认初始值
类内成员的默认初始值:可以使用大括号、等号;不可以使用小括号
class Widget {
...
private:
int x{ 0 }; x的默认初始值为0
int y = 0; 同上
int z( 0 ); 报错
}
注意:当大括号用于类内成员的默认初始值时,如果初始值存在丢失信息的风险,则编译器将报错:
doubel ld = 3.14;
int a {ld}; 报错,存在信息丢失风险
int b (ld); 正确
4)声明对象还是创建对象
调用带参构造函数,可以使用如下方法:
Widget w1(10);
如果想调用午餐构造函数时,不可以使用下面的方法,因为这会被编译器理解为:声明了个函数,而不是创建对象
Widget w2()
正确的方法:
Widget w2;
或者使用大括号
Widget w2{};
2、大括号和std::initializer_list
大括号这么牛逼,统一都使用大括号不就得了。实际情况不是这么理想。这很C++……
1)先看使用 std::initializer_list 的情况(小括号和大括号行为一致)
大括号初始化的缺点是它有时会显现令人惊讶的的行为。这些行为的出现是因为与std::initializer_list混淆了。在构造函数中,只
class Widget {
public:
Widget(int i, bool b);
Widget(int i, double d);
...
};
Widget w1(10, true); 调用第一个构造函数
Widget w2{10, true}; 调用第一个构造函数
Widget w3(10, 5.0); 调用第二个构造函数
Widget w4{10, 5.0}; 调用第二个构造函数
2)形参带有std::initializer_list
如果构造函数的形参带有std::initializer_list,调用构造函数时大括号初始化语法会强制使用带 std::initializer_list 参数的重载构造函数:
class Widget {
public:
Widget(int i, bool b);
Widget(int i, double d);
Widget(std::initializer_list<long double> il);
...
};
Widget w1(10, true); 使用圆括号,调用第一个构造函数
Widget w2{10, true}; 使用大括号,强制调用第三个构造函数,10和true被转换为long double
Widget w3(10, 5.0); 使用圆括号,调用第二个构造函数
Widget w4{10, 5.0}; 使用大括号,强制调用第三个构造函数,10和5.0被转换为long double
3)拷贝构造和赋值构造也受带有std::initializer_list的构造函数的影响
class Widget {
public:
Widget(int i, bool b);
Widget(int i, double d);
Widget(std::initializer_list<long double> il);
operator float() const; // 支持隐式转换为float类型
...
};
Widget w5(w4); 使用圆括号,调用拷贝构造函数
Widget w6{w4}; 使用大括号,调用第三个构造函数,原因是先把w4转换为float,再把float转换为long dobule
Widget w7(std::move(m4)); 使用圆括号,调用移动构造函数
Widget w8{std::move(m4)}; 使用大括号,调用第三个构造函数,理由同w6
4)即使参数数量不匹配,照样优先使用 std::initializer_list 的构造函数,
class Widget {
public:
Widget(int i, bool b);
Widget(int i, double d);
Widget(std::initializer_list<bool> il); // long double 改为 bool
...
};
Widget w{10, 5.0};
报错,因为参数个数不匹配,编译器会忽略另外两个构造函数(第二个还是参数精确匹配的!)
5)只有当大括号内的值无法转换为std::initializer_list元素的类型时,编译器才会使用正常的重载选择方法:
class Widget {
public:
Widget(int i, bool b);
Widget(int i, double d);
Widget(std::initializer_list<std::string> il); // bool 改为 std::string
...
};
Widget w1(10, true); 使用圆括号,调用第一个构造函数
Widget w2{10, true}; 使用大括号,不过调用第一个构造函数,因为无法转换为string
Widget w3(10, 5.0); 使用圆括号,调用第二个构造函数
Widget w4{10, 5.0}; 使用大括号, 不过调用第二个构造函数,因为无法转换为string
6)只有空括号可以不强制匹配 std::initializer_list 构造函数
class Widget {
public:
Widget();
Widget(std::initializer_list<int> il);
...
};
Widget w1; 调用默认构造函数
Widget w2{}; 调用默认构造函数
7)小括号( )和大括号{ }初始化容器时的却
std::vector v1(10, 20); // 使用不带std::initializer_list的构造函数,创建10个元素的vector,每个元素的初始值为20
std::vector v2{10, 20}; // 使用带std::initializer_list的构造函数, 创建2个元素的vector,元素值为10和20
原因是:
std::vector中有一个可以指定容器的大小和容器内元素的初始值的不带std::initializer_list构造函数,但它也有一个可以指定容器中元素值的带std::initializer_list函数。