• Effective C++ 3rd 读书笔记




    第一章:让自己习惯C++


    条款01. 视C++为一个语言联邦


    a. C++由四个主要次语言组成: C. Object-Oriented C++(类,封装,继承,多态,virtual函数等). Template C++.  STL(容器(containers),迭代器(iterators),算法(algorithms)以及函数对象(function objects))。

    b. 这四个次语言,当你从某个次语言切换到另一个,导致高效编程守则要求你改变策略。C++高效编程守则视状况而变化,取决于你使用C++的哪一部分
     


    条款02. 尽量以const,enum,inline替换#define


    a. #define当编译出错时,目标码出现多份,跟踪难度大,因为所使用的名称可能并未进入记号表(symbol table),处理方法:例:#define ASPECT_RATIO 1.653  --> (语言常量) const double AspectRatio = 1.653;

    b. 对于单纯常量,最好以const对象或enums替换#defines;定义常量指针时采用如下两种方法: const char* const authorName = ”yxstars“. const std::string authorName(”yxstars“)
    c. #define并不重视作用域,所以无法提供任何封装
    d. 对于class常量,某些编译器不允许“static整数型class常量”完成“in calss初值设定”,可以采用enum来实现。例:static const int NumTurns = 5 --> enum { NumTurns = 5 };
    e. 宏看起来像函数,但不会招致函数调用带来的额外开销,对于形似函数的宏,最好改用inline函数替换#defines。
    例:#define CALL_WITH_MAX(a,b)    f((a) > (b)) ? (a) : (b)) --> 
          template<typename T>
          inline void callWithMax(cosnt T &a, cosnt T &b)
          {
                f(a > b ? a : b);
          }



    条款03. 尽可能的使用const


    a. 将某些东西声明为const可帮助编译器侦测出错误用法。const可被施加于任何作用域内的对象、函数参数、函数返回类型、成员函数本体;

    b. 编译器强制实施bitwise constness,但你编写程序时应该使用“概念上的常量性”(conceptual constness);
    c. 当cosnt和non-const成员函数有着实质等价的实现时,令non-const版本调用const版本可避免代码重复。


    条款04. 确定对象使用前已先被初始化


    a. 为内置型对象进行手工初始化,因为C++不保证初始化它们;
    b. 构造函数最好使用成员初始化列表,而不要在构造函数本体内使用赋值操作。初始化列表列出的成员变量,其排列次序应该和它们在类中的声明次序相同;
    c. 为免除“跨编译单元之初始化次序”问题,以local static对象代替non-local static对象;

       fileSystem& tfs(){
        static fileSystem fs;
    	return fs;
       }





    第二章:构造/析构/赋值运算


    条款05. 了解C++默默编写并调用哪些函数


    a. 编译器可以暗自为类创建默认构造函数、拷贝构造函数、拷贝赋值操作符,以及析构函数。



    条款06:若不想使用编译器自动生成的函数,就该明确拒绝

      
    a. 由于编译器产生的函数都是public,因此可以将拷贝构造函数或拷贝赋值操作符声明为private。通过这个小“伎俩”可以阻止人们在外部调用它,但是类的成员函数和友元函数还是可以调用private。解决方法是在一个专门为了阻止拷贝动作而设计的基类,当调用它的时候,就会提示连接错误(Boost提供的那个类名为noncopyable)。


    b. 为驳回编译器自动(暗自)提供的机能,可将相应的成员函数声明为private并且不予实现。使用不实现的基类也是一种做法,如果你需要其他的类来拒绝默认函数,就直接继承这个基类就可以了.
    class Uncopyable(){
    protected:
     Uncopyable(){}
     ~Uncopyable(){}
    Private:
     Uncopyable(const Uncopyable&); //zhu zhi copying
     Uncopyable& operator=(const Uncopyable&);
    };
    
    
    class HomeYxstars: private Uncopyable{
    ......
    };


    条款07. 为多态基类声明virtual析构函数



    a. 带有多态性质的基类应该声明一个virtual析构函数。如果一个类带有任何virtual函数,它就应该拥有一个virtual析构函数。
    b. 一个类的设计目的不是作为基类使用,或不是为了具备多态性,就不该声明virtual析构函数。
    c. 最深层派生的那个class其析构函数最新被调用,然后是每一个base class的析构函数被调用。
    d. 标准的string和STL容器都不是被设计作为base class。
    e. 某些class的设计目的是作为base class使用,但并不是用作多态。例如Uncopyable和标准程序库input_iterator_tag,因此他们不需要vitual析构函数。


    条款08. 别让异常逃离析构函数



    a. 析构函数绝对不要吐出异常。如果一个被析构函数调用的函数可能抛出异常,析构函数应该捕捉任何异常,然后吞下它们(不传播)或结束程序。
       DBConn::DBConn(){
    try{db.close();}
    catch(...){
    std::abort(); //捕获异常,强迫结束程序或什么也不做
    }
    }

    b. 如果客户需要对某个操作函数运行期间抛出的异常做出反应,那么类应该提供一个普通函数(而非在析构函数中)执行该操作。   




    条款09. 决不让构造和析构过程中调用virtual函数


    a. 在构造和析构函数期间不要调用虚函数,因为这类调用从不下降至派生类。
    b. 基类的构造函数要早于派生类的构造函数运行,当基类的构造函数执行时,派生类的成员变量尚未初始化。指向虚函数表的指针vptr没被初始化不能去调用派生类的virtual函数。


    条款10. 令operator= 返回一个reference to *this

        
    a. 连续赋值:x = y = z = 15; 解析为:x = (y = (z = 15));为了实现“连锁赋值”,赋值操作符必须返回一个reference指向操作符的左侧实参。

    Widget & operator = (const Widget &rhs)
    {
    ...
    return *this;
    }


    b.这个协议适合于所有赋值相关运算。例如:+=,-=,*=。
    c.所有内置类型和标准程序库提供的类型如string,vector,complex或即将提供的类型共同遵守。
       


    条款11. 在operator =中处理“自我赋值”


    a. 确保当对象自我赋值时operator =有良好行为。其中技术包括比较“来源对象”和“目标对象”的地址、精心周到的语句顺序、以及copy-and-swap。

    b. 确定任何函数如果操作一个以上的对象,而其中多个对象是同一个对象时,其行为仍然正确。   
         Widget& Widget::operator=(const Widget& rhs) 
         {     
             delete pb;                     //这里对pb指向内存对象进行delete,试想 *this == rhs?,不安全
             pb = new Bitmap(*rhs.pb);   
             return *this; 
          }
         

    试同测试,但仍存在异常麻烦(“new BItmap可能因为内存分配不足或Bitmap的copy构造函数抛出异常)
       
      Widget& Widget::operator=(const Widget& rhs)
         { 
             if (this == &rhs) 
             return *this;            //试同测试
    
    
             delete pb; 
             pb = new Bitmap(*rhs.pb); 
             return *this; 
         }


     “许多时候一群精心安排的语句就可以导出异常安全(以及自我赋值安全)的代码。”,我们只需在复制pb所值东西之前别删除pb。
     
       Widget& Widget::operator=(const Widget& rhs)
         { 
             Bitmap *pOrig = pb;                //记住原先的pb
             pb = new Bitmap(*rhs.pb);      //令pb指向*pb的一个复本
             delete pOrig;                           //删除原先的pb
             return *this;  //这样既解决了自我赋值,又解决了异常安全问题。自我赋值,将pb所指对象换了个存储地址。
         }


    c.copy and swap技术
     class Widget(){
     ...
     void swap(Widget& rhs);
     ...
     }
     
     widget& widget::operator=(cosnt widget& rhs)
     {
    widget temp(rhs);
    swap(temp);
    return *this;
     }


    条款12:复制对象时勿忘其每一个成员


    a. 如果你在类中添加一个成员变量,你必须同时修改相应的copying函数(所有的构造函数,拷贝构造函数以及拷贝赋值操作符)。
    b. Copying函数应该确保复制“对象内的所有成员变量”及“所有基类成员”;
    c. 不要尝试以某个copying函数实现另一个copying函数。应该将共同机能放进第三个函数中,并由两个copying函数共同调用。 
    d. 另copy assignment操作符调用copy构造函数是不合理的,因为这就像试图构造一个已经存在的对象。反过来同样无意义。





    文/yanxin8原创,获取更多信息请移步至yanxin8.com...



    Meet so Meet. C plusplus I-PLUS....
  • 相关阅读:
    spring之aop概念和配置
    netty概念
    maven使用实例记录
    maven概念
    Runtime.getRuntime().addShutdownHook
    aop前传之代理
    实例化bean的三种方式
    easyui datagrid treegrid 取消行选中、取消高亮
    sqlserver 保存 立方米(m³)
    sqlserver 备份集中的数据库备份与现有的 'XXX' 数据库不同。
  • 原文地址:https://www.cnblogs.com/iplus/p/4467217.html
Copyright © 2020-2023  润新知