虚函数与多态(二)

  • 虚函数是实现多态性的前提
    ①、需要在基类中定义共同的接口
    ②、接口要定义为虚函数
  • 如果基类的接口没办法实现怎么办?
    如形状类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 基类指针;
    ②、程序会根据基类指针指向的对象的类型确定要调用的析构函数;
    ③、基类的析构函数为虚函数,所有派生类的析构函数都是虚函数;
  • 构造函数不得是虚函数。
  • 如果要操作具有继承关系的类的动态对象,最好使用虚析构函数。特别是在析构函数需要完成一些有意义的操作——比如释放内存时。
  • 析构函数还可以是纯虚的。
    什么场景会用到它呢?下面举个例子说明一下:


    这时就可以用到纯虚析构函数:

    编译:



    由于构建的调用顺序是先析构派生类,再析构基类,而目前基类是纯虚析构,所以就无法调用成功了,那有没有办法解决能实例化派生类的问题呢?

    编译:

    想要说明的是:对于一个没有任何接口的类,如果想要将它定义成抽象类,只能将虚构函数声明为纯虚的,通常情况下在基类中的纯虚析构不需要实现,但是上面这种场景是个例化是需要让纯虚析构函数也给出实现的。

posted on 2016-07-14 21:54  cexo  阅读(178)  评论(0)    收藏  举报

导航