【M33】将非尾端类设计为抽象类
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、结论,对于继承体系中的非尾端类,应该设计为抽象类,如果使用外界的程序库,需要做一下变通。
【M33】将非尾端类设计为抽象类的更多相关文章
- Java集合为什么设计为:实现类继承了抽象类,同时实现抽象类实现的接口
更好阅读体验:Java集合为什么设计为:实现类继承了抽象类,同时实现抽象类实现的接口 问题 Java集合源码为什么设计为:「实现类继承了抽象类,同时实现抽象类实现的接口?」 看着List 集合的UML ...
- python基础----继承与派生、组合、接口与归一化设计、抽象类、子类中调用父类方法
一.什么是继承 继承是一种创建新的类的方式,在pyth ...
- python基础之类的继承与派生、组合、接口与归一化设计、抽象类、子类中调用父类方法
一.什么是继承 继承是一种创建新的类的方式,新建的类可以继承自一个或者多个父类,原始类称为基类或超类,新建的类称为派生类或子类. 派生:子类继承了父类的属性,然后衍生出自己新的属性,如果子类衍生出的新 ...
- Java11-java基础语法(十)类设计综合案例
Java11-java语法基础(十)类设计综合案例 一.类综合设计方法 1.类设计步骤 (1)分析数据成员 (2)分析成员方法和构造方法 (3)画出类图 (4)编码测试 2.具体问题 1)分析数据成员 ...
- iOS控制器之基类设计
题记 在进入新公司后.经过这一个月的重构项目,终于把项目做到了个人相对满意的程度(还有一种不满意的叫老板的需求,提过多次意见也没用= =!).在这次重构中按照以前的思路设计出了个人觉得比较适用的一个基 ...
- 水果项目第1集-想法>需求->功能->数据库设计->类设计
懒,懒人,我是个懒人. 懒人想做点事,总是拖拖拉拉,迟迟没有开始. 很久很久以前,就想做属于自己的产品,但是至今还没有一个属于自己的产品. 两年前,终于想好,要做一个网上卖水果的系统,手机上点点,水果 ...
- 非Controller类无法使用Service bean解决方案
尝试方案: 1 在Spring的配置文件springmvc.xml中,增加扫描项base-package="zxs.ssm.util",增加你需要使用service的类所在的包 ...
- [theWord] 一种英文字典的基类设计
theWord --- 一种英文字典的基类设计 使用场景 想写一个应用,来记录自己背单词时候,对每个单词的记忆状况之类的东西.至于为什么做这个,试过了一些背单词软件,并不觉得好用,自己做一个吧. 那么 ...
- c++特性:指向类成员的指针和非类型类模板参数和函数指针返回值 参数推导机制和关联型别
一.c++允许定义指向类成员的指针,包括类函数成员指针和类数据成员指针 格式如下: class A { public: void func(){printf("This is a funct ...
随机推荐
- N元数组的子数组之和的最大值
题目:有N个整数的元素的一维数组,求子数组中元素之和中最大的一组(思想:动态规划) 分析: 设该数组为array[N], 那么对于array[i]该不该在元素之和最大的那个子数组中呢?首先,不如假设a ...
- PHP开发规范
十.开发规范下面我们讲解 Yii 编程中推荐的开发规范.为简单起见,我们假设 WebRoot 是 Yii 应用安装的目录.1.URL默认情况下,Yii 识别如下格式的 URL: http://host ...
- nginx upstream的几种配备方式
nginx upstream的几种配置方式 nginx 的upstream目前支持4种方式的分配 1.轮询(默认) 每个请求按时间顺序逐一分配到不同的后端服务器 ,如果后端服务器down掉,能自动剔 ...
- 字符串和数组中split().toString(),join(),splice(),slice(),substr()和substring()
<!Doctype html> <head> <mate charset="utf-8"> <title>string change ...
- with 语句
with 语句用于设置代码在特定对象中的作用域. 它的语法: with (expression) statement例如: var sMessage = "hello"; with ...
- TopFreeTheme精选免费模板【20130827】
今天我们整理了一些关于WordPress, Joomla, Drupal, Magento, OpenCart的最新免费模板主题,绝大多数都是来自ThemeForest的响应式风格模板主题.题材多样化 ...
- Flex3.0 Loader类的练习
<?xml version="1.0" encoding="utf-8"?> <mx:Application xmlns:mx="h ...
- Ubuntu安装PostgreSQl
warrior@pc:~$ sudo apt-get install postgresql-xx-xx #可以使用Tab键进行代码补全 warrior@pc:~$ sudo su postgres # ...
- Java基础 —— 面向对象
面向对象的程序设计: 1. 基本特征:抽象性,封装性,继承性,多态性. 2. 类及成员的访问控制:private:同一类中: default:同一包中: protected:子类中: public:全 ...
- 阿里云开放服务oss的api
http://imgs-storage.cdn.aliyuncs.com/help/oss/OSS_API_20131015.pdf?spm=5176.383663.5.23.JQjiIK&f ...