美文网首页
c++多态和虚函数

c++多态和虚函数

作者: 编程小兔崽 | 来源:发表于2018-10-11 08:31 被阅读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++和算法知识学习分享总结

                   欢迎关注交流共同进步

相关文章

  • 深刻剖析之c++博客文章

    三大特性 封装、继承、多态 多态 C++ 虚函数表解析C++多态的实现原理 介绍了类的多态(虚函数和动态/迟绑定)...

  • 查漏补缺

    C++虚函数: 多态: 静态多态(重载)、动态多态(虚函数) 虚函数 虚函数表:编译器为每个类创建了一个虚函数表...

  • 2020-07-06----《C++类的学习》

    函数重载:同名不同参。 C++类的特点:封装、继承、多态。 //多态与函数重载是啥关系? 虚函数:和软件架构相关 ...

  • C++虚函数注意事项以及构成多态的条件

    C++ 虚函数对于多态具有决定性的作用,有虚函数才能构成多态。 虚函数注意事项 只需要在虚函数的声明处加上 vir...

  • 面试题目收集总结

    C++: 多态: 多态性都有哪些?(静态和动态,然后分别叙述了一下虚函数和函数重载) c语言和c++有什么区别?(...

  • C++第六篇多态

    C++中的多态性分为编译时多态性和运行时多态性,编译时多态通过函数重载和模板体现,运行多态通过虚函数体现编译、连接...

  • 语法

    virtual 1.virtual声明的函数实现多态就是通用的多态实现 2.纯虚函数C++的纯虚函数用于表示一个类...

  • C语言模拟实现多态

    C++的多态分为编译时多态的函数重载和运行时的虚函数. 一个基类类型的指针可以指向派生类的对象, 调用虚函数的时候...

  • pwnable.kr之uaf && c++虚函数

    c++的逆向还是要熟悉下。 一、关于c++虚函数 虚函数使得c++能够实现多态,每个类有一个虚表,每个对象在实现的...

  • 深入研究C++多态(虚函数和虚继承)

    面向对象的三大特性是封装、继承和多态。多态是非常重要的一个特性,C++多态基于虚函数和虚继承实现,本文将完整挖掘C...

网友评论

      本文标题:c++多态和虚函数

      本文链接:https://www.haomeiwen.com/subject/qqpvaftx.html