Effective C++学习笔记-01

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应当提供一个普通函数(而非在析构函数中)处理。
相关文章
相关标签/搜索