虚函数与多态(二)

- 虚函数是实现多态性的前提
 ①、需要在基类中定义共同的接口
 ②、接口要定义为虚函数
- 如果基类的接口没办法实现怎么办?
 如形状类Shape 
- 解决方法
 将这些接口定义为纯虚函数
- 在基类中不能给出有意义的虚函数定义,这时可以把它说明成纯虚函数,把它的定义留给派生类来做。
- 定义纯虚函数:
 class 类名{ 
 virtual 返回值类型 函数名(参数表) = 0;
 };
- 纯虚函数不需要实现。

- 作用
 ①、抽象类为抽象和设计的目的而声明,将有关的数据和行为组织在一个继承层次结构中,保证派生类具有要求的行为。
 ②、对于暂时无法实现的函数,可以声明为纯虚函数,留给派生类去实现。
- 注意
 ①、抽象类只能作为基类来使用。
 ②、不能声明抽象类的对象。
 ③、构造函数不能是虚函数,析构函数可以是虚函数。【之前已经解释过】
 ④、抽象类不能用于直接创建对象实例,可以声明抽象类的指针和引用。
 ⑤、可使用指向抽象类的指针支持运行时多态性。
 ⑥、派生类中必须实现基类中的纯虚函数,否则它仍将被看作一个抽象类。
下面用代码来进行说明:
#include <iostream> using namespace std; class Shape {//形状类,只要一个类中拥有一个纯虚函数既为抽象类 public: virtual void draw() = 0; }; class Circle : public Shape {//圆形类 public: void draw() { cout<<"Circle::draw() ..."<<endl; } }; class Square : public Shape {//圆形类 public: void draw() { cout<<"Square::draw() ..."<<endl; } }; int main(void) { Shape s; return 0; }
编译:

这个结果很容易理解,但是可以声明成引用,然后去指向具体的派生类,有什么好处呢?下面来看一下:
#include <iostream> #include <vector> using namespace std; class Shape {//形状类,只要一个类中拥有一个纯虚函数既为抽象类 public: virtual void draw() = 0; }; class Circle : public Shape {//圆形类 public: void draw() { cout<<"Circle::draw() ..."<<endl; } }; class Square : public Shape {//圆形类 public: void draw() { cout<<"Square::draw() ..."<<endl; } }; //绘制所有的形状 void drawAllShapes(const vector<Shape*>& v) { vector<Shape*>::const_iterator it; for(it=v.begin();it!=v.end();++it) { (*it)->draw(); } } int main(void) { //Shape s;//ERROR,不能实例化抽象类 vector<Shape*> v; Shape* ps; ps = new Circle; v.push_back(ps); ps = new Square; v.push_back(ps); drawAllShapes(v); return 0; }
编译运行:

如果我们不将Shape的draw方法声明为纯虚的,那结果又是如何呢?


对比来看,其实就是涉及到多态的问题,也就是虚函数可以用统一的观点来看待不同的行为。还是将代码还原,将Shape中的draw方法声明为纯虚函数,下面来看一下虚构函数:
#include <iostream> #include <vector> using namespace std; class Shape {//形状类,只要一个类中拥有一个纯虚函数既为抽象类 public: virtual void draw() = 0; }; class Circle : public Shape {//圆形类 public: void draw() { cout<<"Circle::draw() ..."<<endl; } ~Circle() { cout<<"~Circle() ..."<<endl; } }; class Square : public Shape {//圆形类 public: void draw() { cout<<"Square::draw() ..."<<endl; } ~Square() { cout<<"~Square() ..."<<endl; } }; //绘制所有的形状 void drawAllShapes(const vector<Shape*>& v) { vector<Shape*>::const_iterator it; for(it=v.begin();it!=v.end();++it) { (*it)->draw(); } } void deleteAllShapes(const vector<Shape*>& v) { vector<Shape*>::const_iterator it; for(it=v.begin();it!=v.end();++it) { delete *it; } } int main(void) { //Shape s;//ERROR,不能实例化抽象类 vector<Shape*> v; Shape* ps; ps = new Circle; v.push_back(ps); ps = new Square; v.push_back(ps); drawAllShapes(v); deleteAllShapes(v); return 0; }
编译运行:

为啥派生类的构造函数都没调用呢?原因是需要将基类定义为虚析构才行:

再次编译运行:


- 多态性有助于更好地对程序进行抽象
 ①、控制模块能专注于一般性问题的处理。
 ②、具体的操作交给具体的对象去做。
- 多态性有助于提高程序的可扩展性
 ①、可以把控制模块与被操作的对象分开。
 ②、可以添加已定义类的新对象,并能管理该对象。
 ③、可以添加新类(已有类的派生类)的新对象,并能管理该对象。
比如如果再增加一个矩形类,不需要改动代码就能支持动态添加,如下:
#include <iostream> #include <vector> using namespace std; class Shape {//形状类,只要一个类中拥有一个纯虚函数既为抽象类 public: virtual void draw() = 0; virtual ~Shape(){} }; class Circle : public Shape {//圆形类 public: void draw() { cout<<"Circle::draw() ..."<<endl; } ~Circle() { cout<<"~Circle() ..."<<endl; } }; class Square : public Shape {//圆形类 public: void draw() { cout<<"Square::draw() ..."<<endl; } ~Square() { cout<<"~Square() ..."<<endl; } }; class Rectangle : public Shape {//矩形类 public: void draw() { cout<<"Rectangle::draw() ..."<<endl; } ~Rectangle() { cout<<"~Rectangle() ..."<<endl; } }; //绘制所有的形状 void drawAllShapes(const vector<Shape*>& v) { vector<Shape*>::const_iterator it; for(it=v.begin();it!=v.end();++it) { (*it)->draw(); } } void deleteAllShapes(const vector<Shape*>& v) { vector<Shape*>::const_iterator it; for(it=v.begin();it!=v.end();++it) { delete *it; } } int main(void) { //Shape s;//ERROR,不能实例化抽象类 vector<Shape*> v; Shape* ps; ps = new Circle; v.push_back(ps); ps = new Square; v.push_back(ps); ps = new Rectangle; v.push_back(ps); drawAllShapes(v); deleteAllShapes(v); return 0; }
编译运行:

其实对于上面的代码可以进一步优化,对于创建每一个形状其实可以用放到统一的地方便于管理,改造代码如下:
#include <iostream> #include <vector> #include <string> using namespace std; class Shape {//形状类,只要一个类中拥有一个纯虚函数既为抽象类 public: virtual void draw() = 0; virtual ~Shape(){} }; class Circle : public Shape {//圆形类 public: void draw() { cout<<"Circle::draw() ..."<<endl; } ~Circle() { cout<<"~Circle() ..."<<endl; } }; class Square : public Shape {//圆形类 public: void draw() { cout<<"Square::draw() ..."<<endl; } ~Square() { cout<<"~Square() ..."<<endl; } }; class Rectangle : public Shape {//矩形类 public: void draw() { cout<<"Rectangle::draw() ..."<<endl; } ~Rectangle() { cout<<"~Rectangle() ..."<<endl; } }; //绘制所有的形状 void drawAllShapes(const vector<Shape*>& v) { vector<Shape*>::const_iterator it; for(it=v.begin();it!=v.end();++it) { (*it)->draw(); } } void deleteAllShapes(const vector<Shape*>& v) { vector<Shape*>::const_iterator it; for(it=v.begin();it!=v.end();++it) { delete *it; } } //简单工厂模式 class ShapeFactory { public: static Shape* createShape(const string& name) { Shape* ps = 0; if(name == "Circle") { ps = new Circle; } else if(name == "Square") { ps = new Square; } else if(name == "Rectangle") { ps = new Rectangle; } return ps; } }; int main(void) { //Shape s;//ERROR,不能实例化抽象类 vector<Shape*> v; /*Shape* ps; ps = new Circle; v.push_back(ps); ps = new Square; v.push_back(ps); ps = new Rectangle; v.push_back(ps);*/ Shape* ps; ps = ShapeFactory::createShape("Circle"); v.push_back(ps); ps = ShapeFactory::createShape("Square"); v.push_back(ps); ps = ShapeFactory::createShape("Rectangle"); v.push_back(ps); drawAllShapes(v); deleteAllShapes(v); return 0; }
但是上面简单工厂模式中用了大量的if..else,如果之后再要添加一个元素,则这个工厂里面的创建方法还是需要修改,有没有完全不修改里面的方法而达到随意任意添加不同的形状呢?答案是肯定的,这个在之后会学到一种动态创建技术,这里抛出来,目前先体会多态的好外既可。

- 析构函数可以声明为虚函数
 ①、delete 基类指针;
 ②、程序会根据基类指针指向的对象的类型确定要调用的析构函数;
 ③、基类的析构函数为虚函数,所有派生类的析构函数都是虚函数;
- 构造函数不得是虚函数。
- 如果要操作具有继承关系的类的动态对象,最好使用虚析构函数。特别是在析构函数需要完成一些有意义的操作——比如释放内存时。
- 析构函数还可以是纯虚的。
 什么场景会用到它呢?下面举个例子说明一下:  
 这时就可以用到纯虚析构函数: 
 编译:   
 由于构建的调用顺序是先析构派生类,再析构基类,而目前基类是纯虚析构,所以就无法调用成功了,那有没有办法解决能实例化派生类的问题呢? 
 编译: 
 想要说明的是:对于一个没有任何接口的类,如果想要将它定义成抽象类,只能将虚构函数声明为纯虚的,通常情况下在基类中的纯虚析构不需要实现,但是上面这种场景是个例化是需要让纯虚析构函数也给出实现的。
 
                    
                     
                    
                 
                    
                 
                
            
         
 
         浙公网安备 33010602011771号
浙公网安备 33010602011771号