【C++】虚函数与多态

多态

多态性是面向对象程序设计的重要特征之一,是指发出同样的消息被不同类型的对象接受时有可能导致完全不同的行为。多态的实现:函数重载、运算符重载、模板、虚函数。

静态绑定与动态绑定

静态绑定:绑定过程出现在编译阶段,在编译期就已经确定要调用的函数。
动态绑定:绑定过程工作在程序运行时执行,在程序运行时才确定要调用的函数。

虚函数

虚函数的概念:在基类中冠以关键字virtual的成员函数。
虚函数的定义:

virtual 函数类型 函数名称(参数列表);

如果一个函数在基类中被声明为虚函数,则他在所有的派生类中都是虚函数。只有通过基类指针或调用虚函数才能引发动态绑定。虚函数不能声明为静态。

#include <iostream>
using namespace std;

class Base
{
    
    
public:
    virtual void Fun1()
    {
    
    
        cout << "Base::Fun1 ..." << endl;
    }
    virtual void Fun2()
    {
    
    
        cout << "Base::Fun2 ..." << endl;
    }
    void Fun3()
    {
    
    
        cout << "Base::Fun3 ..." << endl;
    }
};

class Derived :public Base
{
    
    
public:
    virtual void Fun1()

    {
    
    
        cout << "Derived::Fun1 ,,," << endl;
    }
    virtual void Fun2()
    {
    
    
        cout << "Derived::Fun2 ,,," << endl;
    }
    void Fun3()
    {
    
    
        cout << "Derived::Fun3 ..." << endl;
    }
};

int main()
{
    
    
    Base* p;
    Derived d;

    p = &d;
    p->Fun1();//Fun1是虚函数,基类指针指向派生类对象,调用的是派生类对象的虚函数
    p->Fun2();
    p->Fun3();//Fun3非虚函数,根据p指针实际类型来调用相应类的成员函数
    return 0;
}

虚析构函数

何时需要虚析构函数?当你可能通过基类指针删除派生类对象时,如果你打算允许其他人通过基类指针调用对象的析构函数(通过delete这样做是正常的),并且被析构的对象是有重要的析构函数的派生类的对象,就需要让基类的析构函数作为虚函数。

#include <iostream>
using namespace std;

class Base
{
    
    
public:
    virtual void Fun1()
    {
    
    
        cout << "Base::Fun1 ..." << endl;
    }
    virtual void Fun2()
    {
    
    
        cout << "Base::Fun2 ..." << endl;
    }
    void Fun3()
    {
    
    
        cout << "Base::Fun3 ..." << endl;
    }
    Base()
    {
    
    
        cout << "Base ..." << endl;
    }
    virtual ~Base()
    {
    
    
        cout << "~Base ..." << endl;
    }
};

class Derived :public Base
{
    
    
public:
    virtual void Fun1()

    {
    
    
        cout << "Derived::Fun1 ,,," << endl;
    }
    virtual void Fun2()
    {
    
    
        cout << "Derived::Fun2 ,,," << endl;
    }
    void Fun3()
    {
    
    
        cout << "Derived::Fun3 ..." << endl;
    }
    Derived()
    {
    
    
        cout << "Derived ..." << endl;
    }
    //基类析构函数为虚函数   派生类不加virtual也为虚函数
    //如果一个类要作为多态基类,要将析构函数定义成虚函数
    virtual ~Derived()
    {
    
    
        cout << "~Derived ..." << endl;
    }
};

int main()
{
    
    
    Base* p;
    p = new Derived;

    p->Fun1();
    delete p;
    return 0;
}

虚表指针

虚函数的动态绑定是通过虚表来实现的。包含虚函数的类头4个字节存放指向虚表的指针。

object slicing与虚函数

#include <iostream>
using namespace std;

class CObject
{
    
    
public:
    virtual void Serialize()
    {
    
    
        cout << "CObject::Serialize ..." << endl;
    }
};

class CDocument : public CObject
{
    
    
public:
    int data1_;
    void func()
    {
    
    
        cout << "CDocument::func ..." << endl;
        Serialize();
    }
    void Serialize()
    {
    
    
        cout << "CDocument::Serialize ..." << endl;
    }
    CDocument()
    {
    
    
        cout << "CDocument()" << endl;
    }
    CDocument(const CDocument& other)
    {
    
    
        cout << "CDocument(const CDocument& other)" << endl;
    }
};

class CMyDoc : public CDocument
{
    
    
public:
    int data2_;
    void Serialize()
    {
    
    
        cout << "CMyDoc::Serialize ..." << endl;
    }
};

int main()
{
    
    
    CMyDoc mydoc;
    CMyDoc* pmydoc = new CMyDoc;
    cout << "#1 testing" << endl;
    mydoc.func();

    cout << "#2 testing" << endl;
    ((CDocument*)(&mydoc))->func();  

    cout << "#3 testing" << endl;
    pmydoc->func();

    cout << "#4 testing" << endl;
    ((CDocument)mydoc).func();//mydoc对象强制转换为CDocument对象  向上转型  完全将派生类对象转化成了基类对象
    return 0;
}

overload、overwrite、override

成员函数被重载的特征:

  • 相同的范围(在同一个类中);
  • 函数名字相同;
  • 参数不同;
  • virtual关键字可有可无。

覆盖是指派生类函数覆盖基类函数,特征是:

  • 不同的范围(分别位于派生类与基类);
  • 函数名字相同;
  • 参数相同;
  • 基类函数必须有virtual关键字。

重定义(派生类与基类)

  • 不同的范围(分别位于派生类与基类);
  • 函数名与参数都相同,无virtual关键字;
  • 函数名相同,参数不同,virtual可有可无。·

纯虚函数

虚函数实现多态性的前提。需要在基类中定义共同的接口,接口要定义为虚函数。如果基类的接口没办法实现怎么办,就将这些接口定义为纯虚函数。在基类中不能给出有意义的虚函数定义,这时可以把它说明成纯虚函数,把它的定义留给派生类来做。纯虚函数不用实现。定义纯虚函数:

class 类名{
    
    
	virtual 返回值类型 函数名(参数表) = 0}

抽象类

抽象类为抽象和设计的目的而声明,将有关的数据和行为组织在一个继承层次结构中,保证派生类具有要求的行为。对于暂时无法实现的函数,可以声明为纯虚函数,留给派生类去实现。抽象类只能作为基类来使用,不能声明抽象类的对象,构造函数不能是虚函数,析构函数可以是虚函数。抽象类不能用于直接创建对象实例,可以声明抽象类的指针和引用。可使用指向抽象类的指针支持运行时多态性。派生类中必须实现基类中的纯虚函数,否则它将被看作一个抽象类。

多态优点

  • 多态性有助于更好地对程序进行抽象。控制模块能专注于一般性问题的处理。具体的操作交给具体的对象去做。

  • 多态性有助于提高程序的可扩展性。可以把控制模块与被操作的对象分开。可以添加已定义类的新对象,并能管理该对象。可以添加新类(已有类的派生类)的新对象,并能管理该对象。

虚析构函数

析构函数可以声明为虚函数,delete基类指针程序会根据基类指针指向的对象的类型确定要调用的析构函数。基类的析构函数为虚函数,所有派生类的析构函数都是虚函数。构造函数不得是虚函数。如果要操作具有继承关系的类的动态对象,最好使用虚析构函数。特别是在析构函数需要完成一些有意义的操作比如释放内存时,析构函数还可以是纯虚的。

对于一个没有任何接口的类,如果想要将它定义成抽象类,只能将虚析构函数声明为纯虚的,通常情况下在基类中纯虚函数不需要实现,例外是纯虚析构函数要给出实现(给出一个空的实现即可)。

#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);
    }
}

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()
{
    
    
    //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);*/

    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;
}

猜你喜欢

转载自blog.csdn.net/weixin_43912621/article/details/131203755