一些要注意的地方

时间:2021-05-23 19:02:40

1.尽量以const,enum,inline替换define。

 

2.旧式的编译器可能不允许static在其申明式上获得初值。此外所谓的‘in-class’初值设定也许只针对整数常量进行,要是你的编译器不支持上述语法,可以将初值放在定义式。要是数组无法初始化时可以考虑使用类中的枚举值。

 

3.如果成员变量是const或者reference一定要用初值发初始化而不要使用赋值法。

 

4.构造函数最好使用成员初始化列表而不要在构造函数体内使用赋值操作,成员初始化列表列出的值应该与他们在class中的申明次序相同。

 

5.位避免跨编译单元之初始化次序问题,请以local static 对象代替non-local static对象。

 

6.任何class只有含有虚函数都几乎确定应该有个虚析构函数。

 

7.string,还有所有STL容器如vector、list、set等都不带虚析构函数。

 

8.析构函数绝对不要吐出异常,如果一个被析构函数调用的的函数可能抛出异常,析构函数应该能够捕获异常,并且吞下异常或者结束程序。

 

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

 

10.base class函数构造期间virtual函数绝不会下降到derived class阶层,对象作为就像隶属于base类型一样,非正式的和说法就是virtual函数不是virtual函数。而base class函数          早于derived class 的构造函数,base构造时derived的变量还未初始化。相同道理也适用于析构函数,一旦derived的析构函数执行,对象内的derived成员变量呈现未定义值,进入base的析构函数后就成为一个base class对象,而C++的任何部分包括virtual函数dynamic_casts也会那么看待它。一个优秀的做法就是把共同的实化代码放入一个init函数内(非virtual),这么做还是可能存在问题,最好不要在构造函数和析构函数中调用virtual函数。

 

11.令赋值操作符返回一个reference to *this  .
   在operator中处理自我赋值:低级方法--进行自我复制检查。高级方法--保存副本。

 

12.Copying函数应该确保赋值对象内的所有成员变量及base class的成分。不要尝试以一个Copying构造函数初始化另一个Copying构造函数,应将共同技能放在第三个函数中。

 

13.以独立语句将newed对象存储与智能指针内。如果不这样做,一旦异常抛出,有可能导致难以察觉的资源泄漏。

 

14.绝不要返回pointer或者reference指向一个local stack对象,或者返回前指向一个heap-allocate对象(可能没有人delete它)

 

15.类中的纯虚函数可以定义自己的实现,但这可以通过派生类对象或者指向派生类的指针显示调用
class A
{
public:
    virtual void fun1() = 0;
};
void A::fun1()
{
    cout << "void A::fun1()" << endl;
}

class B:public A
{
public:
    virtual void fun1()
    {
        cout << "void B::fun1()" << endl;
        A::fun1();//这样也可以
    }
};

int _tmain(int argc, _TCHAR* argv[])
{
    A *q = new A;
    q->fun1();    //error
    A *p = new B;
    p->fun1();
    p->A::fun1();

    system("pause");

    return 0;
}

 

16.derived class 内的名称会掩盖base class内的名称。通过基类指针指向派生类可以防止名称掩盖而无法正确调用,实际上是利用的多态。
class A
{
public:
    virtual void fun1() = 0;
    virtual void fun1(int n)
    {
        cout << "void A::fun1(int n)" << endl;
    }
    virtual void fun2()
    {
        cout << "void A::fun2()" << endl;
    }
    void fun3()
    {
        cout << "void A::fun3()" << endl;
    }
    void fun3(int n)
    {
        cout << "void A::fun3(int n)" << endl;
    }
};

class B:public A
{
public:
    virtual void fun1()
    {
        cout << "void B::fun1()" << endl;
    }
    void fun3()
    {
        cout << "void B::fun3()" << endl;
    }
    void fun4()
    {
        cout << "void B::fun4()" << endl;
    }
};

int _tmain(int argc, _TCHAR* argv[])
{
//     B b;
//     int n = 250;
//     b.fun1();
//     b.fun1(n);   //error,作用域被覆盖必须显示调用b.A::fun1(n)
//     b.fun2();
//     b.fun3();
//     b.fun3(n);   //error,作用域被覆盖必须显示调用b.A::fun3(n)

    //     B *p;
    //     int n = 250;
    //     p->fun1();
    //     p->fun1(n);   //error,作用域被覆盖必须显示调用p->A::fun1(n)
    //     p->fun2();
    //     p->fun3();
    //     p->fun3(n);   //error,作用域被覆盖必须显示调用p->A::fun3(n)

    //通过基类指针指向派生类可以防止名称掩盖而无法正确调用,实际上是利用的多态
    A *a = new B;
    int n = 250;
    a->fun1();
    a->fun1(n);
    a->fun2();
    a->fun3();
    a->fun3(n);

B *p = new B;
p->fun1();
p->fun1(5);   //error,派生类重写基类的同名称函数后会掩盖base class内的名称,无法调用基类的fun1(int n),如果没有重写的话可以调用基类的对象函数
p->fun2();
p->fun3();
p->fun3(6) ;//error,派生类重写基类的同名称函数后会掩盖base class内的名称,无法调用基类的fun1(int n),如果没有重写的话可以调用基类的对象函数
p->fun4();

    system("pause");

    return 0;
}

 

17. non-virtual函数A::fun3和B::fun3都是都是静态绑定的,意识就是q被申明为pointer-to-A,通过p调用的non-virtual函数永远是A所定义的版本,即使q指向的

一个类型为A的派生class对象。另一方面,virtual函数确实动态绑定,所以他们不会受此问题之苦,如果fun3是个virtual函数,不论p还是q调用fun3,都会导致调用

B::fun3,因为他们真正指的都是一个类型为B的对象。

接上面类A、B

B *p = new B;
p->fun3(); //调用的是B的fun3
A *q = new B;
q->fun3(); //调用的是A的fun3

 

18. 请绝对不要重新定义一个继承而来的non-virtual函数,如果需要这么做,一开始就应该考虑设计为一个virtual函数

 

19. 绝不要重新定义继承而来的缺省参数值,virtual函数是动态绑定,调用一个virtual函数时,究竟调用哪一份函数实现代码,取决于调用的那个对象的鼎泰类型,

     而缺省参数值确是静态绑定,静态绑定下函数并不从其base继承缺省函数值

 

20. 以by-reference方式传递参数可以避免对象切割问题当一个derived class以by-value方式传递并被视为一个base class 对象,base class 的copy构造函数

     会被调用,而造成此对象像个derived class对象的哪些特征化性质会被完全切割掉,仅仅留下一个base class 对象。 解决切割问题的办法就是以by-reference-const

     的方式传递参数,在C++编译器底层reference往往是以指针实现出来的,尽量以pass-by-reference-const替换pass-by-value,前者通常比较高效,并可避免切割问题

     不过对于内置类型以及STL的迭代器和函数对象还是用pass-by-value恰当

class A
{
public:
    A()
    {
        //cout << "A::A()" << endl;
    }
    A(const A &refs)
    {
        cout << "A::A(const A &refs)" << endl;
    }

};

class B:public A
{
public:
    B() :A()
    {
        //cout << "B::B()" << endl;
    }
    B(const B &refs) :A(refs)
    {
        cout << "B::B(const B &refs)" << endl;
    }
    void fun2()
    {
        cout << "B::fun2()" << endl;
    }
};

void fun(A a)
{
    //a.fun2();//error无法调用
}
int _tmain(int argc, _TCHAR* argv[])
{
    B obj;
    fun(obj);//此时会调用A的copy构造函数构造函数,而不会调用B的,不会生成B的对象
    system("pause");

    return 0;
}

 

 

21. 如果你需要为某个函数的所有参数进行类型转换(包括隐含的this参数),那么这个函数必须是non-member

 

22. 尽量延后变量定义的出现。这样做可以增加程序的清晰度和改善程序的效率。

 

23. 如果可以,尽量避免转型,特别是注重效率的代码中避免dynamic_cast。如果有个设计需要转型动作,试着发展无须转型的替代设计。如果转型是必要的,

试着将它隐藏域某个函数背后。客户随后调用这个函数,而不需要将转型放进他们自己的代码中。

 

24. 编译依存性最小化的本质:现实中让头文件尽可能自我满足,万一做不到,则将他与其他文件内的申明式(而非定义式)相依。

     如果使用object-reference或者object pointers可以完成任务,就不要使用objects。

     尽量以class申明式替换定义式。

     为申明式和定义式定义不同的文件。

 

25.避免exception传出destructor之外