C++多态

一、C++多态
多态是指一组具有继承关系的类,拥有相同的接口(函数名、形参和返回值),并允许有各自不同的实现,且一个对象实例只有在调用这共同接口的时候,才能确定调用的是何种实现。 即,“一个接口,多种实现”

二、静态多态
编译器在编译期间完成的
静态多态 : 通过”彼此单独定义但支持共同操作的具体类“来表达共同性。
函数多态:即函数重载
概念:基于不同的参数列表,同一函数名可指向不同的函数体实现。

三、动态多态
动态绑定:在程序执行期间判断所引用对象的实际类型,根据其实际类型调用相应的方法。
以类继承和虚函数机制为基础,表达共同接口。
动态多态是为面向对象编程服务的。因此,多态通常是指动态多态。

1、使用virtual关键字:
使用virtual关键字修饰类的成员函数时,指明该函数为虚函数,派生类需要重新实现,编译器将实现动态绑定
比如下面这个很有意思的例子:

class CWashRoom
{
public:
    void GoToManWashRoom()
    {
        cout << "MAN--->Please Left" << endl;
    }
    void GoToWomanWashRoom()
    {
        cout << "WOMAN--->Please Right" << endl;
    }
};

class CPerson
{
public:
    virtual void GoToWashRoom(CWashRoom& _washRoom) = 0;//纯虚函数
};

class CMan :public CPerson
{
public:
    virtual void GoToWashRoom(CWashRoom& _washRoom)
    {
        _washRoom.GoToManWashRoom();
    }
};

class CWoman :public CPerson
{
public:
    virtual void GoToWashRoom(CWashRoom& _washRoom)
    {
        _washRoom.GoToWomanWashRoom();
    }
};
void Test()
{
    CWashRoom washRoom;
    for (int iIdx = 1; iIdx <= 10; ++iIdx)
    {
        CPerson* pPerson;
        int iPerson = rand() % iIdx;
        if (iPerson & 0x01)
        {
            pPerson = new CMan;
        }
        else
        {
            pPerson = new CWoman;
        }
        pPerson->GoToWashRoom(washRoom);
        delete pPerson;
        pPerson = NULL;
        Sleep(1000);
    }
}

2、虚函数的声明:
虚函数: 虚函数必须是被关键字virtual修饰的某类的非静态成员函数。
声明方法: virtual <函数返回值类型> <函数名>(<参数表>);
注意: 在派生类中重定义的虚函数必须和基类的虚函数完全相同(协变除外),即具有相同的返回类型、参数个数和参数类型。否则就是一种语法错误。
3、继承体系中容易搞混的三种同名函数的关系:

4、虚函数的总结:
为什么静态成员函数不能定义为虚函数
为什么构造函数不能定义为虚函数,为什么operator=可以定义成虚函数,但又不建议这样做


6、分析为什么构造函数和析构函数中不能调用虚函数,以及调用之后存在的问题

7、最好将析构函数声明为虚函数,为什么?

class Base
{
public:
     Base* func()
    {
        cout << "Base::func1" << endl;
        return this;
    }
    virtual~Base()
    //~Base()
    {
        cout << "~Base()" << endl;
    }

};
class Derive :public Base
{
public:
    Derive* func()
    {
        cout << "Derive::func1" << endl;
        return this;
    }
    ~Derive()
    {
        cout << "~Derive()" << endl;
    }
};

int main()
{
    Base* ptr = new Derive;
    delete ptr;
    getchar();
    return 0;
}

class Base
{
public:
    Base()
    {
        cout << "Base()" << endl;
    }
    ~Base()
    {
        cout << "~Base()" << endl;
    }
private:
    int _b;
};
class Derived :public Base
{
public:
    Derived()
    {
        cout << "Derived()" << endl;
        p = new int(0);
    }
    ~Derived()
    {
        cout << "Derived()" << endl;
        delete p;
    }
private:
    int _d;
    int* p;
};
void Test()
{
    Base* pb = new Derived;//如果析构函数不是虚函数,那么就是普通函数的调用方法,直接调用,不会去查函数虚表,这样pb是基类的指针,它只会调用基类的析构函数
    delete pb;
}
int main()
{
    Test();
    getchar();
    return 0;
}

验证结果:


四、虚函数表vftable(virtual function table):

先来看一段代码:

class Base
{
public:
    virtual void FunTest1()
    {
        cout << "Base::FunTest1()" << endl;
    }

private:
    int _b;
};
class Derived :public Base
{
public:
    virtual void FunTest1()
    {
        cout << "Derived::FunTest1()" << endl;
    }
private:
    int _d;
};
int main()
{
    cout << sizeof(Derived) << endl;
    return 0;
}


按照以前的算法 sizeof(D)是8,个字节,那么多出来的4个字节又是什么呢?

调用监视窗口可以看到有两个指针 _vfptr (虚表指针)

1、普通继承(单):

class B
{
publicvirtual void FunTest1()
    {
        cout << "B::FunTest1()" << endl;
    }
    virtual void FunTest2()
    {
        cout << "B::FunTest2()" << endl;
    }
    int _b;
};

class C :public B
{
public:
    virtual void FunTest1()
    {
        cout << "C::FunTest1()" << endl;
    }
    virtual void FunTest3()
    {
        cout << "C::FunTest3()" << endl;
    }
    int _c;
};
void Test(B& b)
{
    b.FunTest1();
    b.FunTest2();
}
int main()
{
    B b;
    C c;
    Test(b);
    Test(c);
    b._b = 1;
    c._c = 2;

    cout << sizeof(C) << endl;//12
    getchar();
    return 0;
}

2、多继承:

class B1
{
public:
    virtual void FunTest1()
    {
        cout << "B1::FunTest1()" << endl;
    }
    virtual void FunTest2()
    {
        cout << "B1::FunTest2()" << endl;
    }
    int _b1;
};
class B2
{
public:
    virtual void FunTest3()
    {
        cout << "B2::FunTest3()" << endl;
    }
    virtual void FunTest4()
    {
        cout << "B2::FunTest4()" << endl;
    }
    int _b2;
};
class C :public B1, public B2
{
public:
    virtual void FunTest1()
    {
        cout << "C::FunTest1()" << endl;
    }
    virtual void FunTest3()
    {
        cout << "C::FunTest3()" << endl;
    }
    virtual void FunTest5()
    {
        cout << "C::FunTest5()" << endl;
    }
    int _c;
};
int main()
{
    cout << sizeof(C) << endl;//20
    B1 b1;
    B2 b2;
    C c;
    B1* p1 = &b1;
    p1->FunTest1();
    p1->FunTest2();
    cout << endl;

    B2* p2 = &b2;
    p2->FunTest3();
    p2->FunTest4();
    cout << endl;

    C* pc = &c;
    pc->FunTest3();
    pc->FunTest5();
    getchar();
    return 0;
}

这里写图片描述

3、虚拟继承:

class B
{
public:
    virtual void FunTest1()
    {
        cout << "B::FunTest1()" << endl;
    }
    virtual void FunTest2()
    {
        cout << "B::FunTest2()" << endl;
    }
    int b;
};

class C :virtual public B
{
public:
    virtual void FunTest1()
    {
        cout << "C::FunTest1()" << endl;
    }
    virtual void FunTest3()
    {
        cout << "C::FunTest3()" << endl;
    }
    int c;
};
int main()
{
    cout << sizeof(C) << endl;//20
    B b;
    C c;
    b.b = 1;
    c.c = 2;
    B* p1 = &b;
    p1->FunTest1();
    p1->FunTest2();
    cout << endl;

    C* pc = &c;
    pc->FunTest1();
    pc->FunTest3();
    getchar();
    return 0;
}


4、菱形继承:

class B
{
public:
    virtual void FunTest1()
    {
        cout << "B::FunTest1()" << endl;
    }
    virtual void FunTest2()
    {
        cout << "B::FunTest2()" << endl;
    }
    int b;
};

class C1 :virtual public B
{
public:
    virtual void FunTest1()
    {
        cout << "C1::FunTest1()" << endl;
    }
    virtual void FunTest3()
    {
        cout << "C1::FunTest3()" << endl;
    }
    int c1;
};
class C2 :virtual public B
{
public:
    virtual void FunTest1()
    {
        cout << "C2::FunTest1()" << endl;
    }
    virtual void FunTest4()
    {
        cout << "C2::FunTest4()" << endl;
    }
    int c2;
};
class D :public C1, public C2
{
public:
    virtual void FunTest1()
    {
        cout << "D::FunTest1()" << endl;
    }
    virtual void FunTest3()
    {
        cout << "D::FunTest3()" << endl;
    }
    virtual void FunTest5()
    {
        cout << "D::FunTest5()" << endl;
    }
    int d;
};
int main()
{
    cout << sizeof(D) << endl;
    getchar();
    return 0;
}


这次为什么D的大小是36 呢?
这时我们就需要分析其对象模型

虚表指针:
每一个多态类都有且仅有一个存储所有虚函数入口地址的数组。
虚函数表指针vptr:
这里写图片描述
每一个多态类的所有对象都有且仅有一个的隐藏成员vptr,指向虚函数表vftable.
vptr和vftable的初始化:
基类对象的vptr指向基类的vftable。
派生类对象vptr指向该派生类的vftable。
如果派生类没有覆盖(override)父类的虚函数,则其vftable中对应表项指向其父类的此函数。 如果派生类覆盖了父类的虚函数,则vftable中对应表项指向重写后的此函数。 如果派生类定义了新的虚函数,则此函数的地址将被添加到vftable中
虚函数的工作原理:
多态的函数调用语句被编译成一系列根据基类指针所指向的(或基类引用所引用的)对象中存放的虚函数表的地址,在虚函数表中查找虚函数地址,并调用虚函数的过程。
vptr和vftable在内存中的存储

class Base
{
public:
    virtual void func1()
    {
        cout << "Base::func1" << endl;
    }
    virtual void func2()
    {
        cout << "Base::func2" << endl;
    }
    void func3()
    {
        cout << "Base::func3" << endl;
    }
};
class Derive :public Base
{
public:
    virtual void func1()
    {
        cout << "Derive::func1" << endl;
    }
    virtual void func3()//构成重载   func也要放在虚表中
    {
        cout << "Derive::func3" << endl;
    }
    virtual void func4()
    {
        cout << "Derive::func3" << endl;
    }

};
typedef void(*V_FUNC)();


void PrintVTable(int vtable)//打印虚表
{
    int* vf_array = (int*)vtable;
    printf("vtable:0x%p\n",vtable);
    for (size_t i = 0; vf_array[i] != 0; ++i)
    {
        printf("vatable[%d]:0x%p->", i, vf_array[i]);
        V_FUNC f = (V_FUNC)vf_array[i];
        f();
    }
    printf("----------------------------\n");
}

int main()
{
    Base b;
    Derive d;
    PrintVTable(*(int*)&b);
    PrintVTable(*(int*)&d);

    Base* ptr=&d;
    ptr->func1();//构成多态
    ptr->func3();//不是多态

    getchar();
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值