C++可视为:
四者的集合。
例子:
int a = 10;int* const b1 = &a; //顶层const,b1本身是一个常量const int* b2 = &a; //底层const,b2本身可变,所指的对象是常量const int b3 = 20; //顶层const,b3是常量不可变const int* const b4 = &a; //前一个const为底层,后一个为顶层,b4不可变const int& b5 = a; //用于声明引用变量,都是底层constmutable(可变的)。mutable释放掉non-static成员变量的bitwise constness约束。(即可以在const成员函数内修改 mutable修饰的成员变量const变量在成员变量那里声明,然后再在构造函数初始化,那样会报错,只能在构造函数体运行前完成初始化赋值工作,就是在构造函数初始化列表。**const**或**references**,为避免需要记住成员变量何时必须在成员初值列中初始化,何时不需要,最简单的做法就是:构造函数使用成员初始化列表来赋值,而不是在构造函数里去赋值(会导致赋值两次,浪费了)base classes更早于其derived classes被初始化(见条款12),而class的成员变量总是以其声明次序被初始化。上面的第一条法则,是为了防止内存泄漏
第二条法则则是为了防止额外花销,当一个函数不通过基类指针进行虚构时,首先是析构派生类自己,接着会按照继承顺序的反方向来,依次调用相应的析构函数。故不需要额外的虚函数指针来确保内存安全。
当使用多态这个特性的时候,用基类指针指向派生类时,析构函数的调用是静态的,故需要将析构声明为虚的,进行重写,为析构时调用的函数特制化,才能避免内存泄漏。
为了实现“连锁赋值”,赋值操作符必须返回一个reference指向操作符的左侧实参。令赋值(assignment)操作符返回一个reference to*this。
由于变量有别名的存在(多个指针或引用只想一个对象),所以可能出现自我赋值的情况。比如 a[i] = a[j],可能是同一个对象赋值。这时就需要慎重处理赋值操作符以免删除了自己后再用自己来赋值。
解决方法有:

把资源放进对象内,我们便可倚赖 C++的“析构函数自动调用机制”确保资源被释放。
因为编译器对于“跨越语句的各项操作”没有重新排列的自由(只有在语句内它才拥有那个自由度)。
以独立语句将 newed对象存储于(置入)智能指针内。如果不这样做,一旦异常被抛出,有可能导致难以察觉的资源泄漏。
好的接口要容易被正确使用,不容易被误用,符合客户的直觉。
在设计class时,要考虑一系列的问题,包括
pass by reference-to-const这种传递方式的效率高得多:没有任何构造函数或析构函数被调用,因为没有任何新对象被创建。以by reference方式传递参数也可以避免slicing(对象切割)问题。当一个derived class对象以by value方式传递并被视为一个base class对象,base class的copy构造函数会被调用,而“造成此对象的行为像个derived class对象”的那些特化性质全被切割掉了,仅仅留下一个base class对象。
从封装的角度观之,其实只有两种访问权限:private(提供封装)和其他(不提供封装)。
通常,令类支持隐式类型转换通常是个糟糕的主意。当然这条规则有其例外,最常见的例外是在建立数值类型时。
例:
const Rational operator*(const Rational& rhs) const;//如果定义一个有理数类,并实现*操作符为成员函数,如上所示;那么考虑一下调用:Rational oneHalf(1, 2);result = oneHalf * 2; // 正确,2被隐式转换为Rational(2,1) //编译器眼中应该是这样:const Rational temp(2); result = oneHalf * temp;result = 2 * oneHalf; // 错误,2,可不被认为是Rational对象;因此无法调用operator* class Rational { ... // contains no operator* }; const Rational operator*(const Rational& lhs, Rational& rhs) { return Rational(lhs.numerator() * rhs.numerator(), lhs.denominator() * rhs.denominator()); } Rational oneFourth(1, 4); Rational result; result = oneFourth * 2; result = 2 * oneFourth; //这下两个都工作的很好,通过隐式转换实现std::swap对你的类型效率不高时,提供一个swap成员函数,并确定这个函数不抛出异常。member swap,也该提供一个non-member swap用来调用前者。对于classes(而非templates),也请特化std::swap。swap时应针对std::swap使用using声明式,然后调用swap并且不带任何“命名空间资格修饰”。std templates全特化是好的,但千万不要尝试在std内加入某些对std而言全新的东西。请记住:
避免让外部可见的成员函数返回handles(包括引用、指针、迭代器)指向对象内部(更隐私的成员变量或函数),即使返回const修饰也有风险。这一方面降低了封装性,另一方面可能导致其指向的对象内部元素被修改或销毁。
struct RectData { Point ulhc; Point lrhc; }; class Rectangle { public: ... Point& upperLeft() const { return pData->ulhc; }1//const只对函数内进行保护,函数返回后呢?? Point& lowerRight() const { return pData->lrhc; }2 //const只对函数内进行保护,函数返回后呢?? private: std::tr1::shared_ptr<RectData> pData; ... };1,2两函数都返回引用,指向private内部数据,调用者于是可通过这些引用更改内部数据!这严重破坏了数据的封装性,对私有成员进行直接操作?太不可思意了!
异常安全函数(Exception-safe functions)提供以下三个保证之一:
异常安全码(Exception-safe code)必须提供上述三种保证之一。如果它不这样做,它就不具备异常安全性。
copy and swap。原则很简单:为你打算修改的对象(原件)做出一份副本,然后在那副本身上做一切必要修改。若有任何修改动作抛出异常,原对象仍保持未改变状态。待所有改变都成功后,再将修改过的那个副本和原对象在一个不抛出异常的操作中置换(swap)。
inline只是对编译器的一个请求,大部分编译器拒绝将太过复杂(例如带有循环或递归)的函数inlining,而所有对virtual函数的调用(除非是最平淡无奇的)也都会使inlining落空。inlining某个函数,还是可能为该函数生成一个函数本体。举个例子,如果程序要取某个inline函数的地址,编译器通常必须为此函数生成一个outlined函数本体。毕竞编译器哪有能力提出一个指针指向并不存在的函数呢?Handle classes和Interface classes。Handle classes是一个声明类,一个imp实现类,声明类中不涉及具体的定义,只有接口声明,在定义类中include声明类,而不是继承。而Interface classes是在接口类中提供纯虚函数,作为一个抽象基类,定义类作为其子类来实现具体的定义。derived classes内的名称会遮掩base classes内的名称。在public继承下从来没有人希望如此。尽管函数参数不同,仍然会被遮掩
声明一个纯虚函数的目的是为了让衍生类只继承其函数接口,而自己进行函数定义实现。
声明一个虚函数的目的是为了让衍生类继承该函数的接口和缺省实现(一般实现),如果有特别的操作需求,可以在衍生类中进行实现来覆盖。如果担心因此忘记做特异化实现,可以利用纯虚函数,在父类给纯虚函数一个实现,然后在子类的该函数的实现中调用它,这样就会记得在需要特异化的子类中进行其他特异化实现。
声明一个非虚函数的目的是为了让衍生类完全继承该函数的接口和实现,也就是声明该函数的实现方式不得更改,所有子类都表现一致。
virtual函数(本质是希望子类的实现不同)的替代方案:(并非不推荐virtual,只是推荐我们多去思考)
public的非虚函数来调用private的虚函数具体实现,非虚函数必须为子类继承且不得更改,所以它决定了何时调用以及调用前后的处理;虚函数实现可以在子类中覆写,从而实现多态。(NVI手法,NVI手法自身是一个特殊形式的Template Method设计模式)
tr1::function成员变量替换虚函数,从而允许包括函数指针在内的任何可调用物搭配一个兼容于需求的签名式。(Strategy设计模式)本条款的启示为:为避免陷入面向对象设计路上因常规而形成的凹洞中,偶尔我们需要对着车轮猛推一把。这个世界还有其他许多道路,值得我们花时间加以研究。
classes之间是private继承关系,那么编译器不会自动将一个derived class对象转换为一个base class对象。private base class继承而来的所有成员,在derived class中都会变成private属性,纵使它们在base class中原本是protected 或public属性。class A {public: void check() { cout << "A" << endl; } }; class B {private: void check() const { cout << "B" << endl; }}; class C : public A, public B {}; int main(){ C c; c.check(); //报错 }注意以上无法调用,因为尽管B的check()是private的,但是c++先找出最佳匹配才检查可用性,但是此时没有最佳匹配。为解决歧义必须显示指出调用哪个函数例如c.A::check();
virtualbase classes不带任何数据,将是最具实用价值的情况。例子:
template<typename T>void print(const T& container){ T::const_iterator* m; ...}这里我们声明m为一个局部变量,它是一个指向T::const_iterator的指针。
但是有一种情况,如果T::const_iterator它不是一个类型,而是T中的一个静态成员变量(确实有这个可能),而这时m碰巧也是一个全局变量(哪有这么巧的事呀!),那上面的代码就不是定义一个指针变量了,而是两个静态变量相乘。
上面的情况虽然特殊,但确实有可能发生的。所以C++解析器的设计者必须考虑如何避免这个问题。
C++有个规则可以解析这一歧义状态:如果解析器在模板中遇到这个嵌套从属名称,它便假设这个名称不是一个类型。除非你明确告诉他。在缺省情况下,嵌套从属名称不是类型。
应给编译器点提示:
template<typename T>void print(const T& container){ if(container.size()>=1) typename T::const_iterator iter(container.begin()); //明确指定是一个类型 ...}如果基类是模板类,那么衍生类直接调用基类的成员函数无法通过编译器,因为可能会有特化版的模板类针对某个类不声明该接口函数。
解决方法有:
例子:
template<typename T1>class Class_A{public: void send1(T1 var);};template <typename T1>class Class_B:public Class_A< T1 >{public: void send2(T1 var) { send1(var); } //编译错误,调用模板基类内函数失败};原因:由于base class template可能被特化,而特化版本可能不提供和一般性template模板相同的接口。因此编译器往往拒绝在templated base class(模板基类)内寻找继承而来的名称,例子:
template<>class Class_A<Type_1>{ //特化模板类,类型为Type_1时的模板类public: void send3(Type_1 var); // 该特化类中不存在send1函数};知识点:template<>为特化模板标志
现象:由于特化类中不存在send1函数,因此派生类调用该函数失败。所以编译器由于知道基类模板可能被特化,使得接口不一致,所以禁止了这样的调用。
解决方法的原理:对编译器承诺,base class template的任何版本(包括特化版本)都将支持一般版本所提供的的接口。
template <typename T1>class Class_B:public Class_A< T1 >{public: void send2(T1 var) { this->send1(var); } // 告诉编译器,假设send1被继承};template <typename T1>class Class_B:public Class_A< T1 >{public: using Class_A< T1 >::send1; // 告诉编译器,send1在基类中 void send2(T1 var) { send1(var); } };template <typename T1>class Class_B:public Class_A< T1 >{public: void send2(T1 var) { Class_A< T1 >::send1(var); } // 告诉编译器,send1在基类中 // 此方法不好若send1为虚函数,则Class_A< T1 >::修饰会关闭virtual绑定行为};任何模板代码都不该与某个造成膨胀的参数产生相依关系:

template<typename T>class SmartPtr {public: template<typename U> SmartPtr(const SmartPtr<U>& other) // 以other的heldPtr初始化this的heldPtr : heldPtr(other.get()) { ... } T* get() const { return heldPtr; } ...private: T* heldPtr; // 这个SmartPtr持有内置指针};下面是tr1::shared_ptr的一份定义摘要,例证上述所言
template<class T>class shared_ptr {public: shared_ptr(shared_ptr const& r); // copy构造函数 template <class Y> shared_ptr(shared_ptr<Y> const& r); // 泛化copy构造函数 shared_ptr& operator=(shared_ptr<Y> const& r); // copy assignment template <class Y> shared_ptr& operator=(shared_ptr<Y> const& ); // 泛化copy assignment ...};条款24讨论过为什么唯有non-member函数才有能力“在所有实参身上实施隐式类型转换”,
template<typename T> class Rational { public: ... friend const Rational operator*(const Rational& lhs, const Rational& rhs); };template<typename T>const Rational<T> operator*(const Rational<T>& lhs, const Rational<T>& rhs){ ... }如何使用一个traits class了:
请记住
例子:
我们不要这样:
我们想在编译期就确定调用哪个advance,于是实现traits classes,如stl中的容器那样,每个容器都有自己的迭代器,而且迭代器可以在类内typedef,这样不会影响到外面,而且还可以入乡随俗,依据此实现trait
trait别名,这里是迭代器
trait分类(此处为迭代器),实现一组重载函数(身份像劳工)或函数模板,

TMP可将工作由运行期移往编译器,因而得以实现早期错误侦测和更高的执行效率。
实现方式以模板为基础,因为模板会在编译时确定,上一条款的traits classes就是一种TMP,依靠模板函数参数不同的重载来在编译器模拟if else(其在运行期才会判断)。
另一个例子是用模板来在编译器实现阶乘:
template<unsigned n>struct Factorial { enum { value = n * Factorial<n-1>::value };};template<>struct Factorial<0> { enum { value = 1 };}用模板来实现递归从而在编译器实现阶乘运算,用参数为0的特异化来做递归的终结。


如果你的new接收的参数除了必定有的size_t外还有其他,就是个placement new。delete类似。
当创建对象时,会先进行new,然后调用构造函数,如果构造出现异常,就需要delete,否则内存泄漏。如果用了placement new,那么编译器会寻找含有同样参数的placement delete,否则不会delete,因此必须成对写接收同样参数的placement new和placement delete。
同时,为了让用户主动使用delete时能进行正确操作,你需要同时定义一个普通形式的delete,来执行和placement delete同样的特殊实现。
你在类中声明placement new后,会掩盖C++提供的new函数,因此除非你确实不想用户使用默认的new,否则你需要确保它们还可用(条款33)。
而TR1是新的一系列组件,在std内的tr1命名空间中,比如:std::tr1::shared_ptr。它包含:
Boost是一个程序库,其由C++标准委员会成员创设,可视为一个“可被加入标准C++的各种功能”的测试场,涵盖众多经过多轮复核的优质程序,如果想知道当前C++最高技术水平、想一瞥未来C++的可能长相?看看Boost吧。
http://boost.org