Effective C++55条款速记版(上)

  1. 视C++为一个语言联邦——c、Object-Oriented C++、Template C++、STL 。
  2. 尽量以const, enum, inline替换#define
  3. 尽可能使用const
  4. 确定对象被使用前已先被初始化
  5. 了解C++默默编写并调用哪些函数——default构造,copy构造,析构,copy assignment(拷贝赋值)惟有被需要时候,才被编译器创建起来。
  6. 若不想使用编译器自动生成的函数,就该明确拒绝——将copy构造,copy assignment操作声明为private而且没有定义,可用伪copy构造函数。
  7. 为多态基类声明virtual析构函数——只有class内含至少一个virtual函数,才为它声明virtual析构函数。classes设计母的如果不是作为base classes使用,或不具备多态性,就不该声明virtual析构函数。
  8. 别让异常逃离析构函数——数据库连接例子中,使用双保险,设置bool类型变量检测,若用户未关闭则替用户关闭,若用户关闭,不做任何操作。若关闭抛出异常,吐下异常。
  9. 绝不在构造和析构过程中调用virtual函数——因为在构造和析构函数中,virtual函数将失去多态性。这类调用从不下降至derived class。
  10. 令operator = 返回一个reference(引用) to *this。为了实现连锁形式如x = y = z = 15;该协议同样适用于+=,-=,*=,等,无强制性,只是一个协议。
  11. 在operator = 中处理“自我赋值"(widget w; w = w;//自我赋值)——可能由于"别名"引起。 让operator=具备异常安全性,将自动获取“自我赋值完全的回报”。eg:
    widget& widget::operator=(const Widget& rhs)
    { 
        Bitmap* pOrig = pb;          //记住原先的pb
        pb = new Bitmap(*rhs.pb);    //令pb指向*pb的一个副本
        delete pOrig;                //删除原先的pb
        return *this;
    }
  12. 复制对象时勿忘其每一个成分——(1)复制所有local成员变量,(2)调用所有base classes内的适当的copying函数。(3)不要尝试以某个copying函数实现另一个copying函数。应该将共同部分放在第三个函数中,两个copying共同调用。eg:
  13. 以对象管理资源——RAII(资源取得时机便是初始化时机),在heap-based资源上(1)使用智能指针auto_ptr(类指针对象)如:std::auto_ptr pInv(createInvestment())管理,由于auto_ptr被销毁时会自动删除它所指之物,别让多个auto_ptr同时指向同一个对象。若通过copying函数复制他们,他们会变成null,而复制所得的指针将取得资源的唯一拥有权(2)使用“引用计数型智慧指针”(reference-counting smart pointer;RCSP)--tr1::shared_ptr pInv(createInvestment())。
  14. 在资源管理类中小心copying行为——(1)抑制copying行为(2)施行引用计数法(3)复制底部资源,进行深拷贝(4)转移底部资源的拥有权如auto_ptr奉行的复制意义。
  15. 在资源管理类中提供对原始资源的访问——(1)显式转换get()函数,更加安全,但较繁琐(2)隐式转换,更加自然方便,但增加出错机会。
  16. 成对使用new和delete时要采用相同形式——(1)使用new动态生成一个对象时候,第一,内存被分配出来(通过operator new的函数见E49和E51),第二,针对此内存会有一个构造函数被调用。(2)使用delete,第一析构函数调用,第二内存被释放(通过operator delete的函数见E51)。(3)new表达式中使用【】,delete表达式中也要使用【】。
  17. 以独立语句将newed对象置入智能指针——错误做法:
    processWidget(shared_prt pw(new Widget), priority());
    做三件事(1)调用priority(2)执行“new Widget”(3)调用tr1::shared_ptr构造函数,一旦调用顺序为2->1->3,而在1时候导致异常,则资源泄露。因此正确做法是:
    shared_prt pw(new Widget); 
    processWidget(pw,priority());
    分两行,独立newed对象置入智能指针。
  18. 让接口容易被使用,不容易被误用——“阻止误用”的办法包括建立新类型、限制类型上的操作,舒服对象值,以及消除客户的资源管理责任。
  19. 设计class犹如设计type——设计高效classses需要回答如下问题(1)新type的对象应该如何被创建和销毁?(2)对象的初始化和赋值该有什么样的差别?(3)新type的对象如果被passed by value,意味着什么?(4)什么是新type的“合法值”?(5)你的新type需要配合某个继承图系吗?(6)你的新type需要什么样的转换?(7)什么样的操作符和函数对此新type而言是合理的?(8)什么样的函数应该被驳回?(9)谁该取用新type的成员?(10)什么是新type的“未声明接口”?(11)你的新type有多么一般化?(12)你真的需要一个新type吗?
  20. 宁以pass-by-reference-to-const替换pass-by-value——前者高效并且可避免切割问题,对于内置类型,以及STL的迭代器和函数对象,使用pass-by-value往往比较合适。
  21. 必须返回对象时,别妄想返回其reference——任何函数如果返回一个reference指向某个local对象,都将一败涂地。绝不返回pointer或者reference指向一个local stack对象,或返回指向一个local static对象而又可能同时需要多个这样的对象。
  22. 将成员变量声明为private——private提供封装,其他不提供封装。
  23. 宁以non-member、non-friend替换member函数——在实现一些功能时候,使用non-member函数能够带来更好的封装性。为了实现封装,可以引入namespace技术。
  24. 若所有参数皆需类型转换,请为此采用non-member函数——
class Rational
{
public:
    Rational(int numerator=0, int denominator=1);//非explicit,允许隐式转换
    const Rational operator*(const Rational& rhs);  
    .......................
};

result=oneHalf*2;//正确,相当于oneHalf.operator*(2);
result=2*oneHalf;//错误,相当于2.operator*(oneHalf);
我们可以看到result=2*oneHalf;这句无法通过编译,因为编译器找不多这样一个函数。那为什么result=oneHalf*2;这句话能通过编译?这是因为此时的类Rational可以进行隐式转换。

为了能执行result=2*oneHalf;这句,我们可以定义一个非member函数,如下
const Rational operator*(const Rational& lhs, const Rational& rhs);
result=oneHalf*2;//正确,相当于operator*(oneHalf,Rational temp(2));
result=2*oneHalf;//正确,相当于operator*(Rational temp(2),oneHalf);
25. 考虑写出一个不抛出异常的swap函数

你可能感兴趣的:(C++相关书籍)