C++对象模型(单继承下的虚表)

注:以下操作都是在VS(2013)的32位平台下完成的,各个平台有些许差异

所谓多态,其实就是多种形态

C++中虚函数的主要所用就是为了实现多态,当父类指针/引用调用重写虚函数,当父类的指针/引用指向父类时,调用父类的虚函数,指向子类时,调用子类的虚函数.    ->    这就是所谓的与类型无关,只与对象有关.

举个栗子:

实现一个购买门票的场景,学生应该买学生票,成人应该买成人票

class Person  
{  
public:  
    virtual void BuyTickets()  
    {  
        cout << "Person::成人票" << endl;  
    }  
};  
  
class Student : public Person  
{  
public:  
    virtual void BuyTickets()  
    {  
        cout << "Student::学生票" << endl;  
    }  
};  
  
void func(Person* p)  
{  
    p->BuyTickets();  
}  
  
int main()  
{  
    Person per;  
    Person* p = &per;  
    func(p);  
  
    Student s;  
    p = &s;  
    func(p);  
    return 0;  
}  

这时候我们希望p指向的是学生,就去调用学生类的BuyTickets函数,p指向的是成人,就去调用成人类的BuyTickets函数



探究虚函数表

虚函数表是通过一块连续内存来存储虚函数的地址。这张表解决了继承、虚函数(重写)的问题。在有虚函数的对象实例中都存在一张虚函数表,虚函数表就像一张地图,指明了实际应该调用的虚函数函数。

下面我们举一个简单的栗子

class A  
{  
public:  
    virtual void func1()  
    {  
        cout << "A::func1()" << endl;  
    }  
protected:  
    int _a;  
};  
  
class B : public A  
{  
public:  
    virtual void func1()  
    {  
        cout << "B::func1()" << endl;  
    }  
    virtual void func2()  
    {  
        cout << "B::func2()" << endl;  
    }  
protected:  
    int _b;  
};  
  
int main()  
{  
    A a;  
    B b;  
    return 0;  
}  

class B 共有继承class A,并且两个类中的func1函数构成多态,class B中还有一个自己的虚函数

我们知道,成员函数都是存在常量区的,虚函数也不例外,只是在类中有一个指针,他指向了一张表的开始,这张表像是一个指针数组,表中的每一个元素都存储了一个地址,也就是当前类中虚函数的地址,数组以NULL结尾(vs中)

下面我们写一个函数将这些虚函数都打印出来

typedef void(*V_FUNC)();  
  
void PrintVTable(int* vtable)  
{  
    printf("vtable : %p\n", vtable);  
    for (int i = 0; vtable[i] != NULL; ++i)  
    {  
        printf("vfunc[%d] : %p ", i, vtable[i]);  
        V_FUNC f = (V_FUNC)vtable[i];  
        f();  
    }  
    cout << "--------------------------------" << endl;  
}  

代码中的 typedef void(*V_FUNC)();是定义了一个函数指针,为了便于我们查看打印的结果,在最后通过函数指针调用以下当前函数,打印出对应的结果

下面是完整代码和运行结果

class A  
{  
public:  
    virtual void func1()  
    {  
        cout << "A::func1()" << endl;  
    }  
protected:  
    int _a;  
};  
  
class B : public A  
{  
public:  
    virtual void func1()  
    {  
        cout << "B::func1()" << endl;  
    }  
    virtual void func2()  
    {  
        cout << "B::func2()" << endl;  
    }  
protected:  
    int _b;  
};  
  
typedef void(*V_FUNC)();  
  
void PrintVTable(int* vtable)  
{  
    printf("vtable : %p\n", vtable);  
    for (int i = 0; vtable[i] != NULL; ++i)  
    {  
        printf("vfunc[%d] : %p ", i, vtable[i]);  
        V_FUNC f = (V_FUNC)vtable[i];  
        f();  
    }  
    cout << "--------------------------------" << endl;  
}  
  
int main()  
{  
    A a;  
    PrintVTable((int*)*((int*)&a));  
    B b;  
    PrintVTable((int*)*((int*)&b));  
    return 0;  
} 

再举一个复杂一点点的栗子

class A  
{  
public:  
    virtual void func1()  
    {  
        cout << "A::func1()" << endl;  
    }  
    virtual void func2()  
    {  
        cout << "A::func2()" << endl;  
    }  
protected:  
    int _a;  
};  
  
class B : public A  
{  
public:  
    virtual void func1()  
    {  
        cout << "B::func1()" << endl;  
    }  
    virtual void func3()  
    {  
        cout << "B::func3()" << endl;  
    }  
protected:  
    int _b;  
};  

这里的class A类里有两个虚函数,分别为func1,func2,func1被class B的func1重写,class B中也有两个虚函数,func1,func3



下面我们来打印虚表验证一下


上面的操作都是基于32位操作系统的,在64位操作系统下,打印虚表的函数可以修改为

typedef void(*V_FUNC)();

void PrintVTable(int** vtable)
{
	printf("vtable : %p\n", vtable);
	for (int i = 0; vtable[i] != NULL; ++i)
	{
		printf("vfunc[%d] : %p ", i, vtable[i]);
		V_FUNC f = (V_FUNC)vtable[i];
		f();
	}
	cout << "--------------------------------" << endl;
}

调用方法也应该改为

	A a;
	PrintVTable((int**)(*((int**)&a)));
	B b;
	PrintVTable((int**)(*((int**)&b)));


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值