Effective c++阅读笔记(不断更新……)

来源:互联网 发布:win7网络连接不可用 编辑:程序博客网 时间:2024/05/24 06:58

1. copy构造函数和copy赋值的使用区别。

    class A;   

   class B(A);        //调用copy构造函数

   B = A;              //调用copy赋值

   class B = A;   //调用copy构造函数

   如果一个对象被定义,一定会有个构造函数被调用,此时不可能调用赋值操作,如果没有对象定义,就不会调用构造函数,调用的自然是赋值操作。

 copy构造函数定义了如何进行对象的值传递。

2. 尽量以const 、enum、inline代替#define

#define是简单的替换,会导致多份数据的拷贝,而const仅有一份数据拷贝。

3. 类中static静态成员的初始化

方法一: class A {

                       private : static const int Num = 5;            //常量声明(非定义)

                                      int array[Num];         //使用该常量

                     }

           有些编译器可能坚持要看到一个定义式,否则会有编译错误,这时候需要在实现类的cpp文件中(非头文件)中对static成员进行定义,如下:

          const int A::Num;   //因为声明时已经赋予初值,因此此处定义不可以再设初值。如果声明时不设初值,则 array成员不能使用 Num,因为在编译class时,必须要知道array的大小,而此时Num并没有值。

  #define不可以定义这样的常量,因为define并不重视作用域,一旦宏被定义,就会一直有效,除非遇到undef。

方法二: 如果编译器(一般是旧式编译器)不支持static成员在声明时初始化,那可以在定义时初始化,如:

class A {

                       private : static const double  Num ;            //常量声明(非定义)

                             ...

                     }

         const   double  A::Num = 1.89  //同样放在实现文件cpp中,非头文件

方法三:在编译器不支持static成员在类中初始化时,要想在类中使用常量,比如数组大小, 可借助enum

                      class A{

                                 enum { Num = 5};

                               int  array [Num];

                          }

4. 正确使用const

const 出现在 * 左边,表示被指物是常量,出现在 * 右边,表示指针本身是常量, const int * p 与 int const * p是一样的。

const 迭代器 const  vector<T>::iterator it = v.begin()  ,相当于 T * const, 即常量指针,但指向的内容可以变

vector<T>::const_iterator  it = v.begin() 相当于 const T *,指向的内容是常量。

如果要在一个const的成员函数中修改非static成员变量的值,则应以mutable 来修饰要修改的非static成员变量。

5. 如何在non-const成员函数中调用const成员函数?

class  A{

   public :

         const char &   operator[] ( int position )  const  {

                   return text[positon];

           }

         char&  operator[] ( int position ) {

               return     const_cast<char&>( static_cast<const   A&>(*this)[position] );

          }

   .....

}

第二个非const成员函数调用了第一个const成员函数,进行了两次转型。第一次是static_cast  ,他主要是把non-const的(*this)转化为const  A&,这样才能调用到const成员函数。第二个是把返回的const char& 转化成non-const char &。static_cast 类型与C语言中的强制类型转换,如int  a = 10;   double  d = (double) a,; double d = static_cast<double> a.

6. 鼓励在构造函数中使用成员初值列进行成员的初始化,这样比赋值高效。

class A {

     private:

           int  a;

           string  str;

     public:

            A(int d, string  s)  {

                     a = d;

                     str = s;

            }          //这是赋值,并非初始化,先调用string 的default构造函数构造str, 再调用copy构造函数,效率较低

            A (int d, string s) : a(d), str( s) {   };  //这是初始化,直接调用copy构造函数,较高效

}

7. 虚析构函数的使用

在class的继承体系中,如果基类的析构函数为非virtual的,则在释放派生类实例的时候,只能调用基类的析构函数,而派生类本身的没有被调用,造成局部销毁的结果,这这结果是灾难性的。但如果某个类不希望作为基类,则不应该声明virtual析构函数,因为这会增加类的体积,原因是这种类的对象除了类成员变量的空间外,还有些辅助信息用来运行时期确定哪一个virtual函数被调用,通常由一个vptr(virtual table pointer)指针指出,vptr指向一个函数指针数组,称为vtbl(virtual table),每一个带有virtual函数的class都会有一个相应的vbtl。

STL中的vector、list、set、map、string等class都不具有virtual 析构函数,所以最好不要用作基类。

带多态性质的基类应该声明一个virtual析构函数,如果class带有任何virtual函数,他就应该有一个virtual析构函数;如果class设计的目的不是作为基类,或不是为了具备多态性,就不该声明virtual析构函数。

 8. 不要让异常逃离析构函数

如果一个析构函数可能会抛出异常,要用try。。catch捕捉,防止异常传播,产生程序过早结束或者不明确的行为。如果程序员自己希望处理这种异常,则需要单独写一个成员函数捕捉异常,在析构函数中无法处理。

 

9. 将文件间的编译依存关系降至最低:

如果一个class中需要引用到别的class或者struct,不要include包含这些class或struct的头文件,而应在定义的class前直接声明这些class和struct,如:

class Date;

class Address;

class Persion

{

    private :

            Date date;

            Address  add;

....

};

 

10: private 的virtual函数允许子类重定义,但是不能调用,如果要调用,一个方法是在基类写个非virtual函数,里面直接调用private的virtual函数,这样调用非virtual的函数时

动态确定里面调用的是那个子类的virtual函数。

11. 在private继承中,子类不能转换为基类,即不能让一个声明为基类的指针指向某一个子类!因为private继承并不是is-a关系

原创粉丝点击