什么情况下应用纯虚类

前几天跟同事brainstorm,讨论一个关于纯虚类的使用问题,挺有意思。回来心中久久不能平静,写出来一吐为快。

不论在C++中还是C#中,纯虚类都是不能实例化的,这是因为纯虚类其实是一个对业务类型的一种高度抽象,本质上是不存在这种东西的,所以也就不能实例化它。对于C++中只要类中含有一个纯虚函数就是纯虚类,而C#中是abstract修饰的类就是纯虚类,即使类中没有虚方法也可以是纯虚类,在这里我觉得C#的纯虚类没有C++的严谨,如果纯虚类中没有纯虚方法的话,那有何意义。

明白了纯虚类的原理,那纯虚类应该肯定需要被别的类继承的,因为如果不继承的话,它自己本身也不能实例化,就没有存在的意义了,所以它肯定是需要被继承的。那它需不需要含有一个或者多个纯虚方法呢?答案是肯定的,因为既然纯虚类是需要被继承的,那没有纯虚方法又有何意义。没有纯虚方法它就应该是可以实例化的,那就没有必要将其设定为纯虚类的,这也是我吐槽C#的abstract的一个原因。

有了纯虚类一定要有纯虚方法的这个基础,那就可以想象纯虚类的业务场景应该是下面这个样子的

abstract class Goods

{

public int GetPrice()

{

int price = 0;

//do some standard things

price = this.CaculatePrice();

//do some other steps

return price;

}

protected abstract int CaculatePrice();

}

我们有一类商品,它们都有一些统一的步骤需要做,先预处理,然后计算价格,这个计算价格是每种商品不一样的,最后还要统一再处理下,对一个售货员来说它其实并不关心这是什么商品有什么特性,她只关心这个商品的价格,如果购物车中有多个商品,计算出所有商品的总价格。所以一般会构建一个商品,它没有任何意义,只是将所有商品标准化东西统一起来,将不同的东西抽象为纯虚方法,这样不同的商品就可以只关心它是如何计算价格的,其它不同的部分交给虚基类来处理。这个是比较经典的业务场景,这里虚基类中可能会有多个public的方法,这些方法可以调用纯虚方法也是可以的。总之到最后Goods的使用者是直接调用Goods索引,而不是操作具体的商品类。这种情况一般纯虚类中的纯虚方法一般都是private或者protected(在C++中纯虚函数可以是private、protected和public,但是在C#中纯虚函数只能是protected和public),因为Goods的使用者一般不会直接操作商品内部的CaculatePrice的,这个应该是需要对使用者隐藏的。设成protected是有些时候需要对Goods再进行抽象,这类商品有它特定的使用者,这个时候它又需要调用Goods的方法,这种情况就开发成为protected,比如有一类商品,它们需要提供另外一种方法,计算另外一种类型的价格,但是这种计算方法又是依赖商品的CaculatePrice的结果,继承类需要再写一种方法调用基类的CaculatePrice的方法,如果是private,继承类就没有办法访问,此时就需要将其修改成为protected。

看样子纯虚类中的纯虚方法是不能为public的,一般是private或者protected的,那是否可以是public呢?答案是可以的。针对上面的例子,假设每种商品没有统一的步骤,计算方法各不一样,没有统一的步骤,那这个时候就需要将GetPrice改成纯虚函数,但是这里需要考虑的另外一个问题就是当一个纯虚类中有一个public的纯虚方法,要考虑这个纯虚方法是否是一种interface(这个是C#才有C++中没有的,这个是一个亮点,将is a和支持的方法隔离开来,因为is a的关系太强了),在这个例子我觉得还是纯虚类比较合适,interface不大合适。interface更多的像是一种技能,现实生活中不好想象这样的例子,在基础类库中的IEnumerator这个接口,这个是linq实现的基础,它就是一个典型的接口而不应该实现为纯虚类,list,array和dict它们没有公共关系,不好抽象出来一个基类来实现is
a的关系,但是都需要支持遍历数据结构中所有元素的方法,所以都实现IEnumerator的接口,所以interface更像一种技能。

在实际开发过程中需求往往是经常渐进,一开始我们并不知道要设计一个纯虚类,更多的情况是一开始只有一种商品,设计了Goods,然后有另外一种商品,它们有很大的共性,于是将共性提出来,让第二个商品继承第一个商品,其实此时就应该考虑纯虚类的,如果未来看得出来有更多同类的商品出现的话,应该重构一个纯虚基类出来,但是暂时没有让商品2继承商品1没问题,因为我觉得增加一个纯虚基类出来,而且未来还不知道能不能用上,没有必要,因为增加纯虚基类意味着强类型,将来很难再refactor,第二类太多对代码的维护成本也是很高的,基于done
is enough的原则,上面直接将商品2继承商品1没有问题,即使看上去两个不应该有继承关系,所以此时为了更好的解决这个问题,可以考虑将商品1改个名字,让它们的继承关系合法化。当商品越来越多的时候大多时候是不会重构出纯虚基类的,因为随着代码的不断增加,维护的成本会指数级增加,而且是修改一个基类,impact会比较大,一般也不会refactor一个纯虚基类。除非整个代码需要重构,可以考虑这么做,如果没有的话,这个代码结构不应该进行重构。所以纯虚基类一般都是代码整体重构的产物,当然如果在需求提出的时候就已经明确那是更好的。

在实际代码中我们会经常遇到一个虚方法是一个空函数的,那什么情况下会将一个虚方法设定为一个空函数呢?

空函数存在的价值是它是有意义的才可以,比如针对上面Goods类,假设它不是纯虚类,只是一个基类,如果有种商品就是没有价格,比如赠品,CalculatePrice直接返回0,没有任何计算,它的子类商品都有自己的计算价格方法,可以重写这个方法。所以说针对空函数的话,如果有意义才可以,一般不需要将其设成空函数,没有意义就可以直接将其改成纯虚方法。

总之:

纯虚类中是一定要有纯虚函数的,如果是私有纯虚方法那这个方法肯定是被纯虚类中其它public调用的,如果纯虚方法是public的时候是语义上更是is a的关系决定的,但是这个时候需要看看这个方法是否也算是has的方法,如果更多的是has的方法,需要考虑将这个方法改成interface,这个只有c#中才有这种功能,c++中是没有的,只是一个纯虚接口类而已。

时间: 2024-08-28 05:50:22

什么情况下应用纯虚类的相关文章

C++纯虚类小览

一.文章来由 virtual 方法和 virtual 类可以说是c++语言的一大特性,甚至有人说是c++语言的精髓,其实这么说也是有一定道理的,因为运行时多态在c++中体现淋漓尽致,而 virtual 就是为多态服务的.这也是一个一定要搞懂的c++问题,所以有了这篇文章.同时,我觉得这类底层问题不可能一文以蔽之,而且我也相信真正想搞懂这个问题的读者,不会只读我这一篇文章,所以只是小览,同时欢迎讨论和指正. 二.引入原因 其实,引入纯虚函数的原因我在我另一篇文章虚函数与多态小览就有写,不过重要的话

纯虚类中的纯析构函数

1. 纯虚类特点 纯虚类不能实例化: class AES{ public: virtual ~AES()=0; }; AES::~AES() { } class BES:public AES{ public: virtual ~AES()=0; }; int main() { AES *p=new BES; delete p; return 0; } // 通过基类的指针释放派生类对象:(虚函数实现)

在仅拿到头文件的情况下,如何修改类中的私有成员值?

1 通过使用从对象开始处的硬编码/手工编码的偏移量构造指针来访问私有成员数据 class Weak { public: Weak() = default; ~Weak() = default; // 想想如果去掉该函数,外部想修改类中的私有成员变量 m_name 时该如何操作? void name(const std::string &name) { m_name = name; } std::string name() const { return m_name; } private: std

C++虚函数和纯虚函数

只有用virtual声明类的成员函数,使之成为虚函数,不能将类外的普通函数声明为虚函数.因为虚函数的作用是允许在派生类中对基类的虚函数重新定义.所以虚函数只能用于类的继承层次结构中. 一个成员函数被声明为虚函数后,在同一类族中的类就不能再定义一个非virtual的但与该虚函数具有相同的参数(包括个数和类型)和函数返回值类型的同名函数. 根据什么考虑是否把一个成员函数声明为虚函数? ①  看成员函数所在的类是否会作为基类 ② 看成员函数在类的继承后有无可能被更改功能,如果希望更改其功能的,一般应该

C++ Primer学习笔记(14)——虚函数的实现机制、纯虚函数

在行文之前查阅了相关书籍,参考了一些别人的博客,在这里谢谢大家的分享!希望自己和大家在学习语言的道路上渐行渐远,一直走下去~~~ 上一篇文章中说道,C++ 的三个基本特质是 封装.继承.多态. 多态性是将接口与实现进行分离.用形象的语言解释就是实现已共同的方法,但因个体差异而采用不同的策略. 多态包括静多态和动多态,分别在编译和运行过程中实现.而动多态是由虚函数来实现的,其实现机制体现了C++的神秘性. 1.虚函数的实现机制 虚函数是那些以 virtual 关键字修饰的成员函数,是用来实现多态的

C++:抽象基类和纯虚函数的理解

转载地址:http://blog.csdn.net/acs713/article/details/7352440 抽象类是一种特殊的类,它是为了抽象和设计的目的为建立的,它处于继承层次结构的较上层. ⑴抽象类的定义: 称带有纯虚函数的类为抽象类. ⑵抽象类的作用: 抽象类的主要作用是将有关的操作作为结果接口组织在一个继承层次结构中,由它来为派生类提供一个公共的根,派生类将具体实现在其基类中作为接口的操作.所以派生类实际上刻画了一组子类的操作接口的通用语义,这些语义也传给子类,子类可以具体实现这些

C++学习笔记(十二):类继承、虚函数、纯虚函数、抽象类和嵌套类

类继承 在C++类继承中,一个派生类可以从一个基类派生,也可以从多个基类派生. 从一个基类派生的继承称为单继承:从多个基类派生的继承称为多继承. 1 //单继承的定义 2 class B:public A 3 { 4 < 派生类新定义成员> 5 }; 6 //多继承的定义 7 class C:public A,private B 8 { 9 < 派生类新定义成员> 10 }; 我们这篇主要说单继承. 派生类共有三种C++类继承方式: 公有继承(public) 基类的公有成员和保护成

C++ 虚函数&amp;纯虚函数&amp;抽象类&amp;接口&amp;虚基类(转)

http://www.cnblogs.com/fly1988happy/archive/2012/09/25/2701237.html 1. 多态 在面向对象语言中,接口的多种不同实现方式即为多态.多态是指,用父类的指针指向子类的实例(对象),然后通过父类的指针调用实际子类的成员函数. 多态性就是允许将子类类型的指针赋值给父类类型的指针,多态是通过虚函数实现的. 多态可以让父类的指针有“多种形态”,这是一种泛型技术.(所谓泛型技术,就是试图使用不变的代码来实现可变的算法). 2. 虚函数 2.1

C++入门经典-例8.9-抽象类,纯虚函数,创建纯虚函数

1:包含有纯虚函数的类称为抽象类,一个抽象类至少具有一个纯虚函数.抽象类只能作为基类派生出的新的子类,而不能在程序中被实例化(即不能说明抽象类的对象),但是可以使用指向抽象类的指针.在程序开发过程中并不是所有代码都是由软件构造师自己写的,有时候需要调用库函数,有时候分给别人写.一名软件构造师可以通过纯虚函数建立接口,然后让程序员填写代码实现接口,而自己主要负责建立抽象类. 2:纯虚函数 纯虚函数是指被标明为不具体实现的虚成员函数,它不具备函数的功能.许多情况下,在基类中不能给虚函数一个有意义的定