标签:链接 创建 ++ 超出 入口 存在 lse 时机 不同
类:有虚函数,会产生一个虚函数表;
类对象:有一个指针,指针vptr会指向虚函数表的开始地址;
虚函数表位于整个对象模型的顶端;
// objModel.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <iostream>
using namespace std;
class A
{
public:
virtual void func() {};
public:
int i;
};
int main()
{
A a;
cout << sizeof(a) << endl;
char* p = reinterpret_cast<char*>(&a);
char* q = reinterpret_cast<char*>(&a.i);
if (p == q) //如果二者位置相同,说明i在对象的上面;虚函数表指针vptr在下面;
cout << "yes" << endl;
else
cout << "no" << endl;
return 0;
}
// objModel.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <iostream>
using namespace std;
class Base
{
public:
virtual void func()
{
cout << "Base::void func()" << endl;
}
virtual void gunc()
{
cout << "Base::void gunc()" << endl;
}
virtual void hunc()
{
cout << "Base::void hunc()" << endl;
}
};
class Derive :public Base
{
public:
virtual void gunc()
{
cout << "Derive::void hunc()" << endl;
}
};
int main()
{
//直接用类名和用对象一样,都是这个类的对象的大小
cout << sizeof(Base) << endl; //父类 4字节
cout << sizeof(Derive) << endl; //子类 4字节
Derive* d = new Derive(); //派生类指针
long* pvptr = (long*)d; //指向对象的指针d转成了long*类型
long* vptr = (long*)(*pvptr); //(*pvptr)表示pvptr所指向的对象,也就是Derive本身, Derive对象是4字节,代表的是虚函数表指针
for (int i = 0; i <= 2; i++) //循环3次 打印虚函数的入口地址
{
printf("vptr[%d] = 0x:%p\n", i, vptr[i]);
}
typedef void(*Func)(void); //定义一个函数指针类型
Func f = (Func)vptr[0]; //f是函数指针变量,vptr[0]是指向第一个虚函数;
Func g = (Func)vptr[1]; //f是函数指针变量,vptr[1]是指向第二个虚函数;
Func h = (Func)vptr[2]; //f是函数指针变量,vptr[2]是指向第三个虚函数;
f();
g();
h();
cout << "---------------------------------------------------------------------" << endl;
Base* b = new Base(); //基类指针
long* pvptr_base = (long*)b; //指向对象的指针b转成了long*类型
long* vptr_base = (long*)(*pvptr_base);
for (int i = 0; i <= 2; i++) //循环3次 打印虚函数的入口地址
{
printf("Base[%d] = 0x:%p\n", i, vptr_base[i]);
}
Func f_base = (Func)vptr_base[0]; //f_base是函数指针变量,vptr_base[0]是指向第一个虚函数;
Func g_base = (Func)vptr_base[1]; //g_base是函数指针变量,vptr_base[1]是指向第二个虚函数;
Func h_base = (Func)vptr_base[2]; //h_base是函数指针变量,vptr_base[2]是指向第三个虚函数;
f_base();
g_base();
h_base();
return 0;
}
一个类只有包含虚函数才存在虚函数表,同属于一个类的对象共享虚函数表,但是有各自的vptr(虚函数指针),所指向的虚函数的入口地址都是相同的;
// objModel.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <iostream>
using namespace std;
class Base
{
public:
virtual void func()
{
cout << "Base::void func()" << endl;
}
virtual void gunc()
{
cout << "Base::void gunc()" << endl;
}
virtual void hunc()
{
cout << "Base::void hunc()" << endl;
}
};
class Derive :public Base
{
public:
virtual void gunc()
{
cout << "Derive::void hunc()" << endl;
}
};
int main()
{
//直接用类名和用对象一样,都是这个类的对象的大小
cout << sizeof(Base) << endl; //父类 4字节
cout << sizeof(Derive) << endl; //子类 4字节
Derive* d = new Derive(); //派生类指针
long* pvptr = (long*)d; //指向对象的指针d转成了long*类型
long* vptr = (long*)(*pvptr); //(*pvptr)表示pvptr所指向的对象,也就是Derive本身, Derive对象是4字节,代表的是虚函数表指针
for (int i = 0; i <= 2; i++) //循环3次 打印虚函数的入口地址
{
printf("vptr[%d] = 0x:%p\n", i, vptr[i]);
}
Derive* d_2 = new Derive(); //派生类指针
long* pvptr_2 = (long*)d_2; //指向对象的指针d转成了long*类型
long* vptr_2 = (long*)(*pvptr_2); //(*pvptr)表示pvptr所指向的对象,也就是Derive本身, Derive对象是4字节,代表的是虚函数表指针
for (int i = 0; i <= 2; i++) //循环3次 打印虚函数的入口地址
{
printf("vptr_2[%d] = 0x:%p\n", i, vptr_2[i]);
}
return 0;
}
父类中有虚函数,子类中有虚函数;即父类中有虚函数表,子类中肯定也有虚函数表;
无论是父类还是子类都会只有一个虚函数表,不能认为子类中有一个虚函数表 + 父类中一个虚函数表,得到子类中有两个虚函数表;
子类中是否可能有多个虚函数表?
如果子类中完全没有新的虚函数,则我们可以认为子类的虚函数表和父类的虚函数表内容相同;但仅仅是内容相同,这两个表在内存中处于不同位置;
虚函数表中每一项,保存一个虚函数的入口地址,如果子类的虚函数表某项和父类虚函数表某项相同(这表示子类没有覆盖父类的虚函数);
超出虚函数表部分内容不可知;无实际意义;
// objModel.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//基类不同对象虚函数指针
//基类和父类对象 虚函数指针
#include <iostream>
using namespace std;
class Base
{
public:
virtual void func()
{
cout << "Base::void func()" << endl;
}
virtual void gunc()
{
cout << "Base::void gunc()" << endl;
}
virtual void hunc()
{
cout << "Base::void hunc()" << endl;
}
};
class Derive :public Base
{
public:
virtual void gunc()
{
cout << "Derive::void hunc()" << endl;
}
};
int main()
{
//直接用类名和用对象一样,都是这个类的对象的大小
cout << sizeof(Base) << endl; //父类 4字节
cout << sizeof(Derive) << endl; //子类 4字节
Derive derive; //__vfptr = 0x00f89b90 {objModel.exe!void(* Derive::`vftable‘[4])()} {0x00f812da {objModel.exe!Base::func(void)}, ...}
long* p_vptr_derive = (long*)(&derive); //指向对象的指针d转成了long*类型
long* vptr_derive = (long*)(*p_vptr_derive); //(*pvptr)表示pvptr所指向的对象,也就是Derive本身, Derive对象是4字节,代表的是虚函数表指针
typedef void(*Func)(void); //定义一个函数指针类型
Func f = (Func)vptr_derive[0]; //f是函数指针变量,vptr_derive[0]是指向第一个虚函数;
Func g = (Func)vptr_derive[1]; //f是函数指针变量,vptr_derive[1]是指向第二个虚函数;
Func h = (Func)vptr_derive[2]; //f是函数指针变量,vptr_derive[2]是指向第三个虚函数;
Derive derive_2 = derive; //调用拷贝构造函数 __vfptr = 0x00f89b90 {objModel.exe!void(* Derive::`vftable‘[4])()} {0x00f812da {objModel.exe!Base::func(void)}, ...}
long* p_vptr_derive_2 = (long*)(&derive_2); //指向对象的指针d转成了long*类型
long* vptr_derive_2 = (long*)(*p_vptr_derive_2); //(*pvptr)表示pvptr所指向的对象,也就是Derive本身, Derive对象是4字节,代表的是虚函数表指针
Func f_2 = (Func)vptr_derive_2[0]; //f是函数指针变量,vptr_derive[0]是指向第一个虚函数;
Func g_2 = (Func)vptr_derive_2[1]; //f是函数指针变量,vptr_derive[1]是指向第二个虚函数;
Func h_2 = (Func)vptr_derive_2[2]; //f是函数指针变量,vptr_derive[2]是指向第三个虚函数;
//直接用子类对象给父类对象值,子类中属于分类的那部分内容会被编译器自动区分出来,并拷贝给了父类对象
//所以Base base = derive; 实际上做了两件事
//1. 生成一个base对象
//2. 用derive来初始化base对象的值,编译器做了选择,derive的虚函数表指针值并没有覆盖base对象的虚函数表指针值;
Base base = derive; //直接用子类对象给父类对象值 __vfptr = 0x00f89b34 {objModel.exe!void(* Base::`vftable‘[4])()} {0x00f812da {objModel.exe!Base::func(void)}, ...}
long* p_vptr_base = (long*)(&base); //指向对象的指针d转成了long*类型
long* vptr_base = (long*)(*p_vptr_base); //(*
Func f_base = (Func)vptr_base[0]; //f是函数指针变量,vptr_derive[0]是指向第一个虚函数;
Func g_base = (Func)vptr_base[1]; //f是函数指针变量,vptr_derive[1]是指向第二个虚函数;
Func h_base = (Func)vptr_base[2]; //f是函数指针变量,vptr_derive[2]是指向第三个虚函数;
//面向对象oo和基于对象ob
// 1. C++通过类的指针和引用来支持多态,就是面向对象OO;
// 2. OB,也叫ADT抽象数据模型,不支持多态,执行速度更快,因为函数调用的解析不需要运行时决定(没有多态)
// 因为函数调用的解析不需要运行时决定(没有多态),而是在编译器期间就解析完成,内存空间紧凑程度上更紧凑,因为没有虚函数指针和虚函数表这些概念;
// 但是OB的设计灵活性就差;
//C++既支持OO,有支持OB;
return 0;
}
一个对象,如果它的类有多个基类则有多个虚函数表指针(注意是两个虚函数表指针,而不是两个虚函数表);
在多继承中,对应各个基类的vptr按照继承顺序依次放置在类的内存空间中,且子类与第一个基类公用一个vptr(第二个基类有自己的vptr);
子类对象有两个虚函数表指针,vptr1和vptr2;
类Derived有两个虚函数表,因为它继承自两个基类;
子类和第一个基类公用一个vptr(应为vptr指向一个虚函数表,所以也可以说子类和第一个基类公用一个虚函数表vtb1)
因为我们注意到类Dreived的虚函数表1里面的5个函数u,g正好是基类1里面的函数;
子类中的虚函数覆盖了父类中的同名虚函数;
#include <iostream>
using namespace std;
class Base_1
{
public:
virtual void func()
{
cout << "Base_1::void func()" << endl;
}
virtual void gunc()
{
cout << "Base_1::void gunc()" << endl;
}
};
class Base_2
{
public:
virtual void hunc()
{
cout << "Base_2::void func()" << endl;
}
virtual void iunc()
{
cout << "Base_2::void gunc()" << endl;
}
};
class Derived :public Base_1, public Base_2
{
public:
virtual void func()
{
cout << "Derived::void func()" << endl;
}
virtual void iunc()
{
cout << "Derived::void iunc()" << endl;
}
//自己的虚函数
virtual void munc()
{
cout << "Derived::void munc()" << endl;
}
virtual void nunc()
{
cout << "Derived::void nunc()" << endl;
}
virtual void junc()
{
cout << "Derived::void junc()" << endl;
}
};
int main()
{
//多重继承虚函数表分析
cout << sizeof(Base_1) << endl;
cout << sizeof(Base_2) << endl;
cout << sizeof(Derived) << endl;
Derived derive;
Base_1& b_1 = derive;
Base_2& b_2 = derive;
Derived& d = derive;
typedef void(*Func)(void);
long* p_derived_1 = (long*)(&derive);
long* vptr_1 = (long*)(*p_derived_1); //取第一个虚函数表指针
long* p_derived_2 = p_derived_1 + 1;
long* vptr_2 = (long*)(*p_derived_2); //取第二个虚函数表指针
Func f_1_1 = (Func)vptr_1[0]; //f_1_1 = 0x0039146f {objModel.exe!Derived::func(void)}
Func f_1_2 = (Func)vptr_1[1]; //f_1_2 = 0x00141460 {objModel.exe!Base_1::gunc(void)}
Func f_1_3 = (Func)vptr_1[2]; //f_1_3 = 0x00141483 {objModel.exe!Derived::munc(void)}
Func f_1_4 = (Func)vptr_1[3]; //f_1_4 = 0x00141492 {objModel.exe!Derived::nunc(void)}
Func f_1_5 = (Func)vptr_1[4]; //f_1_5 = 0x0014146a {objModel.exe!Derived::junc(void)}
Func f_2_1 = (Func)vptr_2[0]; //f_2_1 = 0x00141479 {objModel.exe!Base_2::hunc(void)}
Func f_2_2 = (Func)vptr_2[1]; //f_2_2 = 0x0014145b {objModel.exe!Derived::iunc(void)}
f_1_1();
f_1_2();
f_1_3();
f_1_4();
f_1_5();
cout << "-----------------------------" << endl;
f_2_1();
f_2_2();
//
return 0;
}
辅助工具: **cl.exe ** 编译链接工具
标签:链接 创建 ++ 超出 入口 存在 lse 时机 不同
原文地址:https://www.cnblogs.com/Trevo/p/13367558.html