C++深度解析 C++对象模型分析(下) --- 继承对象模型,多态对象模型,虚函数表,用C语言实现多态(50)
继承对象模型
在C++编译器的内部类可以理解为结构体。
子类是由 父类成员 叠加 子类新成员 得到的。
代码如下:继承对象模型初探
#include <iostream>
#include <string>
using namespace std;
class Demo
{
protected:
int mi;
int mj;
};
class Derived : public Demo
{
int mk;
public:
Derived(int i, int j, int k)
{
mi = i;
mj = j;
mk = k;
}
void print()
{
cout << "mi = " << mi << ", "
<< "mj = " << mj << ", "
<< "mk = " << mk << endl;
}
};
struct Test
{
int mi;
int mj;
int mk;
};
int main()
{
cout << "sizeof(Demo) = " << sizeof(Demo) << endl; // 8
cout << "sizeof(Derived) = " << sizeof(Derived) << endl; // 12
Derived d(1, 2, 3);
//把一个指针类型重新解释为另一个指针类型
Test* p = reinterpret_cast<Test*>(&d);
cout << "Before changing ..." << endl;
d.print();
//通过指针p改变d对象成员变量的值
p->mi = 10;
p->mj = 20;
p->mk = 30;
cout << "After changing ..." << endl;
d.print();
return 0;
}
结果如下:
分析:
成员函数存放在代码段,实际对象只包含成员变量。
继承对象模型,父类成员变量叠加子类新定义成员变量,父类成员变量排在前面,子类新定义成员变量排在后面。
多态对象模型
多态与虚函数的区别:
多态:面向对象理论中的一个概念,相同的行为方式,不同的行为结果,表现多种形态。
虚函数:多态的表现形式由虚函数实现。
C++多态的实现原理
- 当类中声明虚函数时,编译器会在类中生成一个虚函数表
- 虚函数表是一个存储成员函数地址的数据结构
- 虚函数是由编译器自动生成与维护的
- virtual成员函数会被编译器放入虚函数表中
- 存在虚函数时,每个对象中都有一个指向虚函数表的指针
void run(Demo* p, int v)
{
p->add(v);
}
编译器确认run()是否为虚函数:
如果run是虚函数,编译器在对象VPTR所指向的虚函数表中查找add()的地址
如果run不是虚函数,编译器直接可以确定被调用成员函数的地址
代表如下:
#include <iostream>
#include <string>
using namespace std;
class Demo
{
protected:
int mi;
int mj;
public:
//里面存在一个虚函数表指针
virtual void print()
{
cout << "mi = " << mi << ", "
<< "mj = " << mj << endl;
}
};
class Derived : public Demo
{
int mk;
public:
Derived(int i, int j, int k)
{
mi = i;
mj = j;
mk = k;
}
void print()
{
cout << "mi = " << mi << ", "
<< "mj = " << mj << ", "
<< "mk = " << mk << endl;
}
};
struct Test
{
void *p; //指向虚函数表的指针
int mi;
int mj;
int mk;
};
int main()
{
cout << "sizeof(Demo) = " << sizeof(Demo) << endl;
cout << "sizeof(Derived) = " << sizeof(Derived) << endl;
Derived d(1, 2, 3);
//把一个指针类型重新解释为另一个指针类型
Test* p = reinterpret_cast<Test*>(&d);
cout << "Before changing ..." << endl;
d.print();
//通过指针p改变d对象成员变量的值
p->mi = 10;
p->mj = 20;
p->mk = 30;
cout << "After changing ..." << endl;
d.print();
return 0;
}
结果如下:
分析:
当创建对象时,如果类里面有虚函数,最终生成对象时,会被编译器强行塞入一个指针成员变量,这个指针成员变量是不可见的,但是它确实存在,这个指针成员变量指向虚函数表。
用C语言实现面向对象,用C语言实现多态
代码如下:
50-2.h
#ifndef _50_2_H_
#define _50_2_H_
typedef void Demo;
typedef void Derived;//子类
Demo* Demo_Create(int i, int j);
int Demo_GetI(Demo* pThis);
int Demo_GetJ(Demo* pThis);
int Demo_Add(Demo* pThis, int value);
void Demo_Free(Demo* pThis);
//子类成员函数
Derived* Derived_Create(int i, int j, int k);
int Derived_GetK(Derived* pThis);
int Derived_Add(Derived* pThis, int value);//虚函数
#endif
50-2.c
#include "50-2.h"
#include "malloc.h"
//定义一个全局的虚函数,这个函数在当前文件能访问
static int Demo_Virtual_Add(Demo* pThis, int value);
static int Derived_Virtual_Add(Demo* pThis, int value);
//用结构体来表示虚函数表
struct VTable //2. 定义虚函数表数据结构
{
//函数指针
int (*pAdd)(Derived*, int); //3. 虚函数表里面存储什么???
};
struct ClassDemo
{
struct VTable* vptr;//1. 定义虚函数表指针 ==》 虚函数表指针类型???
int mi;
int mj;
};
//父类的成员变量叠加子类的成员变量
struct ClassDerived
{
//最开始的部分为父类
struct ClassDemo d;
int mk;
};
//虚函数表变量,用送她题材
static struct VTable g_Demo_vtbl =
{
Demo_Virtual_Add
};
static struct VTable g_Derived_vtbl =
{
Derived_Virtual_Add
};
Demo* Demo_Create(int i, int j)
{
struct ClassDemo* ret = (struct ClassDemo*)malloc(sizeof(struct ClassDemo));
if( ret != NULL )
{
//指向虚函数表
ret->vptr = &g_Demo_vtbl; //4. 关联对象和虚函数表
ret->mi = i;
ret->mj = j;
}
return ret;
}
int Demo_GetI(Demo* pThis)
{
struct ClassDemo* obj = (struct ClassDemo*)pThis;
return obj->mi;
}
int Demo_GetJ(Demo* pThis)
{
struct ClassDemo* obj = (struct ClassDemo*)pThis;
return obj->mj;
}
//6. 定义虚函数表中指针所指向的具体函数
static int Demo_Virtual_Add(Demo* pThis, int value)
{
struct ClassDemo* obj = (struct ClassDemo*)pThis;
return obj->mi + obj->mj + value;
}
//5. 分析具体的虚函数!!!!
int Demo_Add(Demo* pThis, int value)
{
struct ClassDemo* obj = (struct ClassDemo*)pThis;
//通过对象,找到虚函数表的指针,然后在虚函数表中找到具体调用函数的地址
return obj->vptr->pAdd(pThis, value);
}
void Demo_Free(Demo* pThis)
{
free(pThis);
}
//子类构造函数
Derived* Derived_Create(int i, int j, int k)
{
struct ClassDerived* ret = (struct ClassDerived*)malloc(sizeof(struct ClassDerived));
if( ret != NULL )
{
ret->d.vptr = &g_Derived_vtbl;
//初始化父类
ret->d.mi = i;
ret->d.mj = j;
//初始化子类
ret->mk = k;
}
return ret;
}
int Derived_GetK(Derived* pThis)
{
struct ClassDerived* obj = (struct ClassDerived*)pThis;
return obj->mk;
}
static int Derived_Virtual_Add(Demo* pThis, int value)
{
struct ClassDerived* obj = (struct ClassDerived*)pThis;
return obj->mk + value;
}
int Derived_Add(Derived* pThis, int value)
{
struct ClassDerived* obj = (struct ClassDerived*)pThis;
return obj->d.vptr->pAdd(pThis, value);
}
main.c
#include "stdio.h"
#include "50-2.h"
void run(Demo* p, int v)
{
int r = Demo_Add(p, 3);
printf("r = %d\n", r);
}
int main()
{
//创建父类对象
Demo* pb = Demo_Create(1, 2);
//创建子类对象
Derived* pd = Derived_Create(1, 22, 333);
printf("pb->add(3) = %d\n", Demo_Add(pb, 3));
printf("pd->add(3) = %d\n", Derived_Add(pd, 3));
run(pb, 3);
run(pd, 3);
Demo_Free(pb);
Demo_Free(pd);
return 0;
}
结果如下:
小结:
继承的本质就是父子间成员变量的叠加
C++中的多态是通过虚函数表实现的
虚函数表是由编译器自动生成与维护的
虚函数的调用效率低于普通成员函数