Effective C++

【Effective C++(6)】继承与面向对象设计

2018-01-05  本文已影响4人  downdemo

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

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

详见此文中的访问控制与继承部分

class A {
private:
    int x;
public:
    virtual void f1() = 0;
    virtual void f1(int);
    virtual void f2();
    void f3();
    void f3(double);
    ...
};
class B : public A {
public:
    using A::f1; // 让基类中名为f1和f3的所有东西在派生类中可见
    using A::f3;
    virtual void f1();
    void f3();
    void f4();
    ...
};
B b;
int x;
b.f1(); // B::f1
b.f1(x); // A::f1,如果没有using就会出错,因为被B::f1覆盖了找不到
b.f2(); // A::f2
b.f3(); // B::f3
b.f3(x); // A::f3,如果没有using就会出错,因为被B::f3覆盖了

// 如果只希望继承一部分,比如继承A::f1()而非A::f1(int),using声明式就不管用了
// 这时需要用到转交函数
class A {
public:
    virtual void f1() = 0;
    virtual void f(int);
    ...
};
class B : public A {
public:
    virtual void f1() // 转交函数
    { A::f1(); } // 隐式inline
    ...
};
B b;
int x;
b.f1(); // A::f1()
b.f1(x); // 错误,A::f1()被覆盖了

34 区分接口继承和实现继承

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

class GameCharacter {
public:
    virtual int healthValue() const;
    ...
};
class GameCharacter {
public:
    int healthValue() const // virtual函数的wrapper(外覆器)
    {
        ... // 事前工作
        int retVal = doHealthValue(); // 真正的工作
        ... // 事后工作
        return retVal;
    }
    ...
private:
    virtual int doHealthValue() const
    {
        ...
    }
};  
class GameCharacter; // 前置声明
int defaultHealthCalc(const GameCharacter& gc); // 缺省算法

class GameCharacter {
public:
    typedef int (*HealthCalcFunc)(const GameCharacter&);
    explicit GameCharacter(HealthCalcFunc hcf = defaultHealthCalc)
    : healthFunc(hcf)
    {}
    int healthValue() const
    { return healthFunc(*this); }
    ...
private:
    HealthCalcFunc healthFunc;
};
class EvilBadGuy : public GameCharacter {
public:
    explicit EvilBadGuy(HealthCalcFunc hcf = defaultHealthCalc)
    : GameChacter(hcf)
    { ... }
};

// 两个不同的计算函数
int loseHealthQuickly(const GameCharacter&);
int loseHealthSlowly(const GameCharacter&);

// 相同类型人物搭配不同计算方式
EvilBadGuy ebg1(loseHealthQuickly);
EvilBadGuy ebg2(loseHealthSlowly);

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

class Base {
public:
    virtual int f();
};
chass D1 : public Base {
public:
    int f(int); // 隐藏了基类的f,此f(int)不是虚函数
    virtual void f2(); // 新的虚函数
};
class D2 : public D1 {
public:
    int f(int); // 非虚函数,隐藏了D1::f(int)
    int f(); // 覆盖Base的虚函数f()
    void f2(); // 覆盖D1的虚函数f2
};

Base b;
D1 d1;
D2 d2;

Base* bp1 = &b;
Base* bp2 = &d1;
Base* bp3 = &d2;
// 编译器在运行时确定虚函数版本,判断依据是该指针绑定对象的真实类型
bp1->f(); // 运行时调用Base::f()
bp2->f(); // 运行时本来调用D1::f(),但D1没有这个虚函数,往上调用Base::f()
bp2->f2(); // 错误,Base没有f2()
bp3->f(); // 运行时调用D2::f()

D1* d1p = &d1;
D2* d2p = &d2;
d1p->f2(); // 运行时调用D1::f2()
d2p->f2(); // 运行时调用D2::f2()

// 再看看对非虚函数f(int)的调用
Base* p1 = &d2;
D1* p2 = &d2;
D2* p3 = &d2;
p1->f(42); // 错误,Base没有f(int)
p2->f(42); // 静态绑定,调用D1::f(int)
p3->f(42); // 静态绑定,调用D2::f(int)

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

// widget.h
class widget : public QMainWindow
{
    Q_OBJECT

public:
    explicit widget(QWidget* parent = 0);
    ~widget();
private:
    Ui::Form* ui;
}

// widget.cpp
widget::widget(QWidget* parent) // 此处不能再指定默认实参
: QMainWindow(parent), ui(new Ui::Form)
{
    ui->setupUi(this);
}
class A {
public:
    virtual void f(int i = 42)
    {
        cout << "A:" << i << endl;
    }
};

class B : public A {
public:
    virtual void f(int i = 55)
    {
        cout << "B:" << i << endl;
    }
};

int main()
{
    A* b = new B;
    b->f(); // B:42
}
class A {
public:
    void g(int i = 42) { f(i); }
private:
    virtual void f(int i) = 0;
};

class B : public A {
private:
    virtual void f(int i) // 传入了来自A::g的实参,即使指定默认实参也会被忽略
    {
        cout << "B:" << i << endl;
    }
};

int main()
{
    A* b = new B;
    b->g(); // B:42
}

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

class Address { ... };
class PhoneNumber { ... };
class Person {
public:
    ...
private:
    std::string name;
    Address address;
    PhoneNumber voiceNumber;
    PhoneNumber faxNumber;
};
template<typename T>
class Set : public std::list<T> { ... };
template<calss T>
class Set {
public:
    bool member(const T& item) const;
    void insert(const T& item);
    void remove(const T& item);
    std::size_t size() const;
private:
    std::list<T> rep;
};
template<typename T>
bool Set<T>::member(const T& item) const
{
    return std::find(rep.begin(). rep.end(), item) != rep.end();
}
template<typename T>
void Set<T>::insert(const T& item)
{
    if (!member(item)) rep.push_back(item);
}
template<typename T>
void Set<T>::remove(const T& item)
{
    typename std::list<T>::iterator it = 
        std::find(rep.begin(), rep.end(), item);
    if (it != rep.end()) rep.erase(it);
}
template<typename T>
std::size_t Set<T>::size() const
{
    return rep.size();
}

39 明智而审慎地使用private继承

// class Student : private Person
void eat(const Person& p);
Person p;
Student s;
eat(p); // OK,人会吃东西
eat(s); // 错误,学生不会吃东西?

40 明智而审慎地使用多重继承

class A {
public:
    void f();
    ...
};
class B {
public:
    bool f() const;
    ...
};
class C : public A, public B
{ ... };
C c;
c.f(); // 调用哪个f
c.A::f();
class A { ... };
class B : virtual public A { ... };
class C : virtual public A { ... };
class D : public B, public C
{ ... };
上一篇下一篇

猜你喜欢

热点阅读