c++多态和虚函数

2018-10-11  本文已影响7人  编程小兔崽

原创: 神秘编程 神秘编程 今天

问题抛出:为什么要使用多态?如果子类定义了与父类中原型相同的函数会发生什么?

多态:同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果。在运行时,可以通过指向基类的指针,来调用实现派生类中的方法。

那么C++是用什么实现多态的呢?

虚函数,抽象类,覆盖,模板(重载和多态无关)。

简单多态例子

#include<iostream>

using namespace std;

class Base

{

public:

Base(){}

virtual void foo()

{

cout<<"This is A."<<endl;

}

private:

int a;

int b;

};

class D: public Base

{

public:

D(){}

void foo()

{

cout<<"This is B."<<endl;

}

int x;

int y;

};

int main(int argc, char *argv[])

{

Base *a = new D();

a->foo();

if(a != NULL)

delete a;

return 0;

}

如果把virtual去掉,将显示:

This is A.

前面的多态通过使用虚函数virtual void foo()来实现。

到这里相信大家对多态有一丁点感觉了。

多态成立的个条件:

1:必须要有继承;

2:类中存在的成员函数是虚函数(virtual);

3:要有父类的指针或者引用指向子类对象;

4:子类一定要三同(返回值、函数名、参数列表);

5:二个类外;

(1)父类的成员函数返回值为 A*;子类的成员函数返回值为 B*,现在打破三同,还是覆盖。

(2)父类的析构函数一般都要加上(virtual),就可以利用多态性级联的调用析构函数,此时

先调用子类的,在调用父类的;(避免发生内存泄漏)。

内存模型

定义一个子类对象时,该子类就会有相应的内存情况,此时就是内存模型;

虚函数

虚函数是一个类的成员函数,其定义格式如下:virtual 返回值类型 函数名(参数表);

关键字virtual指明该成员函数为虚函数。

class Test()

{

public:

virtual void foo()

{

cout<<"This is A."<<endl;

}

}

虚函数剖析

(1)、当在类中出现一个(virtual),虚方法时,对象中第一个成员将是:_vptr;

此时只有一个成员,应该为4B,但是其因为有虚函数的存在,内部的第一个成员就一定为虚表指针;

指针32位下为4字节,所以此时一共为8字节;(不管内部有多少个虚函数,但是虚表指针只有一个);

(2)、虚函数将在继承体系中,一直为虚(覆盖时); 此时的virtual可以省略不写;

(3)、多态:就是对虚方法的重写;

(4)、虚表:装虚函数的表,其本质是地址的覆盖;

多态的原理

没有覆盖的虚表

如果子类有一个方法与父类的虚方法三同,此时覆盖;

但是通过父类的指针/引用永远只能访问父类对象的部分;

或取得虚表中的函数

Base b;

cout<<&b<<endl;   //对象的地址

printf("%p\n", *(int *)(&b));  //虚表中虚表指针的值(指针4字节),所以转换整形指针,也就是虚表指针的地址;

((Fun)*((int*)*(int*)(&b) + 0))();  //Fun是函数指针,将获得虚表中的第一个函数;

((Fun)*((int*)*(int*)(&b) + 1))();  //Fun是函数指针,将获得虚表中的第二个函数;

((Fun)*((int*)*(int*)(&b) + 2))();  //Fun是函数指针,将获得虚表中的第三个函数;

多继承中虚表

父类均为虚函数,子类中也有虚函数,且没有进行覆盖,则将子类的放到第一个虚表的最后,其余的父类虚表就不用放了;因为,

就是放了,通过父类的指针/引用也访问不了,浪费内存空间;要是有覆盖的,则每个虚表都得画出;其余情况类似分析就行。

纯虚函数与抽象类

#include<iostream>

using namespace std;

class Test{

public:

   virtual void fun() = 0;  //这种形式就是存虚函数,赋值为0;

   virtual void fun1() = 0;

   virtual void fun2() = 0;

   virtual void fun3() = 0;

};

int main(void){

   return 0;

}

单继承下的虚函数表

//单继承下虚函数表:是如何组织的

class A{

public:

virtual void func(){

cout << "A::func" << endl;

}

virtual void funcA(){

cout << "A::funcA" << endl;

}

};

class B:public A{

public:

virtual void func(){

cout << "B::func" << endl;

}

virtual void funcB(){

cout << "B::funcB" << endl;

}

};

class C:public A{

public:

virtual void func(){

cout << "C::func" << endl;

}

virtual void funcC(){

cout << "C::funcC" << endl;

}

};

typedef void (*FUNC)();

int main()

{

A a;

B b;

C c;

cout << "A::虚表:" << endl;

((FUNC)(*(int *)(*(int*)(&a))))();

((FUNC)(*((int*)(*(int*)(&a)) + 1)))();

cout << "-------------------------------------" << endl;

cout << "B::虚表:" << endl;

((FUNC)(*(int *)(*(int*)(&b))))();

((FUNC)(*((int*)(*(int*)(&b)) + 1)))();

((FUNC)(*((int*)(*(int*)(&b)) + 2)))();

cout << "-------------------------------------" << endl;

cout << "C::虚表:" << endl;

((FUNC)(*(int *)(*(int*)(&c))))();

((FUNC)(*((int*)(*(int*)(&c)) + 1)))();

((FUNC)(*((int*)(*(int*)(&c)) + 2)))();

system("pause");

return 0;

}

单继承情况下:(虚函数的存放情况----->存放地址)

1:先基类的虚函数;

2:再派生类的虚函数;

3:存在覆盖的话,派生类的虚函数占据了覆盖的基类的虚函数位置;

typedef void(*FUNC)();

class A{

public:

virtual void func(){

cout << "A::func" << endl;

}

virtual void funcA(){

cout << "A::funcA" << endl;

}

private:

int a;

};

class B{

public:

virtual void func(){

cout << "B::func" << endl;

}

virtual void funcB(){

cout << "B::funcB" << endl;

}

private:

int b;

};

class C :public A, public B{

public:

virtual void func(){

cout << "C::func" << endl;

}

virtual void funcC(){

cout << "C::funcC" << endl;

}

private:

int c;

};typedef void(*FUNC)();

class A{

public:

virtual void func(){

cout << "A::func" << endl;

}

virtual void funcA(){

cout << "A::funcA" << endl;

}

private:

int a;

};

class B{

public:

virtual void func(){

cout << "B::func" << endl;

}

virtual void funcB(){

cout << "B::funcB" << endl;

}

private:

int b;

};

class C :public A, public B{

public:

virtual void func(){

cout << "C::func" << endl;

}

virtual void funcC(){

cout << "C::funcC" << endl;

}

private:

int c;

};

多继承条件下的虚函数表

//多继承条件下的虚函数表

void test()

{

C c;

cout << "多继承条件下的虚函数表:" << endl;

cout << "------------------------" << endl;

((FUNC)(*((int*)(*(int *)(&c)))))();

((FUNC)(*((int*)(*(int*)(&c)) + 1)))();

((FUNC)(*((int*)(*(int*)(&c)) + 2)))();

cout << "------------------------" << endl;

((FUNC)(*(int*)(*((int*)(&c) + 2))))();

((FUNC)(*((int*)(*((int*)(&c) + 2)) + 1)))();

}

多继承下的虚函数表------->有几个基类就有几张虚函数表-----派生类的虚函数存放在声明的基类的虚函数表中。

1:首先要声明基类的虚函数表

该类的成员变量(int a);

2:然后再声明基类虚的函数表

该类的成员变量(int b);

3:派生类中与基类造成覆盖的-->分别占据相应的位置-->C::func分别位于二个基类的虚函数形成覆盖。

4:派生类中未覆盖的,放在首先声明的基类的虚函数后面----->C::funC。

多继承条件下,基类指针指向派生类后,基类指针所能访问的函数

//多继承条件下,基类指针指向派生类后,基类指针所能访问的函数

void test1()

{

C c;

A *pa = &c;

B *pb = &c;

C *pc = &c;

cout << "基类指针pa所能调用的函数:" << endl;

pa->func();

pa->funcA();

//pa->funcB();error:提示类A没有成员funcB、funcC  -->受到类型的限制

//pa->funcC();error

cout << "基类指针pb所能调用的函数:" << endl;

pb->func();

pb->funcB();

//pb->funcA();error

//pb->funcC();error

cout << "派生类指针pc所能调用的函数:" << endl;

pc->func();

pc->funcA();

pc->funcB();

pc->funcC();

}

多继承条件下,基类指针指向派生类对象后,基类指针之间强制类型转化之后,所能访问的函数

void test2()

{

C c;

A *pa = &c;

B *pb = &c;

C *pc = &c;

pa = reinterpret_cast<A *>(pb);

pa->func();

pa->funcA();

//pb = reinterpret_cast<B *>(pa);

//pb->func();

//pb->funcB();

//pa = reinterpret_cast<A *>(pc);

//pa->func();

//pa->funcA();

}

如果喜欢,欢迎关注微信公众号:神秘编程   一起讨论学习

专注Linux C/C++和算法知识学习分享总结

                   欢迎关注交流共同进步

上一篇 下一篇

猜你喜欢

热点阅读