博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
【M33】将非尾端类设计为抽象类
阅读量:6242 次
发布时间:2019-06-22

本文共 1668 字,大约阅读时间需要 5 分钟。

1、考虑下面的需求,软件处理动物,Cat与Dog需要特殊处理,因此,设计Cat和Dog继承Animal。Animal有copy赋值(不是虚方法),Cat和Dog也有copy赋值。考虑下面的情况:

Cat cat1;
Cat cat2;
Animal *a1 = &cat1;
Animal *a2 = &cat2;
*a1 = *a2;
思考*a1 = *a2会有什么问题? copy赋值不是虚方法,根据表面类型,调用Animal的copy赋值,这就导致所谓的部分赋值,cat2的Animal成分赋值给cat1的Animal成分,二者的Cat成分保持不变。

2、怎么解决上面的问题?

将Animal的copy赋值声明为virtual方法,如下:
virtual Animal& operator=(const Animal &rhs);
Cat和Dog重写:
virtual Cat& operator=(const Animal &rhs);
virtual Dog& operator=(const Animal &rhs);
这里使用了C++语言后期的一个特性,即协变,返回的引用更加具体。但是,对于形参表,重写必须保证保持一致。将copy赋值声明为virtual,解决了部分赋值的问题。但是,引入了一个新的问题。如下:
Cat cat;
Dog dog;
Animal* a1 = &cat;
Animal* a2 = &dog;
*a1 = *a2;
这是异型赋值,左边是Cat,右边是Dog。C++是强类型语言,一般情况下,异型赋值不合法,不会造成问题。但是,这种情况下导致异型赋值合法。对于指针解引用的情况,我们期望同型赋值是合法的,异型赋值是非法的。容易想到的办法是,在重写的copy赋值中,使用dynamic_cast进行同型判断。比如Cat的copy赋值,首先判断rhs是不是Cat,如果是,就赋值,如果不是,抛出异常。
我们知道,使用dynamic_cast效率低,考虑下面的情况,cat1 = cat2; 即使cat1与cat2的表面类型就是Cat,也会调用Cat& operator=(const Animal &rhs),进行一次dynamic_cast的运算,这不是我们所期望的。解决办法是:增加一个过载方法,编译器编译时,根据表面类型确定方法的调用。如下:Cat& operator=(const Cat &rhs)。同时对于重写的方法,可以调用前面的方法,如下:
Cat& operator=(const Animal &rhs)
{
return operator=(dynamic_cast<Cat&>(rhs));
}

3、运行期的类型检查,dynamic_cast的使用应该尽量避免。因为,首先效率低,其次,有些编译器还不支持dynamic_cast,不具有移植性。有没有更好的办法?

导致问题的原因是,对于指针解引用的赋值,父类的copy赋值不是虚方法,导致部分赋值。
因此,解决办法是,提取一个抽象类AbstractAnimal,将copy赋值声明为protected,子类可以调用,表面类型是抽象类的指针解引用赋值,不能调用。增加一个Animal类,继承AbstractAnimal。
对于抽象类,内部至少要有一个纯虚方法,很自然地将析构方法声明为纯虚方法。对于纯虚方法,需要注意:
a、纯虚方法意味着当前类为抽象类,不能实例化。
b、纯虚方法要求子类必须重写。
c、特别注意,纯虚方法一般不提供实现,但是允许提供实现,子类也可以调用。如果析构方法为纯虚方法,必须要提供实现。因为子类调用自身的析构方法后,必定会去调用父类的析构方法。

4、考虑,具体基类没有字段,是不是就不需要上述的抽象类了?这有两个问题,首先现在没有字段,以后可能会有字段,其次如果一个类没有字段,一开始就应该是一个抽象类。

5、结论,对于继承体系中的非尾端类,应该设计为抽象类,如果使用外界的程序库,需要做一下变通。

转载地址:http://kbpia.baihongyu.com/

你可能感兴趣的文章
函数适配器bind2nd 、mem_fun_ref 源码分析、函数适配器应用举例
查看>>
武汉科技大学ACM :1002: A+B for Input-Output Practice (II)
查看>>
extjs中form.reset(true)出现的bug修复
查看>>
Some Android functions
查看>>
ORB-SLAM2学习4 initializer.h
查看>>
正向代理和反向代理
查看>>
1092 回文字符串(LCSL_DP)
查看>>
day01-Python介绍,安装,idea
查看>>
AX函数,将EXCEL列号转为列名
查看>>
UNDO -- Concept
查看>>
养生《一》
查看>>
es6的模块化--AMD/CMD/commonJS/ES6
查看>>
DevStack部署Openstack环境
查看>>
新年最新的100句超牛的语言(转)
查看>>
Chromium Graphics: Graphics and Skia
查看>>
asp.net core mvc上传大文件解决方案
查看>>
二叉树
查看>>
十分简单的抛物线运动
查看>>
乘法逆元(转)
查看>>
android repo库的创建及代码管理
查看>>