More Effective C++ 条款33 将非尾端(non-leaf classes)设计为抽象类(abstract

  1. 假如程序有一个Chicken class,Lizard class,Animal class,其中Chicken class和Lizard class继承自Animal class,整个继承体系像这样:

More Effective C++ 条款33 将非尾端(non-leaf classes)设计为抽象类(abstract

Animal负责具体化所有东吴的共同特征,Lizard和Chicken是需要特殊对待的两种动物,它们的简化定义像这样:
More Effective C++ 条款33 将非尾端(non-leaf classes)设计为抽象类(abstractMore Effective C++ 条款33 将非尾端(non-leaf classes)设计为抽象类(abstract

class Animal {
public:
    Animal& operator=(const Animal& rhs);
    ...
};
class Lizard: public Animal {
public:
    Lizard& operator=(const Lizard& rhs);
    ...
};
class Chicken: public Animal {
public:
    Chicken& operator=(const Chicken& rhs);
    ...
};

View Code
那么也就允许这样的代码:
More Effective C++ 条款33 将非尾端(non-leaf classes)设计为抽象类(abstractMore Effective C++ 条款33 将非尾端(non-leaf classes)设计为抽象类(abstract

Animal* pAnimal1;
Animal* pAnimal2;
...
*pAnimal1=*pAnimal2;

View Code
由于Animal具有多态性,也就是说它实际所指向的类型是不确定的,因此pAnimal1和*pAnimal2的动态类型不同而导致operator=被错用的情况也可能出现,类的设计者有责任对这种赋值行为进行检查或避免"异型赋值"的出现.

最直接的方法是将operator=设为virtual,像这样:
More Effective C++ 条款33 将非尾端(non-leaf classes)设计为抽象类(abstractMore Effective C++ 条款33 将非尾端(non-leaf classes)设计为抽象类(abstract

class Animal {
public:
    virtual Animal& operator=(const Animal& rhs);
    ...
};
class Lizard: public Animal {
public:
    virtual Lizard& operator=(const Animal& rhs);
    ...
};
class Chicken: public Animal {
public:
    virtual Chicken& operator=(const Animal& rhs);
    ...
};

View Code
C++要求虚函数原型相同,尽管允许函数返回引用时可以返回派生类引用,但函数参数仍然必须完全相同.这就要求在Lizard和Chicken的operator=内部使用dynamic_cast,Lizard的operator=的定义像这样:
More Effective C++ 条款33 将非尾端(non-leaf classes)设计为抽象类(abstractMore Effective C++ 条款33 将非尾端(non-leaf classes)设计为抽象类(abstract

Lizard& Lizard::operator=(const Animal& rhs)
{
    //将rhs转为const Lizard&,失败则抛出异常
    const Lizard& rhs_liz = dynamic_cast<const Lizard&>(rhs);
    proceed with a normal assignment of rhs_liz to *this;
}

View Code
由于同型赋值实际上不需要dynamic_cast,因此opeator=的实现可以升级成这样:
More Effective C++ 条款33 将非尾端(non-leaf classes)设计为抽象类(abstractMore Effective C++ 条款33 将非尾端(non-leaf classes)设计为抽象类(abstract

class Lizard: public Animal {
public:
    virtual Lizard& operator=(const Animal& rhs);
    Lizard& operator=(const Lizard& rhs); //新增的non-virtual函数
    ...
};
Lizard& Lizard::operator=(const Animal& rhs)
{
    return operator=(dynamic_cast<const Lizard&>(rhs));
}

View Code
当使用dynamic_cast有其固有的缺点:dynamic_cast可能抛出异常,也就是说编译器会产生额外代码用于"待命捕捉bad_cast exceptions,并作某些合理应对",因此会产生一定的效率损失;此外错误只有在运行期才能检查出来.因此应该考虑其他策略.

阻止类似于pAnimal1=pAnimal2的行为的最直接办法就是将Animal的operator=设为private,但这样一来Animal的自身赋值也不允许,而且Lizard和Chicken的operator=也无法实现,因为它们默认调用Animal的operator=,即使将Animal的operator=设为protected,前一个问题仍然存在.最简单的办法就是消除Animal对象相互赋值的需要,也就是将Animal设为抽象基类,但又由于Animal可能必须作为一个具体类,因此另一个策略就是使Lizard和Chicken不再继承自Animal,并使得Lizard,Chicken,Animal继承自一个更痛的抽象基类AbstactAnimal,整个继承体系像这样:

More Effective C++ 条款33 将非尾端(non-leaf classes)设计为抽象类(abstract

AbstractAnimal,Lizard,Animal,Chicken的定义像这样:
More Effective C++ 条款33 将非尾端(non-leaf classes)设计为抽象类(abstractMore Effective C++ 条款33 将非尾端(non-leaf classes)设计为抽象类(abstract

class AbstractAnimal {
protected:
    AbstractAnimal& operator=(const AbstractAnimal& rhs);
public:
    virtual ~AbstractAnimal() = 0; // 虽然AbstractAnimal的析构函数被设为pure virtual,但仍然需要提供定义
    ...
};
class Animal: public AbstractAnimal {
public:
    Animal& operator=(const Animal& rhs);
    ...
};
class Lizard: public AbstractAnimal {
public:
    Lizard& operator=(const Lizard& rhs);
    ...
}
class Chicken: public AbstractAnimal {
public:
    Chicken& operator=(const Chicken& rhs);
    ...
};

View Code
采用这种策略直接禁止了像pAbstractAnimal1=pAbstractAnimal2的操作,而仍然像pAnimal1=pAnimal2的操作并且在编译时检查类型.

  1. 1的分析过程实际上体现了这样一种思想:当具体类被当做基类使用时,应该将具体类转变为抽象基类.

然而有时候需要使用第三方库,并继承其中一个具体类,由于无法修改该库,也就无法将该具体类转为抽象基类,这是就需要采取其他选择:

1). 继承自现有的具体类,但要注意1所提出的assignment问题,并小心条款3所提出的数组陷阱.

2). 试着在继承体系中找一个更高层的抽象类,然后继承它.

3). 以"所希望继承的那么程序库类"来实现新类.例如使用复合或private继承并提供相应接口.此策略不具灵活性.

4). 为"所希望继承的那么程序库类"定义一些non-member,不再定义新类.

原文链接: https://www.cnblogs.com/reasno/p/4876064.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/223006

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年2月13日 上午11:56
下一篇 2023年2月13日 上午11:56

相关推荐