Effective C++ 笔记(二)

16、保证异常安全

1 void PrettyMenu::changBackground(std::istream &imgSrc)
2 {
3     lock(&mutex);
4     delete bgImage;
5     ++imageChanges;
6     bgImage = new Image(imgSrc);
7     unlock(&mutex);
8 }

上述代码中的问题,

a)如果 new Image 失败抛出异常会导致 mutex 没有释放。

b)如果 new Image 失败, bgImage 指向了一个已经被释放的内存。

c)如果 new Image 失败, imageChanges +1,这不符合逻辑。

异常安全函数提供三个保证:

a) 基本承诺:如果抛出异常,程序仍然保持在有效状态。没有任何对象或数据结构被破坏。

b)强烈保证:如果抛出异常,程序状态不变。(如果异常抛出,程序状态不改变,如果成功,完全成功,如果失败,完全失败。)

c)不抛掷保证:保证绝不抛异常,总是能完成承诺的功能。

17、inlining 的里里外外

inline 函数背后的整体观念是,将“对此函数调用”以函数本体替换之。这样做可能增加目标码的大小。在一台内存有限的机器上,过度热衷inlining, 会造成程序体积太大(对可用内存空间而言)。inline造成代码膨胀会造成额外的换页行为,降低指令高速缓存的击中率。

inline函数只是对编译器的一个申请,不是强制命令,申请可以是隐喻提出,也可以明确提出。隐喻提出是将函数定义在class定义式内。

构造函数和析构函数往往是inlining 的糟糕候选人。(往往比我们想象的做的更多。)

18、public继承 == is-a 关系

适用于base class 上的每一件事,一定也适用于derived class。因为每个derived class对象也都是一个base class。

19、避免遮掩继承而来的名称

#include <iostream>

using namespace std;

class Base
{
public:
    virtual void mf1() = 0;
    virtual void mf1(int){}
    virtual void mf2(){}
    void mf3(){}
    void mf3(double){}
};

class Derived : public Base
{
public:
//    using Base::mf1;
//    using Base::mf3;

    virtual void mf1(){}
    void mf3(){}
    void mf4(){}
};


int main()
{
    cout << "Hello World!" << endl;

    Derived d;
    d.mf1();
    d.mf1(5);
    d.mf3();
    d.mf3(2.5);

    return 0;
}

因为Derived实现了mf1(), 所以不在继承 mf1(int). 导致 d.mf1(5); 运行失败。

如果你在使用public继承,而又不继承那些重载函数,就违反了。 base 和 derived 之间的 is-a 关系。

在 Derived 中 声明 使用 using Base::mf1, using Base::mf3 可以解决上面的问题。

20、区分接口继承和实现继承

声明 pure virtual 函数目的是为了让Derived class 只继承函数接口。(pure virtual 可以提供函数定义。调用途径是调用时明确指定出class名称)

声明 impure virtual函数的目的,是让Derived class继承该函数接口和缺省实现。

non-virtual 函数具体指定接口继承以及强制性实现继承。

21、绝不重新定义继承而来的缺省参数值

virtual函数系动态绑定,而缺省参数值却是静态绑定。

#include <iostream>

using namespace std;

class Shape
{
public:
    virtual void draw(int a = 0) {
        cout << "a : " << a << endl;
    }
};

class Rectangle : public Shape
{
public:
    virtual void draw(int a = 1) {
        cout << "a : " << a << endl;
    }
};

class Cycle : public Shape
{
public:
    virtual void draw(int a = 2) {
        cout << "a : " << a << endl;
    }
};


int main(void)
{
    Shape *ps;
    Shape *pr = new Rectangle;
    Shape *pc = new Cycle;

    pr->draw();
    pc->draw();

    Rectangle rec;
    rec.draw();

    Cycle cyc;
    cyc.draw();

    return 0;
} 
View Code

22、复合意味着has-a

当两个类之间不适合is-a的关系,所以public继承不适合用来塑模,可以在class A中包含 class B。

23、明智而慎重的使用private继承

私有继承,编译器不会自动的将Derived对象转为Base对象。private继承而来的所有成员,在derived中都变成private。

private继承意味着,只继承部分实现,不继承接口。

如果D以private继承B,意思是D对象根据B对象实现而得,再没有其他含义。

在has-a 和 private继承之间,尽量选择has-a,只有当涉及私有protected成员或者virtual函数牵扯进来时。

 

posted on 2021-03-27 11:00  gaozy6626  阅读(26)  评论(0编辑  收藏  举报