C++之虚函数与多态

1、多态

前面三种称为静态绑定(静态多态),最后面的虚函数,则称为动态绑定(动态多态)。

2、静态绑定与动态绑定

要实现动态绑定,就必须使用虚函数

3、虚函数

只有当你在:基类的指针指向派生类的对象的时候,正常是调用基类的函数,当你需要掉用那个派生类的函数的时候,就把该函数声明为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 Dericed : public Base
{
public:
    virtual void Fun1()  // 等价于vioid Fun1,在派生类中,即使不加virtual,继承过来的也是虚函数
    {
        cout << "Dericed::Fun1 ..." << endl;
    }

    virtual void Fun2()
    {
        cout << "Dericed::Fun2 ..." << endl;
    }

    void Fun3()
    {
        cout << "Dericed::Fun3 .." << endl;
    }
};

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

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

// 输出
Dericed::Fun1 ...
Dericed::Fun2 ...
Base::Fun3 ..

4、虚析构函数

如果一个类要作为多态基类,要将析构函数定义成虚函数(不然析构的时候,子类不会被析构,存在内存泄露)
如果确定一个类不会被其他类继承,那就没必要定义成虚析构函数
#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 Dericed : public Base
{
public:
    virtual void Fun1()  // 等价于vioid Fun1,在派生类中,即使不加virtual,继承过来的也是虚函数
    {
        cout << "Dericed::Fun1 ..." << endl;
    }

    virtual void Fun2()
    {
        cout << "Dericed::Fun2 ..." << endl;
    }

    void Fun3()
    {
        cout << "Dericed::Fun3 .." << endl;
    }

    Dericed()
    {
        cout << "Dericed() ..." << endl;
    }
    ~Dericed()
    {
        cout << "~Derived() ..." << endl;
    }
};

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

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

//输出
Base ...
Dericed() ...
Dericed::Fun1 ...
~Derived() ...
~Base ...

5、虚表指针

如果一个类中有一个或者以上的虚函数,那么编译器会自动为这个类的头4个字节产生一个指针,指向虚表。

虚函数不能声明为静态函数的原因:

比如Base::Fun2(),他是直接访问,没有this指针,属于类共享的成员,不是类对象的一部分,就没有办法通过对象的头4个字节的虚表指针来找到虚表。同样的友元函数也不行。

图中下面的Base为Dericed

#include <iostream>
using namespace std;

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

    int data1_;
};

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

    virtual void Fun3()
    {
        cout << "Dericed::Fun3 ..." << endl;
    }

    int data2_;
};

typedef void (*FUNC)();
int main() {
    cout << sizeof(Base) << endl;
    cout << sizeof(Dericed) << endl;
    Base b;
    long** p = (long**)&b;
    FUNC fun = (FUNC)p[0][0];
    fun();
    fun = (FUNC)p[0][1];
    fun();
    cout << endl;

    Dericed d;
    p = (long**)&d;
    fun = (FUNC)p[0][0];
    fun();

    fun = (FUNC)p[0][1];
    fun();

    fun = (FUNC)p[0][2];
    fun();

    return 0;
}

6、object slicing与虚函数

对象在向上转型的时候会存在对象切割的问题,派生类特有的成员消失。

#include <iostream>
using namespace std;

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

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

    virtual void Serialize()
    {
        cout << "CDocument::Serialize ..." << endl;
    }

    CDocument()
    {
        cout << "CDocument::CDocument() .." << endl;
    }

    CDocument(const CDocument& other)
    {
        cout << "CDocument(const CDocument& other)" << endl;
    }

    int data1_;
};

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

    int data2_;
};

int main() {
    CMyDoc mydoc;
    CMyDoc* pmydoc = new CMyDoc;

    cout << "#1 testing" << endl;
    mydoc.Fun();

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

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

    // 对象向上转型,CMyDoc中的Serialize被切割,所以调用的是上一层的Serialize
    // 会调用拷贝构造函数
    cout << "#4 testing" << endl;
    ((CDocument)(mydoc)).Fun();

    return 0;
}

//输出
CDocument::CDocument() ..
CDocument::CDocument() ..
#1 testing
CDocument::Fun ...
CMyDoc::Serialize ...
#2 testing
CDocument::Fun ...
CMyDoc::Serialize ...
#3 testing
CDocument::Fun ...
CMyDoc::Serialize ...
#4 testing
CDocument(const CDocument& other)
CDocument::Fun ...
CDocument::Serialize ...

7、overload重载、override覆盖、overwrite重定义或者重写

8、纯虚函数

虚函数的作用:当基类指针指向派生类对象的时候,在派生类中重写或者覆盖某个函数,则掉用的是派生类的虚函数。

这就使得我们可以以一致的观点来看待不同的派生类对象。

9、抽象类

拥有一个纯虚函数的类称为抽象类,抽象类不能实例化。

构造函数不能是虚函数的原因:如果构造函数是虚函数,那么就应该把这个构造函数放入vptr指向的虚函数表中;那么在构造函数还没调用完之前,这个对象是没有办法生成的,既然这个对象还没生成,就没办法知道这个虚表指针指向的地址。

一个类,如果作为一个多态用途的基类,析构函数就应该声明为虚函数。

#include <iostream>
using namespace std;

#include <vector>

class Shape
{
public:
    virtual void Draw() = 0;
    virtual ~Shape()  // Z这边必须要声明为虚析构函数,不然子类不会被释放(基类指针指向子类对象的时候,释放该指针的时候)
    {

    }
};

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() {
    //Shape s;  // ERROR:Variable type 'Shape' is an abstract class

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

//输出
Circle::Draw() ...
Square::Draw() ...
~Circle() ...
~Square() ...

10、多态优点

11、虚析构函数

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值