1、除非有一个非常好的理由允许构造函数被用于隐式类型转换,否则应该把它声明为explicit
2、如果一个新对象被定义,那一定有个构造函数被调用,不可能调用赋值操作。如果没有新对象被定义,就不会有构造函数被调用,那么就是赋值操作被调用
Widget w1; //调用默认构造
Widget w2(w1); //调用拷贝构造
w1 = w2; //调用赋值
Widget w3 = w2; //调用拷贝构造
第一章 让自己习惯C++
C++是个多重泛型编程语言(multiparadigm programming language)、一个同时支持过程形式、面向对象形式、函数形式、泛型形式、元编程形式的语言。
条款1:将C++看作四个次语言的联邦:
1、C。C++中的区块、语句、预处理器、内置数据类型、数组、指针都是来自C。
2、Object-Oriented C++。类、封装、继承、多态、虚函数等。
3、Template C++。泛型编程
4、STL。
条款2:尽量以const,enum,inline替换#define
为何用const、enum替代#define?
1、宏定义是字符替换,没有数据类型的区别,同时这种替换没有类型安全检查,可能产生边际效应等错误;const常量是常量的声明,有类型区别,需要在编译阶段进行类型检查。
2、#define不能用来定义class专属常量,也不能提供任何封装性,不存在private #define,const可以封装。而const可以用于类成员变量的定义。当为让类共享同一个常量,并且只有一份,在前面加static关键字修饰。但是C++只允许对static的整数常量进行in-class处置设定,即
class CostEstimate{
private:
static const double m_dou; //在头文件内声明
static const int m_int = 20; //允许在头文件内初始化static const int类型
//若编译器不允许在头文件内初始化static const int 则使用enum替代
enum {NumSize = 20}; //当然enum只能设置整型
int scores[NumSize]; //类内能初始化static const int也是为了能初始化数组大小
}
//其他的static const类型需要在实现文件内设初值
const double CostEstimate::m_dou = 1.35;
3、enum更像#define,因为enum是无法取地址的。
为何用inline替代#define?
#define EXAMPLE(X) (X*X)
int a = 1;
EXAMPLE(a++); //得到2
EXAMPLE(a+a); //得到15
显然,上述例子得到的结果并非我们想要的,但是如果宏定义的使用不注意的话,就容易造成上述问题,且#define不可以调试,inline可以调试。且inline效率跟#define相同。
条款3:尽可能使用const
int a = 20;
int * const pa1 = &a; //指针常量,指针只能指向初值,不能更改
const int * pa2 = &a; //常量指针,指针指向的值不能更改
int const * pa3 = &a; //常量指针
STL中存在一种迭代器,所指的东西不能被改动,即const_iterator用法跟iterator相同
令函数返回值为一个常量,可以降低很多以外的错误,如:
class Rational1{
int nums;
public:
const friend Rational1 operator*(const Rational1&a, const Rational1& b);
};
const Rational1 operator*(const Rational1&a, const Rational1& b){
Rational1 res;
res.nums = a.nums*b.nums;
return res;
}
int main(){
Rational1 a,b,c;
(a*b) = c; //报错,通过const可以避免这种操作发生
}
C++中const对象只能调用const成员函数,而非const对象都可以调用。const成员函数不可以更改对象内任何non-static成员变量。mutable释放掉non-static成员变量的bitwise constness约束。
C++的const成员函数遵循bitwise constness约束,即const成员函数不会修改对象内的任何数据,而使用mutable修饰的成员可以被const成员函数修改。const成员函数只能调用const成员函数。
对于类中的const和非const成员函数,如果名字相同,且功能一致的话,为了避免代码重复,可以使用non-const调用const来实现non-const函数:
class TextBook{
public :
const char& operator[](size_t position) const{
return text[position];
}
char& operator[](size_t position){
return const_cast<char&>(static_cast<const TextBook&>
(*this)[position]);
}
}
条款4:确定对象被使用前已先被初始化
C++中对于类的构造函数初始化所有成员变量,最好使用构造函数的初始化列表进行初始化,如果在构造函数体内通过赋值操作对成员变量进行赋值,在进入构造函数前会先调用默认构造函数进行初始化一遍,因此效率会比直接上初始化列表低一些。且C++中初始化列表无论怎么改变初始化的成员变量的初始化顺序,其初始化顺序都是按类中声明的顺序进行初始化的。
class Rational{
int nums;
double prices;
std::string str;
public:
Rational(int nums, double price, std::string str):nums(nums),prices(price),str(str) {}
};
C++将函数中的声明static变量称为local static变量,将非函数中的声明static变量成为non-local static变量。
对于在多个文件中的non-local static变量,如果一个non-local static变量的初始化需要使用另一个文件中的non-local static变量,但是编译器无法检测出其没有初始化。对于这种问题,可以通过将每个non-local static对象搬到自己的专属函数中,对象在函数内被声明为static。而函数返回指向对象的引用。用户直接调用这些函数,而不直接使用这些对象。
class FileSystem{};
FileSystem& tfs(){
static FileSystem fs;
return fs;
}
class Directory{};
Directory::Directory(){
size_t disk = tfs().numDisk();
}
Directory& dir(){
static Directory dir;
return dis;
}