C++ 多态、虚函数机制以及虚函数表

1.非virtual函数,调用规则取决于对象的显式类型。例如

A* a  = new B();

a->display();

调用的就是A类中定义的display()。和对象本体是B无关系。

2.virtual函数,具体调用哪个版本,取决于虚函数表。例如

A* a = new B();

a->v_display();

这个时候,对象a就需要查找自身的虚函数表,表中的v_display()是一个函数指针,可能指向不同类中的对应的v_display函数并调用对应版本的v_display。一般而言,如果B重写了v_display(),那么会调用B的v_display(),如果没有会调用A的display();

3.纯虚函数。结合2分析,纯虚函数在父类是没有定义的,如果子类继承后仍然不重写并定义对应的纯虚函数,那么子类也是抽象类,无法初始化对象,直到有一个类定义了这个虚函数。

这个类才可以初始化对象。并在这个对象上维护这个函数的虚函数表相关数据。

关于虚函数表:

一般存储在一个对象的固定的地址,我们甚至可以通过虚函数表来直接调用某些虚函数。

例如:

A* a = new B();

a就是对象a的地址

将a的地址强制转化成int型指针 (int*)a ,也就是理想化将对象看成int型数组,(因为对象的所有数据是按照类似数组规则的相邻地址块存储的,所以将a转化成int*就好比将一个对象转化成了一组int,每个int其实都是一个地址。)

而上述这些地址中每个地址存储的东西是什么呢?别的不知道,第一个存储的就是虚函数表,里边又是一组地址!我们将第一个地址解引用:

*(int*)(a)

然后得到了一个存储虚函数表指针的首地址,然后再将其强转成int*,这样虚函数表的第一个地址我们也得到了,那就是

(int*)*(int*)(a)

这个地址可以像int[] 一样计算 ,如果+1 就可以获得虚函数表的下一个函数。但是毕竟得到的都是每个函数的地址,所以我们还需要将它解引用:

*(int*)*(int*)(a) 这样就获得真实的函数指针了!到这里就简单了,假如我们调用的函数是void(void)类型的,我们创建一个函数指针类型:

typedef void(*Fun)(void)

这样如果上边从虚函数表中获得的函数指针也是void(void)类型的,我们就可以将其强转为对应的函数指针类型

Fun vfun = (Fun)*(int*)*(int*)(a);

vfun();

哦卖萧的。。。。。。

我这里有一组测试代码:

#include <iostream>
#include <string>

using namespace std;

typedef void(*Fun)(void);

class A
{
private :
    string s;
public :
    virtual void display();
    virtual void v_display();
    virtual void v_0_display() const = 0;
};

class B : public A
{
public:
    void display();
    //void v_display();
    void v_0_display () const;
};

void A::display()
{
    cout << "A::display" << endl;
}

void A::v_display()
{
    cout << "A::v_display" << endl;
}

void B::display()
{
    cout << "B::display" << endl;
}

//void B::v_display()
//{
//    cout << "B::v_display" << endl;
//}

void B::v_0_display() const
{
    cout << "B::v_0_display" << endl;
}

int main(int argc, char* argv[])
{
    A* a = new B();
    B* b = new B();

    Fun vfun = NULL;
    vfun = (Fun)*((int*)*((int*)(a))+0);
    cout << "the address the first virtual function is" << (int*)*((int*)(a) + 0) << endl;
    cout << "------------virtual table-------------" << endl;
    vfun();
    vfun = (Fun)*((int*)*((int*)(a))+1);
    vfun();
    vfun = (Fun)*((int*)*((int*)(a)) + 2);
    vfun();
    cout << "--------------------------------------" << endl;

    a->display();
    a->v_display();
    a->v_0_display();
    ((B*)(a))->display();
    ((B*)(a))->v_display();
    ((B*)(a))->v_0_display();
    b->display();
    b->v_display();
    b->v_0_display();
    ((A*)(b))->display();
    ((A*)(b))->v_display();
    ((A*)(b))->v_0_display();

    system("pause");
}

打印结果为:

the address the first virtual function is002FDC94
------------virtual table-------------
B::display
A::v_display
B::v_0_display
--------------------------------------
B::display
A::v_display
B::v_0_display
B::display
A::v_display
B::v_0_display
B::display
A::v_display
B::v_0_display
B::display
A::v_display
B::v_0_display
请按任意键继续. . .
时间: 2024-11-03 12:52:38

C++ 多态、虚函数机制以及虚函数表的相关文章

多态,虚函数,纯虚函数

1. 虚函数和纯虚函数可以定义在同一个类(class)中,含有纯虚函数的类被称为抽象类(abstract class),而只含有虚函数的类(class)不能被称为抽象类(abstract class). 2. 虚函数可以被直接使用,也可以被子类(sub class)重载以后以多态的形式调用,而纯虚函数必须在子类(sub class)中实现该函数才可以使用,因为纯虚函数在基类(base class)只有声明而没有定义. 3. 虚函数和纯虚函数都可以在子类(sub class)中被重载,以多态的形式

[C/C++] 虚函数机制

转自:c++ 虚函数的实现机制:笔记 1.c++实现多态的方法 其实很多人都知道,虚函数在c++中的实现机制就是用虚表和虚指针,但是具体是怎样的呢?从more effecive c++其中一篇文章里面可以知道:是每个类用了一个虚表,每个类的对象用了一个虚指针.具体的用法如下: class A { public: virtual void f(); virtual void g(); private: int a }; class B : public A { public: void g();

多态实现--虚函数与纯虚函数

多态实现--虚函数与纯虚函数 C++中实现多态是使用虚函数表的方法实现的. 那么具体怎么实现的呢? 举例说明 假设有这样一个多态场景: 有一个基类动物(animal类),动物里面又有两个派生类:猫(cat类)和狗(dog类).现在要求动物类有一个共同的方法:叫声(voice成员函数),但猫和狗叫声是不同的(即:它们的叫声实现方法不同). 那么代码怎么写呢? 多态的代码实现 #include <iostream> using namespace std; //1. 定义一个纯虚函数 class

匹夫细说C#:从园友留言到动手实现C#虚函数机制

前言 上一篇文章匹夫通过CIL代码简析了一下C#函数调用的话题.虽然点击进来的童鞋并不如匹夫预料的那么多,但也还是有一些挺有质量的来自园友的回复.这不,就有一个园友提出了这样一个代码,这段代码如果被编译成CIL代码的话,对虚函数的调用会使用call而非callvirt: override string ToString() { return Base.ToString(); } 至于为何是这样,匹夫在回复中也做了解释,因为上面那段代码其实相当于是这样的: override string ToSt

继承中的虚函数、纯虚函数、普通函数

一.虚函数 被virtual关键字修饰的类成员函数就是虚函数.虚函数的作用就是实现运行时的多态性,将接口与实现分离.简单理解就是相同函数有着不同的实现,但因个体差异而采用不同的策略. 基类中提供虚函数的实现,为派生类提供默认的函数实现.派生类可以重写基类的虚函数以实现派生类的特殊化.如下: class Base{ public: virtual void foo() { cout<<"Base::foo() is called"<<endl; } }; clas

C++ 虚函数与纯虚函数 浅析

[摘要] 本文首先简述虚函数与纯虚函数的定义,然后分析比较两者的区别与联系(DWS). [正文] 1)虚函数与纯虚函数有什么区别? 虚函数,不代表函数为不被实现的函数,为了允许用基类的指针来调用子类的这个函数:允许被其子类重新定义的成员函数. 纯虚函数,才代表函数没有被实现,为了实现一个接口,起到一个规范的作用,规范继承这个类的程序员必须实现这个函数. 2)虚就虚在所谓"推迟联编"或者"动态联编"上,一个类函数的调用并不是在编译时刻被确定的,而是在运行时刻被确定的.

【C++】C++中的虚函数与纯虚函数

C++中的虚函数 先来看一下实际的场景,就很容易明白为什么要引入虚函数的概念.假设我们有一个基类Base,Base中有一个方法eat:有一个派生类Derived从基类继承来,并且覆盖(Override)了基类的eat:继承表明ISA(“是一个”)的关系,现在我们有一个基类的指针(引用)绑定到派生类对象(因为派生类对象是基类的一个特例,我们当然可以用基类指针指向派生类对象),当我们调用pBase->eat()的时候,我们希望调用的是Derived类的eat,而实际上调用的是Base类的eat,测试

C++ 虚函数 、纯虚函数、接口的实用方法和意义

也许之前我很少写代码,更很少写面向对象的代码,即使有写多半也很容易写回到面向过程的老路上去.在写面向过程的代码的时候,根本不管什么函数重载和覆盖,想到要什么功能就变得法子的换个函数名字,心里想想:反正函数重载本质也就是入栈了两个不同的函数. 回过头来讲,让我了解标题这三个概念的实际用处,还是在于我这第四次重写毕业论文的代码,将它改写成面向对象的时候,才理解的.在面向对象设计的过程中, 类是从抽象逐渐具体起来的,父类可以是非常非常抽象的东西,而最终实例化的子类就非常具体了.在这个继承的过程中,不断

C++ 虚函数&amp;纯虚函数&amp;抽象类&amp;接口&amp;虚基类(转)

http://www.cnblogs.com/fly1988happy/archive/2012/09/25/2701237.html 1. 多态 在面向对象语言中,接口的多种不同实现方式即为多态.多态是指,用父类的指针指向子类的实例(对象),然后通过父类的指针调用实际子类的成员函数. 多态性就是允许将子类类型的指针赋值给父类类型的指针,多态是通过虚函数实现的. 多态可以让父类的指针有“多种形态”,这是一种泛型技术.(所谓泛型技术,就是试图使用不变的代码来实现可变的算法). 2. 虚函数 2.1