《Effective C++》继承与面对对象设计:条款32-条款40

条款32:确定你的public继承塑模出is-a关系

  • public继承意味着is-a。适用于base class身上的每一个函数也一定适用于derived class。

条款33:避免遮掩继承而来的名称

几层作用域:

  • global作用域
    • namespace作用域
      • Base class作用域
        • Drive class作用域
          • 成员函数
            • 控制块作用域

当前作用域会遮掩上一层作用域的名称(重载的函数也会直接遮掩)

class Base{
public:
    void f1();
}

class Drive : public Base {
public:
    void f1(int);  //会遮掩f1(),子类并没有继承f1()
}

Drive d;
d.f1();  //错误
d.f1(3); //正确

可以通过using声明式(public继承)或者inline转交(public和private继承)解决这一问题

class Base{
public:
    void f1();
}

//using 声明式
class Drive:public Base {
public:
    using Base::f1;
    void f1(int);
}

//inline转交
class Drive : private Base {
public:
    void f1(){
        Base::f1();
    }
    void f1(int);
}

条款34:区分接口继承和实现继承

  • 纯虚函数(只提供接口)

    • 纯虚函数造成了抽象类,抽象类不可以构造实体(对象)
    • Drived class中必须给出纯虚函数的实现
    • 纯虚函数可以给出实现(类外)
  • 虚函数(提供接口和缺省实现)

    • 用于实现多态
  • 非虚函数(提供了强制实现)

    • 可以看成此类独有,且最好不要在Drived class重新定义非虚函数

条款35:考虑virtual函数以外的其他选择

virtual的替代方案

  • non-virtual interface(NVI)手法,以public non-virtual成员函数包裹较低访问性(privat或protected)的virtual函数
#include <iostream>
#include <string>
#include <memory>

using namespace std;

class base {
public:
    void show() {
        doshow();
    }

private:
    virtual void doshow() const {
        cout << "base" << endl;
    }
};


class drive : public base {
public:

private:
    virtual void doshow() const {
        cout << "drive" << endl;
    }

};

int main() {
   shared_ptr<base> d = shared_ptr<base>(new drive);
   d->show();
}
  • 以函数指针变量替换virtual函数
#include <iostream>
#include <string>
#include <memory>

using namespace std;

class base {
    typedef void (*showFuc) (const base&);
public:
    explicit base(showFuc _s) : s(_s){}
    void doshow() const {
        cout << "base" << endl;
    }
    int show() const {
        s(*this);
    }

private:
    showFuc s;
};


void showFuc1(const base& b) {
    cout << "function 1: ";
    b.doshow();
}

void showFuc2(const base& b) {
    cout << "function 2: ";
    b.doshow();
}

int main() {
    shared_ptr<base> b1 = shared_ptr<base>(new base(showFuc1));
    shared_ptr<base> b2 = shared_ptr<base>(new base(showFuc2));
    b1->show();
    b2->show();
}
  • 利用std::function成员变量替换virtual函数(可将函数写在类中)
#include <iostream>
#include <string>
#include <memory>

using namespace std;

class base1 {
public:
    explicit base1(function<void(const base1&)> _f) : f(_f){}

    void doshow() const {
        cout << "base1" << endl;
    }

    void show() const {
        f(*this);
    }

private:
    function<void(const base1&)> f;
};

class base2 {
public:
    void baseFuc(const base1& b) {
        b.doshow();
    }
};

int main() {
    shared_ptr<base2> b2;
    shared_ptr<base1> b1 = shared_ptr<base1>(new base1(bind(&base2::baseFuc, b2, placeholders::_1)));
    b1->show();
}
  • 古典的Strategy设计模式(将virtual函数替换为另一个继承体系内的virtual函数)
#include <iostream>
#include <string>
#include <memory>

using namespace std;

class base2;

class base1 {
public:
    base1(const shared_ptr<base2>& _b) : b(_b) {}

    void doshow() const;
    void show() const ;
private:
    shared_ptr<base2> b;
};

class base2 {
public:
    virtual void baseFuc(const base1& b) {
        b.doshow();
    }
};

void base1::doshow() const {
    cout << "base1" << endl;
}

void base1::show() const {
    b->baseFuc(*this);
}

int main() {
    shared_ptr<base2> b2 = shared_ptr<base2>(new base2());
    shared_ptr<base1> b1 = shared_ptr<base1>(new base1(b2));
    b1->show();
}

条款36:绝不重新定义继承而来的non-virtual函数

条款37:绝不要重新定义继承而来的缺省参数值

  • 缺省参数值是静态绑定
  • 虚函数是动态绑定
class Base{ 
public:
    virtual void print(int a = 1) {cout <<"Base "<< a <<endl;};
    int a;
};

class Drive : public Base{
public:
    void print(int a = 2){cout << "Drive " << a <<endl;}
};                                                                                 
                                                                                   
int main(){                                                                        
  Base *b = new Drive;                                                             
  b->print();   //   vptr[0](1)
}

//Drive 1

条款38:通过复合塑模树has-a 或“根据某物实现出”

  • 复合(composition)
    • has-a(应用域对象间复合)
      • 应用域(程序中塑造的某些事物,如人、汽车、视频画面等等)
    • is-implemented-in-terms-of(实现域对象间复合)
      • 实现域(实现细节的人工制品,如缓冲区、互斥器、查找树等等)

条款39:明智而审慎的使用private继承

  • private继承(不继承接口,但继承实现)
    • 编译器不会自动将一个derived class对象转换为base class对象
    • base class的所有成员在derived class中都变成private属性
    • private继承可以造成empty class最优化(这对致力于"对象尺寸最小化"的程序开发者而言,可能很重要)
class Base{
public:
    void fun(){}
}

//8个字节
class Object{
private:
    int a;
    Base b;
};

//4个字节
class Object : private Base{
private:
    int a;
}

条款40:明智而审慎的使用多重继承

  • 多重继承可能导致歧义(菱形继承)
class A { ... };
class B: public A { ... };
class C: public A { ... };
class D: public B, public C { ... };
//D中会包含两份A的成员变量
  • 采用virtual继承解决歧义
    • virtual继承会增加大小、速度、初始化及赋值等等成本
    • 如果base class不带有任何数据,virtual继承比较有实用价值
class A { ... };
class B: virtual public A { ... };
class C: virtual public A { ... };
class D: public B, public C { ... };
//D中只包含一份A的成员变量
  • 有一些情况可采用 "public继承抽象class(继承接口)" 和 "private继承继承协助实现class(继承实现)" 两相结合的方法
posted @ 2019-01-02 16:17  narjaja  阅读(201)  评论(0编辑  收藏  举报