条款4:非必要不提供default constructor
这里主要是列举下默认构造函数的优点和缺点。
如果没有默认构造函数,定义对象数组会比较麻烦,因为对象数组初始化的时候没法传递非默认构造函数的值,如果要使用,书中提到的方法是给数组每个变量初始化的时候调用构造函数,另一个就是使用指针数组。
第一个的缺点很明显,没法声明类似A a[10];这样的数组,在堆上申请,还得用到placement new这个之前没讲过的东西,另外还得一个个去初始化;后者的缺点当然是,数组里面的每个指针都需要记得去delete掉。
另外,一些模板容器也可能无法使用,因为一般模板容器为了通用,一般都会直接调用默认构造函数。
如果给这样的类增加默认构造函数,有个很打的缺点,就是类成员变量无法控制是否初始化,这样类函数可能会花大力气去判断每个变量是否都已经初始化。
所以我觉得这条还是要看这个类要怎么用,没有绝对的是否要提供默认构造函数。
条款5:对定制的“类型转换函数”保持警惕
自己定制“类型转换函数”的情况应该不多吧,这里主要记录下为什么要给类的构造函数前增加explicit关键字。
类似这样的代码:[cce lang="cpp"]
#include <iostream>
class A
{
public:
A(int a){_a = a;}
bool operator==(const A &lhs) {return _a == lhs._a;}
private:
int _a;
};int main()
{
A a(1);
if(a == 1)
std::cout << "equal";
else
std::cout << "not equal";
return 0;
} [/cce]
#include <iostream>
class A
{
public:
A(int a){_a = a;}
bool operator==(const A &lhs) {return _a == lhs._a;}
private:
int _a;
};int main()
{
A a(1);
if(a == 1)
std::cout << "equal";
else
std::cout << "not equal";
return 0;
} [/cce]
这段代码的输出是equal,因为在if(a == 1)的时候,编译器发现A类型的operator==只能比较A类型,刚好A又有一个参数为int的构造函数,就在这里对后面的1做了隐式转换,通过A(1)这个构造函数构造了A对象,然后调用A.operator==进行比较。
这样的隐式转换可能会存在风险,当不期望这样的转换,就需要在构造函数之前加上explicit关键字,禁止编译器进行隐式转换。如果加了explicit之后,编译会有这样的错误:
test.cpp: 在函数‘int main()’中: test.cpp:15:10: 错误:‘operator==’在‘a == 1’中没有匹配 test.cpp:7:7: 附注:备选是: bool A::operator==(const A&)