1.条款03:尽可能使用const
1.1 const能够修饰指针,指针所指物,二者或者都不是const对象。注意const的位置,位于*号左边的是修饰所指物为常量, *右边是修饰指针
const char* p = greeting //const data, non-const pointer
char* const p = greeting //const pointer, non-const data
const char* const p = greeting // all the const
例子,以迭代器中的T*指针来讲明具体的用法:
std::vector<int> vec;
...
const std::vector<int>::iterator iter = vec.begin(); // iter做用是T* const
*iter = 10; //能够改变指针所指
iter++; //报错,不能修改iter常量
std::vector<int>::const_iterator cIter = vec.begin(); //cIter做用是const T*
*cIter = 10; //不能够修改指针指向
cIter++; //没问题,能够改变所指物
下列两个函数的参数所返回的参数类型是一致的
void f1(const Widget* pw) // f1和f2都返回了一个指针指向常量Widget
void f2(Widget const* pw)
1.2 const成员函数: 经过声明const成员函数有两个好处,第一:使得class接口更容易被理 解,什么内容是不能被修改的 第二:提高C++效率,pass-by-reference-to-const. PS:两个成员函数若是是常量,同样能够被重载。
1.2.1 bitwise constness and logical constness
2.条款04:肯定对象被使用前已经被初始化Make sure that objects are initialized before they're used.
2.1对于内置类型以外的对象的初始化,主要由构造函数来实现,因此确保每一个构造函数把全部的成员变量初始化。
2.2【理解】注意不要混淆了初始化(initialization)和赋值(assignment),参考下面的例子:
赋值操做:
class PhoneNumber{...};
class ABEntry{
private:
std::string theName;
std::string theAddress;
std::List<PhoneNumber> thePhones;
int numTimesConsulted;
public:
ABEntry(const std::string& name, const std::string& address, const std::List<PhoneNumber>& phones);
};
//构造函数进行赋值(assignments)而并不是是初始化(initializations)
ABEntry::ABEntry(const std::string& name, const std::string& address, const std::List<PhoneNumber>& phones){
theName = name;
theAddress = address
thePhones = phones;
numTimesConsulted = 0;
}
//构造函数初始化的最佳写法是使用MemberInitialization List(成员初始化列表)的方式实现
ABEntry::ABEntry(const std::string& name, const std::string& address, const std::List<PhoneNumber>& phones)
: theName(name), theAddress(address), thePhones(phones), numTimesConsulted(0)
{}
这个构造函数比上面的效率更高!上面那个基于赋值的那个构造函数要首先调用一个default默认构造函数设置成员变量初始值。
2.3 为免除“跨编译单元之初始化次序”问题,以local static对象替换non-local static对象
第二:构造函数,析构函数,赋值
1.条款05 了解C++默默调用了哪些函数 Know what functions C++ silently writes and calls
1.1 空类Empty class,C++处理以后会为它添加默认的构造函数,一个赋值操做符和一个析构函数。
若是你写了 class Empty{}; 通过C++处理后至关于:
class Empty{
public:
Empty(){...}; //default构造函数
Empty(const Empty& rhs){..}; //copy构造函数
~Empty(){...}; //析构函数,是否为Virtual?
Empty& operator=(const Empty& rhs){} //copy assignment操做符
}
【请记住】:编译器暗自为class建立default构造函数,copy构造函数,析构函数和copy assignments操做符。
2.条款06 若不想使用编译器自动生成的函数,就该明确拒绝。
为了避免使用编译器自动提供建立函数的功能,可将相应的成员函数声明为private而且不予实现。使用像Uncopyable这样的的base class也是一个方法。
//Uncopyable实现阻止编译器自动建立函数
class Uncopyable{
protected:
Uncopyable(){};
~Uncopyable(){};
private:
Uncopyable(const Uncopyable&); //阻止copy构造函数
Uncopyable& operator=(const Unconpyable&);
}
为了防止HomeSale对象被拷贝,经过继承Uncopyable类即可以免
class Homesale: private Uncopyable{
... //class将再也不自动声明 copy构造函数以及copy assignment操做符。
...
}
3. 条款07 为多态基类声明virtual析构函数(Declare destructors virtual in polymorphic base classes)
【请记住】:
3.1 Polymorphic(带多态性质的)的基类base class,应该声明一个virtual析构函数。若是一个class存在任何一个virtual函数,避免由于子类继承过程当中带来的问题。应该拥有声明一个virtual析构函数。
3.2 Classes设计的目的若是不是为了实现多态做为基类来使用,就不应声明virtual析构函数。
错误使用方法
class SpecialString: public std::string{ //馊主意!std::string中有个non-virtual函数
....
};
SpecialString* pw = new SpecialString("Impending Doom");
std::string* ps;
...
ps = pw; //SpecialString* = string*
delete ps; //未定义!*ps的SpecialString资源会泄露
//由于SpeicalString的析构函数没被调用
4. 条款08 别让异常逃离析构函数(Prevents exceptions from leaving deconstructors)
【请记住】
4.1 析构函数绝对不要吐出异常,若是一个被析构函数调用的函数可能抛出异常,那么析构函数应当捕捉任何异常,而后吞下他们或者结束程序。
4.2 若是客户须要对某个操做函数运行期间抛出的异常作出反应,那么class应当提供一个普通函数(而非在析构函数中)处理。