整理一下继承关系中类的内存模型,算是自己做得一个小测试,有了解的希望可以指正一下文中可能出现的错误。
测试环境windows,g++7.4.0。
普通的类中可能包含的元素有:普通成员变量,静态成员变量,成员函数,静态成员函数,虚函数。
其中能够影响内存模型的有:普通成员变量,虚函数(虚函数表指针)。当然C++的内存对齐机制也会使得对象的大小改变。
#include
using namespace std;
class book
{
private:
int val;
char ch_val;
static int static_val;
public:
book()
{
val = 4;
ch_val = 3;
cout<<"book constructer function\n";
}
~book()
{
cout<<"book deconstructer function\n";
}
static void static_function(){}
void ptr_ostream()
{
cout<<"size of object book\t" << sizeof(*this)<<endl;
cout<<"address of object book\t" << this<<endl;
cout<<"address of val\t"<<&val<<endl;
cout<<"address of ch_val\t"<<&ch_val<<endl;
cout<<"address of static_val\t"<<&book::static_val<<endl;
cout<<"address of static_function\t" << &book::static_function<<endl;
}
};
int book::static_val = 0;
int main()
{
book b;
b.ptr_ostream();
return 0;
}
上面book
类的输出结果是:
book constructer function
size of object book 8
address of object book 00000054A772F8D8
address of val 00000054A772F8D8
address of ch_val 00000054A772F8DC
address of static_val 00007FF670FCA4F4
address of static_function 00007FF670FB1866
book deconstructer function
第二行大小为8字节包含一个int
4字节,一个char
占1个字节,因为内存对齐的关系,最终的大小是8字节。
第三行的对象地址和第一个成员变量的地址相同。
第5行可以看到静态变量和静态函数的地址和类相距比较远,因为他们属于当前类,而不是属于某单个实例。
通过g++ -fdump-class-hierarchy main.cpp
可以看到类的结构,整体大小为5字节,内存对齐之后为8字节。
Class book
size=8 align=4
base size=5 base align=4
book (0x0x6fffe70d8c0) 0
为book
添加两个虚函数virtual_function_rst
和virtual_function_snd
。
#include
using namespace std;
class book
{
private:
int val;
char ch_val;
static int static_val;
public:
book()
{
cout<<"book constructer function\n";
}
~book()
{
cout<<"book deconstructer function\n";
}
static void static_function(){}
virtual void virtual_function_rst()
{
cout<<"book virtual function first\n";
}
virtual void virtual_function_snd()
{
cout<<"book virtual function second\n";
}
void ptr_ostream()
{
cout<<"size of object book\t" << sizeof(*this)<<endl;
cout<<"address of object book\t" << this<<endl;
cout<<"address of val\t"<<&val<<endl;
cout<<"address of static_val\t"<<&book::static_val<<endl;
cout<<"address of static_function\t" << &book::static_function<<endl;
this->virtual_function_rst();
this->virtual_function_snd();
}
};
int book::static_val = 0;
int main()
{
book b;
b.ptr_ostream();
return 0;
}
输出结果为:
book constructer function
size of object book 16
address of object book 00000059180FF648
address of val 00000059180FF650
address of static_val 00007FF629F4A4F4
address of static_function 00007FF629F31866
book virtual function first
book virtual function second
book deconstructer function
通过g++ -fdump-class-hierarchy main.cpp
可以看到类的结构。
Vtable for book
book::_ZTV4book: 4 entries
0 (int (*)(...))0
8 (int (*)(...))(& _ZTI4book)
16 (int (*)(...))book::virtual_function_rst
24 (int (*)(...))book::virtual_function_snd
Class book
size=16 align=8
base size=13 base align=8
book (0x0x6fffe70d8c0) 0
vptr=((& book::_ZTV4book) + 16)
从上面的输出可以看到book
的大小为16,实际大小为13,16是经过内存对齐的。
book
的地址是00000059180FF648
,第一个成员变量地址为00000059180FF650
,可以看到二者相差8个字节,刚好一个指针的大小。这个指针便是虚函数表。该虚函数表包含当前类的所有虚函数的指针。
知道虚函数表指针之后可以通过对地址进行重解析进行函数的访问。如下将函数ptr_ostream
修改为:
void ptr_ostream()
{
cout<<"size of object book\t" << sizeof(*this)<<endl;
cout<<"address of object book\t" << this<<endl;
cout<<"address of val\t"<<&val<<endl;
cout<<"address of static_val\t"<<&book::static_val<<endl;
cout<<"address of static_function\t" << &book::static_function<<endl;
this->virtual_function_rst();
this->virtual_function_snd();
void **vptr = (void**)(int*)*(int**)(this);
cout<<"address of virtual table\t"<<vptr<<endl;
int func_no = 2;
typedef void (*virtual_func)();
for(int i = 0;i < func_no;i++)
{
//virtual_func func = (int)vptr[i];
//func();
virtual_func func = (virtual_func)(int*)vptr[i];
func();
cout<<"the "<<i<<"th virtual function address\t"<<func<<endl;
}
输出结果为:
book constructer function
size of object book 16
address of object book 0000005C03CFFE28
address of val 0000005C03CFFE30
address of static_val 00007FF67AFBA4F4
address of static_function 00007FF67AFA1866
book virtual function first
book virtual function second
address of virtual table 00007FF67AFB40B0
book virtual function first
the 0th virtual function address 00007FF67AFA187A
book virtual function second
可以看到虚函数成功调用。当前类的内存模型如下(当然需要注意的是虚函数表并不是简单的结构,图只是示意图):
重新创建一个类book
作为类science_book
的基类。
#include
using namespace std;
class book
{
private:
int val;
char ch_val;
public:
book()
{
ch_val = 3;
val = 4;
cout<<"book constructer function\n";
}
~book()
{
cout<<"book deconstructer function\n";
}
void ptr_ostream()
{
cout<<"size of object book\t" << sizeof(*this)<<endl;
cout<<"address of object book\t" << this<<endl;
cout<<"address of val\t"<<(int*)&val<<endl;
cout<<"address of ch_val\t"<<(int*)&ch_val<<endl;
}
};
class science_book : public book
{
char sci_val;
public:
science_book()
{
sci_val = 5;
cout<<"science book constructer function\n";
}
~science_book()
{
cout<<"science book deconstructer function\n";
}
void ptr_ostream()
{
book::ptr_ostream();
cout<<"size of object science book\t" << sizeof(*this)<<endl;
cout<<"address of object science book\t" << this<<endl;
cout<<"address of sci_val\t"<<(int*)&sci_val<<endl;
}
};
**构造顺序:**从下面的输出可以看到类的构造销毁顺序是:
book
==>构造子类science_book
;science_book
==>销毁子类book
;book constructer function
science book constructer function
size of object book 8
address of object book 000000AA7238FAC8
address of val 000000AA7238FAC8
address of ch_val 000000AA7238FACC
size of object science book 12
address of object science book 000000AA7238FAC8
address of sci_val 000000AA7238FAD0
science book deconstructer function
book deconstructer function
内存结构结合上面的输出可以看到子类science_book
第一块内存是基类book
,之后子类的内容。
Class book
size=8 align=4
base size=5 base align=4
book (0x0x6fffe70d8c0) 0
Class science_book
size=8 align=4
base size=6 base align=4
science_book (0x0x6fffe53b4c8) 0
book (0x0x6fffe70f000) 0
继承深度超过1的类内存结构和普通的双层继承类似。
#include
using namespace std;
class book
{
private:
int val;
char ch_val;
public:
book()
{
ch_val = 3;
val = 4;
cout<<"book constructer function\n";
}
~book()
{
cout<<"book deconstructer function\n";
}
void ptr_ostream()
{
cout<<"size of object book\t" << sizeof(*this)<<endl;
cout<<"address of object book\t" << this<<endl;
cout<<"address of val\t"<<(int*)&val<<endl;
cout<<"address of ch_val\t"<<(int*)&ch_val<<endl;
}
};
class science_book : public book
{
char sci_val;
public:
science_book()
{
sci_val = 5;
cout<<"science book constructer function\n";
}
~science_book()
{
cout<<"science book deconstructer function\n";
}
void ptr_ostream()
{
book::ptr_ostream();
cout<<"size of object science book\t" << sizeof(*this)<<endl;
cout<<"address of object science book\t" << this<<endl;
cout<<"address of sci_val\t"<<(int*)&sci_val<<endl;
}
};
class task_book : public science_book
{
char tsk_val;
public:
task_book()
{
tsk_val = 5;
cout<<"task book constructer function\n";
}
~task_book()
{
cout<<"task book deconstructer function\n";
}
void ptr_ostream()
{
science_book::ptr_ostream();
cout<<"size of object task book\t" << sizeof(*this)<<endl;
cout<<"address of object task book\t" << this<<endl;
cout<<"address of tsk_val\t"<<(int*)&tsk_val<<endl;
}
};
从下面的输出可以看出其类个构造顺序是先构造基类,再构造派生类。先销毁派生类,在销毁基类。
book constructer function
science book constructer function
task book constructer function
size of object book 8
address of object book 000000725BCFFC48
address of val 000000725BCFFC48
address of ch_val 000000725BCFFC4C
size of object science book 12
address of object science book 000000725BCFFC48
address of sci_val 000000725BCFFC50
size of object task book 16
address of object task book 000000725BCFFC48
address of tsk_val 000000725BCFFC54
task book deconstructer function
science book deconstructer function
book deconstructer function
从下面得到的内存大小和实际输出的内存大小不一致,具体原因不是很清楚。
Class book
size=8 align=4
base size=5 base align=4
book (0x0x6fffe70d8c0) 0
Class science_book
size=8 align=4
base size=6 base align=4
science_book (0x0x6fffe53b4c8) 0
book (0x0x6fffe70f000) 0
Class task_book
size=8 align=4
base size=7 base align=4
task_book (0x0x6fffe53b9a8) 0
science_book (0x0x6fffe53ba10) 0
book (0x0x6fffe70f8a0) 0
新建三个类:book,science_book,task_book
,其中book
是science_book
的基类,science_book
是task_book
的基类。
book
中实现了三个虚函数book_virtual_rst,book_virtual_snd,book_virtual_snd
;science_book
重写了父类的book_virtual_snd,book_virtual_thd
,新添加了一个虚函数sci_book_virtual
;task_book
重写了父类的book_virtual_thd
,新添加了一个虚函数task_virtual
。代码如下:
#include
using namespace std;
class book
{
private:
int val;
char ch_val;
public:
book()
{
ch_val = 3;
val = 4;
cout<<"book constructer function\n";
}
~book()
{
cout<<"book deconstructer function\n";
}
virtual void book_virtual_rst()
{
cout<<"book virtual first!"<<endl;
}
virtual void book_virtual_snd()
{
cout<<"book virtual second!"<<endl;
}
virtual void book_virtual_thd()
{
cout<<"book virtual third!"<<endl;
}
void ptr_ostream()
{
book *ptr = &(*this);
cout<<"\nsize of object book\t" << sizeof(*ptr)<<endl;
cout<<"address of object book\t" << ptr<<endl;
cout<<"address of val\t"<<(int*)&val<<endl;
cout<<"address of ch_val\t"<<(int*)&ch_val<<endl;
void **vptr = (void**)(int*)*(int**)(ptr);
cout<<"book address of virtual table\t"<<vptr<<endl;
int func_no = 5;
typedef void (*virtual_func)();
for(int i = 0;i < func_no;i++)
{
//virtual_func func = (int)vptr[i];
//func();
virtual_func func = (virtual_func)(int*)vptr[i];
func();
cout<<"the "<<i<<"th virtual function address\t"<<func<<endl;
}
}
};
class science_book : public book
{
char sci_val;
public:
science_book()
{
sci_val = 5;
cout<<"science book constructer function\n";
}
~science_book()
{
cout<<"science book deconstructer function\n";
}
virtual void book_virtual_snd()
{
cout<<"science book virtual second!"<<endl;
}
virtual void book_virtual_thd()
{
cout<<"science book virtual third!"<<endl;
}
virtual void sci_book_virtual()
{
cout<<"science book virtual function!"<<endl;
}
void ptr_ostream()
{
book::ptr_ostream();
science_book *ptr = &(*this);
cout<<"\nsize of object science book\t" << sizeof(*ptr)<<endl;
cout<<"address of object science book\t" << ptr<<endl;
cout<<"address of sci_val\t"<<(int*)&sci_val<<endl;
void **vptr = (void**)(int*)*(int**)(ptr);
cout<<"science book address of virtual table\t"<<vptr<<endl;
int func_no = 5;
typedef void (*virtual_func)();
for(int i = 0;i < func_no;i++)
{
//virtual_func func = (int)vptr[i];
//func();
virtual_func func = (virtual_func)(int*)vptr[i];
func();
cout<<"the "<<i<<"th virtual function address\t"<<func<<endl;
}
}
};
class task_book : public science_book
{
char tsk_val;
public:
task_book()
{
tsk_val = 5;
cout<<"task book constructer function\n";
}
~task_book()
{
cout<<"task book deconstructer function\n";
}
virtual void book_virtual_thd()
{
cout<<"task book virtual third!"<<endl;
}
virtual void task_virtual()
{
cout<<"task book virtual function!"<<endl;
}
void ptr_ostream()
{
science_book::ptr_ostream();
task_book *ptr = &(*this);
cout<<"\nsize of object task book\t" << sizeof(*ptr)<<endl;
cout<<"address of object task book\t" << ptr<<endl;
cout<<"address of tsk_val\t"<<(int*)&tsk_val<<endl;
void **vptr = (void**)(int*)*(int**)(ptr);
cout<<"task book address of virtual table\t"<<vptr<<endl;
int func_no = 5;
typedef void (*virtual_func)();
for(int i = 0;i < func_no;i++)
{
//virtual_func func = (int)vptr[i];
//func();
virtual_func func = (virtual_func)(int*)vptr[i];
func();
cout<<"the "<<i<<"th virtual function address\t"<<func<<endl;
}
}
};
从上面构建的类来看book
只包含3个虚函数,science_book
只包含四个虚函数,task_book
而三个类中func_no
都设为5。这个先注意下,下面配合g++
的输出进行解释。
程序的输出结果为:
book constructer function
science book constructer function
task book constructer function
size of object book 16
address of object book 000000ECDDEFFB98
address of val 000000ECDDEFFBA0
address of ch_val 000000ECDDEFFBA4
book address of virtual table 00007FF78B793FF8
book virtual first!
the 0th virtual function address 00007FF78B7818D9
science book virtual second!
the 1th virtual function address 00007FF78B7818CF
task book virtual third!
the 2th virtual function address 00007FF78B7818C5
science book virtual function!
the 3th virtual function address 00007FF78B7818B1
task book virtual function!
the 4th virtual function address 00007FF78B7818B6
size of object science book 24
address of object science book 000000ECDDEFFB98
address of sci_val 000000ECDDEFFBA8
science book address of virtual table 00007FF78B793FF8
book virtual first!
the 0th virtual function address 00007FF78B7818D9
science book virtual second!
the 1th virtual function address 00007FF78B7818CF
task book virtual third!
the 2th virtual function address 00007FF78B7818C5
science book virtual function!
the 3th virtual function address 00007FF78B7818B1
task book virtual function!
the 4th virtual function address 00007FF78B7818B6
size of object task book 32
address of object task book 000000ECDDEFFB98
address of tsk_val 000000ECDDEFFBB0
task book address of virtual table 00007FF78B793FF8
book virtual first!
the 0th virtual function address 00007FF78B7818D9
science book virtual second!
the 1th virtual function address 00007FF78B7818CF
task book virtual third!
the 2th virtual function address 00007FF78B7818C5
science book virtual function!
the 3th virtual function address 00007FF78B7818B1
task book virtual function!
the 4th virtual function address 00007FF78B7818B6
task book virtual third!
task book deconstructer function
science book deconstructer function
book deconstructer function
下面是g++
输出的虚函数表的内容,和最终测试出来的内容有一定冲突并不是完全准确:
Vtable for book
book::_ZTV4book: 5 entries
0 (int (*)(...))0
8 (int (*)(...))(& _ZTI4book)
16 (int (*)(...))book::book_virtual_rst
24 (int (*)(...))book::book_virtual_snd
32 (int (*)(...))book::book_virtual_thd
Class book
size=16 align=8
base size=13 base align=8
book (0x0x6fffe70d8c0) 0
vptr=((& book::_ZTV4book) + 16)
Vtable for science_book
science_book::_ZTV12science_book: 6 entries
0 (int (*)(...))0
8 (int (*)(...))(& _ZTI12science_book)
16 (int (*)(...))book::book_virtual_rst
24 (int (*)(...))science_book::book_virtual_snd
32 (int (*)(...))science_book::book_virtual_thd
40 (int (*)(...))science_book::sci_book_virtual
Class science_book
size=16 align=8
base size=14 base align=8
science_book (0x0x6fffe53b598) 0
vptr=((& science_book::_ZTV12science_book) + 16)
book (0x0x6fffe70f1e0) 0
primary-for science_book (0x0x6fffe53b598)
Vtable for task_book
task_book::_ZTV9task_book: 7 entries
0 (int (*)(...))0
8 (int (*)(...))(& _ZTI9task_book)
16 (int (*)(...))book::book_virtual_rst
24 (int (*)(...))science_book::book_virtual_snd
32 (int (*)(...))task_book::book_virtual_thd
40 (int (*)(...))science_book::sci_book_virtual
48 (int (*)(...))task_book::task_virtual
Class task_book
size=16 align=8
base size=15 base align=8
task_book (0x0x6fffe53bb48) 0
vptr=((& task_book::_ZTV9task_book) + 16)
science_book (0x0x6fffe53bbb0) 0
primary-for task_book (0x0x6fffe53bb48)
book (0x0x6fffe70fc60) 0
primary-for science_book (0x0x6fffe53bbb0)
从内存结构中可以看到整个类中只包含一个虚函数表指针,虚函数表中会将子类中重写的方法的函数地址替换掉父类中的虚函数地址。上面每个ptr_ostream
函数中使用5是为了验证整个类中只有一个虚函数指针。
既然如此,是否可以通过父类的指针访问子类的函数。
task_book tk;
book* ptr = &tk;
ptr->task_virtual();
实际上,上面的代码无法通过编译期,虽然仅仅从运行时来讲是可以的,但是编译器进行类型检查会报错。
另外猜测一下g++
输出的虚函数表的内容和实际不同的原因可能是:该输出只是每个类的内存结构,而实际测试中是针对实际的类实例的,即测试得到的是类实例的内存结构。也就是说如果实例化一个book
或者science_book
的对象,得到的虚函数表结构应该和该输出结果类似。
构建三个基础类:camera,communicator,player
,一个子类phone
继承自上面的三个类。
#include
using namespace std;
class communicator
{
int com_val;
char com_ch;
public:
communicator()
{
cout<<"call communicator constructor!\n";
}
~communicator()
{
cout<<"call communicator desctructor!\n";
}
void ptr_ostream()
{
communicator* ptr = &(*this);
cout<<endl;
cout<<"size of object communicator\t" << sizeof(*ptr)<<endl;
cout<<"address of object communicator\t" << ptr<<endl;
cout<<"address of com_val\t"<<(int*)&com_val<<endl;
cout<<"address of com_ch\t"<<(int*)&com_ch<<endl;
}
};
class camera
{
char cam_ch;
public:
camera()
{
cout<<"call camera constructor!\n";
}
~camera()
{
cout<<"call camera desctructor!\n";
}
void ptr_ostream()
{
camera* ptr = &(*this);
cout<<endl;
cout<<"size of object camera\t" << sizeof(*ptr)<<endl;
cout<<"address of object camera\t" << ptr<<endl;
cout<<"address of cam_ch\t"<<(int*)&cam_ch<<endl;
}
};
class player
{
char play_ch;
public:
player()
{
cout<<"call player constructor!\n";
}
~player()
{
cout<<"call player desctructor!\n";
}
void ptr_ostream()
{
player* ptr = &(*this);
cout<<endl;
cout<<"size of object player\t" << sizeof(*ptr)<<endl;
cout<<"address of object player\t" << ptr<<endl;
cout<<"address of play_ch\t"<<(int*)&play_ch<<endl;
}
};
class phone : public communicator, public camera, public player
{
char ph_ch;
public:
phone()
{
cout<<"call phone constructor!\n";
}
~phone()
{
cout<<"call phone desctructor!\n";
}
void ptr_ostream()
{
communicator::ptr_ostream();
camera::ptr_ostream();
player::ptr_ostream();
phone* ptr = &(*this);
cout<<endl;
cout<<"size of object phone\t" << sizeof(*ptr)<<endl;
cout<<"address of object phone\t" << ptr<<endl;
cout<<"address of play_ch\t"<<(int*)&ph_ch<<endl;
}
};
上面结果的输出如下:
call communicator constructor!
call camera constructor!
call player constructor!
call phone constructor!
size of object communicator 8
address of object communicator 000000BEC44FFBB8
address of com_val 000000BEC44FFBB8
address of com_ch 000000BEC44FFBBC
size of object camera 1
address of object camera 000000BEC44FFBC0
address of cam_ch 000000BEC44FFBC0
size of object player 1
address of object player 000000BEC44FFBC1
address of play_ch 000000BEC44FFBC1
size of object phone 12
address of object phone 000000BEC44FFBB8
address of play_ch 000000BEC44FFBC2
call phone desctructor!
call player desctructor!
call camera desctructor!
call communicator desctructor!
通过g++ -fdump-class-hierarchy
得到的内存模型如下:
Class communicator
size=8 align=4
base size=5 base align=4
communicator (0x0x6fffe70d8c0) 0
Class camera
size=1 align=1
base size=1 base align=1
camera (0x0x6fffe70ee80) 0
Class player
size=1 align=1
base size=1 base align=1
player (0x0x6fffe70f5a0) 0
Class phone
size=8 align=4
base size=8 base align=4
phone (0x0x6fffe321860) 0
communicator (0x0x6fffe70fb40) 0
camera (0x0x6fffe70fba0) 5
player (0x0x6fffe70fc00) 6
现在给上面的每个类添加两个虚函数,子类phone
重新其中一个:
communicator
添加com_vir_rst和com_vir_snd
;camera
添加cam_vir_rst和cam_vir_snd
;player
添加play_vir_rst和play_vir_snd
;phone
添加ph_virtual
,重写com_vir_rst, play_vir_rst, cam_vir_rst
。#include
using namespace std;
class communicator
{
int com_val;
char com_ch;
public:
communicator()
{
cout<<"call communicator constructor!\n";
}
~communicator()
{
cout<<"call communicator desctructor!\n";
}
virtual void com_vir_rst()
{
cout<<"communicator virtual first!\n";
}
virtual void com_vir_snd()
{
cout<<"communicator virtual second!\n";
}
void ptr_ostream()
{
communicator* ptr = &(*this);
cout<<endl;
cout<<"size of object communicator\t" << sizeof(*ptr)<<endl;
cout<<"address of object communicator\t" << ptr<<endl;
cout<<"address of com_val\t"<<(int*)&com_val<<endl;
cout<<"address of com_ch\t"<<(int*)&com_ch<<endl;
void **vptr = (void**)(int*)*(int**)(ptr);
cout<<"communicator address of virtual table\t"<<vptr<<endl;
int func_no = 6;
typedef void (*virtual_func)();
for(int i = 0;i < func_no;i++)
{
if(i == 3) continue;
virtual_func func = (virtual_func)(int*)vptr[i];
func();
cout<<"the "<<i<<"th virtual function address\t"<<func<<endl;
}
}
};
class camera
{
char cam_ch;
public:
camera()
{
cout<<"call camera constructor!\n";
}
~camera()
{
cout<<"call camera desctructor!\n";
}
virtual void cam_vir_rst()
{
cout<<"camera virtual first!\n";
}
virtual void cam_vir_snd()
{
cout<<"camera virtual second!\n";
}
void ptr_ostream()
{
camera* ptr = &(*this);
cout<<endl;
cout<<"size of object camera\t" << sizeof(*ptr)<<endl;
cout<<"address of object camera\t" << ptr<<endl;
cout<<"address of cam_ch\t"<<(int*)&cam_ch<<endl;
void **vptr = (void**)(int*)*(int**)(ptr);
cout<<"camera address of virtual table\t"<<vptr<<endl;
int func_no = 2;
typedef void (*virtual_func)();
for(int i = 0;i < func_no;i++)
{
virtual_func func = (virtual_func)(int*)vptr[i];
func();
cout<<"the "<<i<<"th virtual function address\t"<<func<<endl;
}
}
};
class player
{
char play_ch;
public:
player()
{
cout<<"call player constructor!\n";
}
~player()
{
cout<<"call player desctructor!\n";
}
virtual void play_vir_rst()
{
cout<<"player virtual first!\n";
}
virtual void play_vir_snd()
{
cout<<"player virtual second!\n";
}
void ptr_ostream()
{
player* ptr = &(*this);
cout<<endl;
cout<<"size of object player\t" << sizeof(*ptr)<<endl;
cout<<"address of object player\t" << ptr<<endl;
cout<<"address of play_ch\t"<<(int*)&play_ch<<endl;
void **vptr = (void**)(int*)*(int**)(ptr);
cout<<"player address of virtual table\t"<<vptr<<endl;
int func_no = 2;
typedef void (*virtual_func)();
for(int i = 0;i < func_no;i++)
{
virtual_func func = (virtual_func)(int*)vptr[i];
func();
cout<<"the "<<i<<"th virtual function address\t"<<func<<endl;
}
}
};
class phone : public communicator, public camera, public player
{
char ph_ch;
public:
phone()
{
cout<<"call phone constructor!\n";
}
~phone()
{
cout<<"call phone desctructor!\n";
}
virtual void ph_virtual()
{
cout<<"phone virtual!\n";
}
virtual void com_vir_rst()
{
cout<<"phone communicator virtual first!\n";
}
virtual void cam_vir_rst()
{
cout<<"phone camera virtual first!\n";
}
virtual void play_vir_rst()
{
cout<<"phone player virtual first!\n";
}
void ptr_ostream()
{
communicator::ptr_ostream();
camera::ptr_ostream();
player::ptr_ostream();
phone* ptr = &(*this);
cout<<endl;
cout<<"size of object phone\t" << sizeof(*ptr)<<endl;
cout<<"address of object phone\t" << ptr<<endl;
cout<<"address of play_ch\t"<<(int*)&ph_ch<<endl;
void **vptr = (void**)(int*)*(int**)(ptr);
cout<<"phone address of virtual table\t"<<vptr<<endl;
int func_no = 3;
typedef void (*virtual_func)();
for(int i = 0;i < func_no;i++)
{
virtual_func func = (virtual_func)(int*)vptr[i];
func();
cout<<"the "<<i<<"th virtual function address\t"<<func<<endl;
}
}
};
code输出结果如下:
call communicator constructor!
call camera constructor!
call player constructor!
call phone constructor!
size of object communicator 16
address of object communicator 000000BA45DCF7C8
address of com_val 000000BA45DCF7D0
address of com_ch 000000BA45DCF7D4
communicator address of virtual table 00007FF666DA3FC0
phone communicator virtual first!
the 0th virtual function address 00007FF666D9191F
communicator virtual second!
the 1th virtual function address 00007FF666D91929
phone virtual!
the 2th virtual function address 00007FF666D9191A
phone player virtual first!
the 4th virtual function address 00007FF666D91938
player virtual second!
the 5th virtual function address 00007FF666D91947
size of object camera 16
address of object camera 000000BA45DCF7D8
address of cam_ch 000000BA45DCF7E0
camera address of virtual table 00007FF666DA3E98
phone camera virtual first!
the 0th virtual function address 00007FF666D9193D
camera virtual second!
the 1th virtual function address 00007FF666D9192E
size of object player 16
address of object player 000000BA45DCF7E8
address of play_ch 000000BA45DCF7F0
player address of virtual table 00007FF666DA3FE0
phone player virtual first!
the 0th virtual function address 00007FF666D91938
player virtual second!
the 1th virtual function address 00007FF666D91947
size of object phone 56
address of object phone 000000BA45DCF7C8
address of play_ch 000000BA45DCF7F8
phone address of virtual table 00007FF666DA3FC0
phone communicator virtual first!
the 0th virtual function address 00007FF666D9191F
communicator virtual second!
the 1th virtual function address 00007FF666D91929
phone virtual!
the 2th virtual function address 00007FF666D9191A
call phone desctructor!
call player desctructor!
call camera desctructor!
call communicator desctructor!
通过g++
参数得到的类结构如下:
Vtable for communicator
communicator::_ZTV12communicator: 4 entries
0 (int (*)(...))0
8 (int (*)(...))(& _ZTI12communicator)
16 (int (*)(...))communicator::com_vir_rst
24 (int (*)(...))communicator::com_vir_snd
Class communicator
size=16 align=8
base size=13 base align=8
communicator (0x0x6fffe70d8c0) 0
vptr=((& communicator::_ZTV12communicator) + 16)
Vtable for camera
camera::_ZTV6camera: 4 entries
0 (int (*)(...))0
8 (int (*)(...))(& _ZTI6camera)
16 (int (*)(...))camera::cam_vir_rst
24 (int (*)(...))camera::cam_vir_snd
Class camera
size=16 align=8
base size=9 base align=8
camera (0x0x6fffe70f540) 0
vptr=((& camera::_ZTV6camera) + 16)
Vtable for player
player::_ZTV6player: 4 entries
0 (int (*)(...))0
8 (int (*)(...))(& _ZTI6player)
16 (int (*)(...))player::play_vir_rst
24 (int (*)(...))player::play_vir_snd
Class player
size=16 align=8
base size=9 base align=8
player (0x0x6fffe70ff60) 0
vptr=((& player::_ZTV6player) + 16)
Vtable for phone
phone::_ZTV5phone: 15 entries
0 (int (*)(...))0
8 (int (*)(...))(& _ZTI5phone)
16 (int (*)(...))phone::com_vir_rst
24 (int (*)(...))communicator::com_vir_snd
32 (int (*)(...))phone::ph_virtual
40 (int (*)(...))phone::cam_vir_rst
48 (int (*)(...))phone::play_vir_rst
56 (int (*)(...))-16
64 (int (*)(...))(& _ZTI5phone)
72 (int (*)(...))phone::_ZThn16_N5phone11cam_vir_rstEv
80 (int (*)(...))camera::cam_vir_snd
88 (int (*)(...))-32
96 (int (*)(...))(& _ZTI5phone)
104 (int (*)(...))phone::_ZThn32_N5phone12play_vir_rstEv
112 (int (*)(...))player::play_vir_snd
Class phone
size=48 align=8
base size=42 base align=8
phone (0x0x6fffe321b30) 0
vptr=((& phone::_ZTV5phone) + 16)
communicator (0x0x6fffe2806c0) 0
primary-for phone (0x0x6fffe321b30)
camera (0x0x6fffe280720) 16
vptr=((& phone::_ZTV5phone) + 72)
player (0x0x6fffe280780) 32
vptr=((& phone::_ZTV5phone) + 104)
可以看到多继承体系中每个基类都会有一个虚函数表指针,每个虚函数表会将子类重写的函数替换掉基类中的虚函数指针,并且会在第一个继承类中即communicator
中将子类的虚函数指针添加到该表中。另外可以看到途中communicator
中还有额外的player::play_vir_rst
等两个函数指针,这是我通过调试得到的结果,不知道具体含义为何!(紫色部分为RTTI类型相关信息)
菱形继承,这里创建四个类hardware,camera,communicator,phone
:
hardware
作为公共基类,拥有两个成员变量had_ch
和had_val
,三个虚函数had_rst,had_snd,had_thd
;camera
的基类为hardware
,拥有一个成员变量cam_ch
,和两个虚函数cam_rst,cam_snd
,重写基类虚函数had_rst
;communicator
的基类为hardware
,拥有一个成员变量com_ch
,和两个虚函数com_rst,com_snd
,重写基类虚函数had_rst
;phone
的基类为communicator
和camera
,拥有一个成员变量ph_ch
,和一个虚函数ph_virtual
,重写基类中的虚函数cam_rst,com_rst
;实现代码如下:
#include
using namespace std;
class hardware
{
char hd_ch;
int hd_val;
public:
hardware()
{
cout<<"call hardware constructor!\n";
}
~hardware()
{
cout<<"call hardware destructor!\n";
}
virtual void had_rst()
{
cout<<"hardware virtual first!\n";
}
virtual void had_snd()
{
cout<<"hardware virtual second!\n";
}
virtual void had_thd()
{
cout<<"hardware virtual third!\n";
}
void ptr_ostream(int func_no)
{
hardware* ptr = &(*this);
cout<<endl;
cout<<"size of object hardware\t" << sizeof(*ptr)<<endl;
cout<<"address of object hardware\t" << ptr<<endl;
cout<<"address of hd_ch\t"<<(int*)&hd_ch<<endl;
cout<<"address of hd_val\t"<<(int*)&hd_val<<endl;
void **vptr = (void**)(int*)*(int**)(ptr);
cout<<"hardware address of virtual table\t"<<vptr<<endl;
typedef void (*virtual_func)();
for(int i = 0;i < func_no;i++)
{
virtual_func func = (virtual_func)(int*)vptr[i];
func();
cout<<"the "<<i<<"th virtual function address\t"<<func<<endl;
}
}
};
class communicator : public hardware
{
char com_ch;
public:
communicator()
{
cout<<"call communicator constructor!\n";
}
~communicator()
{
cout<<"call communicator desctructor!\n";
}
virtual void had_rst()
{
cout<<"communicator hadware virtual first!\n";
}
virtual void com_rst()
{
cout<<"communicator virtual first!\n";
}
virtual void com_snd()
{
cout<<"communicator virtual second!\n";
}
void ptr_ostream()
{
communicator* ptr = &(*this);
cout<<endl;
cout<<"size of object communicator\t" << sizeof(*ptr)<<endl;
cout<<"address of object communicator\t" << ptr<<endl;
cout<<"address of com_ch\t"<<(int*)&com_ch<<endl;
}
};
class camera : public hardware
{
char cam_ch;
public:
camera()
{
cout<<"call camera constructor!\n";
}
~camera()
{
cout<<"call camera desctructor!\n";
}
virtual void had_rst()
{
cout<<"camera hadware virtual first!\n";
}
virtual void cam_rst()
{
cout<<"camera virtual first!\n";
}
virtual void cam_snd()
{
cout<<"camera virtual second!\n";
}
void ptr_ostream()
{
camera* ptr = &(*this);
cout<<endl;
cout<<"size of object camera\t" << sizeof(*ptr)<<endl;
cout<<"address of object camera\t" << ptr<<endl;
cout<<"address of cam_ch\t"<<(int*)&cam_ch<<endl;
void **vptr = (void**)(int*)*(int**)(ptr);
cout<<"camera address of virtual table\t"<<vptr<<endl;
}
};
class phone : public communicator, public camera
{
char ph_ch;
public:
phone()
{
cout<<"call phone constructor!\n";
}
~phone()
{
cout<<"call phone desctructor!\n";
}
virtual void ph_virtual()
{
cout<<"phone virtual!\n";
}
virtual void com_rst()
{
cout<<"phone communicator virtual first!\n";
}
virtual void cam_rst()
{
cout<<"phone camera virtual first!\n";
}
void ptr_ostream()
{
communicator::hardware::ptr_ostream(6);
camera::hardware::ptr_ostream(5);
communicator::ptr_ostream();
camera::ptr_ostream();
phone* ptr = &(*this);
cout<<endl;
cout<<"size of object phone\t" << sizeof(*ptr)<<endl;
cout<<"address of object phone\t" << ptr<<endl;
cout<<"address of play_ch\t"<<(int*)&ph_ch<<endl;
}
};
输出结果为:
call hardware constructor!
call communicator constructor!
call hardware constructor!
call camera constructor!
call phone constructor!
size of object hardware 16
address of object hardware 000000BB654FFC48
address of hd_ch 000000BB654FFC50
address of hd_val 000000BB654FFC54
hardware address of virtual table 00007FF7E08CC420
communicator hadware virtual first!
the 0th virtual function address 00007FF7E08C1127
hardware virtual second!
the 1th virtual function address 00007FF7E08C1299
hardware virtual third!
the 2th virtual function address 00007FF7E08C124E
phone communicator virtual first!
the 3th virtual function address 00007FF7E08C1005
communicator virtual second!
the 4th virtual function address 00007FF7E08C101E
phone virtual!
the 5th virtual function address 00007FF7E08C100A
size of object hardware 16
address of object hardware 000000BB654FFC60
address of hd_ch 000000BB654FFC68
address of hd_val 000000BB654FFC6C
hardware address of virtual table 00007FF7E08CC3C0
camera hadware virtual first!
the 0th virtual function address 00007FF7E08C11EA
hardware virtual second!
the 1th virtual function address 00007FF7E08C1299
hardware virtual third!
the 2th virtual function address 00007FF7E08C124E
phone camera virtual first!
the 3th virtual function address 00007FF7E08C1217
camera virtual second!
the 4th virtual function address 00007FF7E08C1186
size of object communicator 24
address of object communicator 000000BB654FFC48
address of com_ch 000000BB654FFC58
size of object camera 24
address of object camera 000000BB654FFC60
address of cam_ch 000000BB654FFC70
camera address of virtual table 00007FF7E08CC3C0
size of object phone 56
address of object phone 000000BB654FFC48
address of play_ch 000000BB654FFC78
call phone desctructor!
call camera desctructor!
call hardware destructor!
call communicator desctructor!
call hardware destructor!
通过g++参数分析结构直接报错:
G:\tmp\tmp>g++ -fdump-class-hierarchy vvpmult.hpp
vvpmult.hpp: In member function 'void phone::ptr_ostream()':
vvpmult.hpp:169:46: error: 'hardware' is an ambiguous base of 'phone'
communicator::hardware::ptr_ostream(6);
^
vvpmult.hpp:170:40: error: 'hardware' is an ambiguous base of 'phone'
camera::hardware::ptr_ostream(5);
虚继承将两个类声明修改virtual public
并且删除had_rst
函数的重写,因为会出现二义性:
class communicator : virtual public hardware
{
...
};
class camera : virtual public hardware
{
...
};
#include
using namespace std;
class hardware
{
char hd_ch;
int hd_val;
public:
hardware()
{
cout<<"call hardware constructor!\n";
}
~hardware()
{
cout<<"call hardware destructor!\n";
}
virtual void had_rst()
{
cout<<"hardware virtual first!\n";
}
virtual void had_snd()
{
cout<<"hardware virtual second!\n";
}
virtual void had_thd()
{
cout<<"hardware virtual third!\n";
}
void ptr_ostream(int func_no)
{
hardware* ptr = &(*this);
cout<<endl;
cout<<"size of object hardware\t" << sizeof(*ptr)<<endl;
cout<<"address of object hardware\t" << ptr<<endl;
cout<<"address of hd_ch\t"<<(int*)&hd_ch<<endl;
cout<<"address of hd_val\t"<<(int*)&hd_val<<endl;
void **vptr = (void**)(int*)*(int**)(ptr);
cout<<"hardware address of virtual table\t"<<vptr<<endl;
typedef void (*virtual_func)();
for(int i = 0;i < 3;i++)
{
virtual_func func = (virtual_func)(int*)vptr[i];
func();
cout<<"the "<<i<<"th virtual function address\t"<<func<<endl;
}
}
};
class communicator : virtual public hardware
{
char com_ch;
public:
communicator()
{
cout<<"call communicator constructor!\n";
}
~communicator()
{
cout<<"call communicator desctructor!\n";
}
virtual void com_rst()
{
cout<<"communicator virtual first!\n";
}
virtual void com_snd()
{
cout<<"communicator virtual second!\n";
}
void ptr_ostream()
{
communicator* ptr = &(*this);
cout<<endl;
cout<<"size of object communicator\t" << sizeof(*ptr)<<endl;
cout<<"address of object communicator\t" << ptr<<endl;
cout<<"address of com_ch\t"<<(int*)&com_ch<<endl;
void **vptr = (void**)(int*)*(int**)(ptr);
cout<<"hardware address of virtual table\t"<<vptr<<endl;
typedef void (*virtual_func)();
for(int i = 0;i < 3;i++)
{
virtual_func func = (virtual_func)(int*)vptr[i];
func();
cout<<"the "<<i<<"th virtual function address\t"<<func<<endl;
}
}
};
class camera : virtual public hardware
{
char cam_ch;
public:
camera()
{
cout<<"call camera constructor!\n";
}
~camera()
{
cout<<"call camera desctructor!\n";
}
virtual void cam_rst()
{
cout<<"camera virtual first!\n";
}
virtual void cam_snd()
{
cout<<"camera virtual second!\n";
}
void ptr_ostream()
{
camera* ptr = &(*this);
cout<<endl;
cout<<"size of object camera\t" << sizeof(*ptr)<<endl;
cout<<"address of object camera\t" << ptr<<endl;
cout<<"address of cam_ch\t"<<(int*)&cam_ch<<endl;
void **vptr = (void**)(int*)*(int**)(ptr);
cout<<"hardware address of virtual table\t"<<vptr<<endl;
typedef void (*virtual_func)();
for(int i = 0;i < 2;i++)
{
virtual_func func = (virtual_func)(int*)vptr[i];
func();
cout<<"the "<<i<<"th virtual function address\t"<<func<<endl;
}
}
};
class phone : public communicator, public camera
{
char ph_ch;
public:
phone()
{
cout<<"call phone constructor!\n";
}
~phone()
{
cout<<"call phone desctructor!\n";
}
virtual void ph_virtual()
{
cout<<"phone virtual!\n";
}
virtual void com_rst()
{
cout<<"phone communicator virtual first!\n";
}
virtual void cam_rst()
{
cout<<"phone camera virtual first!\n";
}
void ptr_ostream()
{
this->hardware::ptr_ostream(3);
communicator::ptr_ostream();
camera::ptr_ostream();
phone* ptr = &(*this);
cout<<endl;
cout<<"size of object phone\t" << sizeof(*ptr)<<endl;
cout<<"address of object phone\t" << ptr<<endl;
cout<<"address of play_ch\t"<<(int*)&ph_ch<<endl;
}
};
Vtable for hardware
hardware::_ZTV8hardware: 5 entries
0 (int (*)(...))0
8 (int (*)(...))(& _ZTI8hardware)
16 (int (*)(...))hardware::had_rst
24 (int (*)(...))hardware::had_snd
32 (int (*)(...))hardware::had_thd
Class hardware
size=16 align=8
base size=16 base align=8
hardware (0x0x6fffe70d8c0) 0
vptr=((& hardware::_ZTV8hardware) + 16)
Vtable for communicator
communicator::_ZTV12communicator: 13 entries
0 16
8 (int (*)(...))0
16 (int (*)(...))(& _ZTI12communicator)
24 (int (*)(...))communicator::com_rst
32 (int (*)(...))communicator::com_snd
40 0
48 0
56 0
64 (int (*)(...))-16
72 (int (*)(...))(& _ZTI12communicator)
80 (int (*)(...))hardware::had_rst
88 (int (*)(...))hardware::had_snd
96 (int (*)(...))hardware::had_thd
VTT for communicator
communicator::_ZTT12communicator: 2 entries
0 ((& communicator::_ZTV12communicator) + 24)
8 ((& communicator::_ZTV12communicator) + 80)
Vtable for camera
camera::_ZTV6camera: 13 entries
0 16
8 (int (*)(...))0
16 (int (*)(...))(& _ZTI6camera)
24 (int (*)(...))camera::cam_rst
32 (int (*)(...))camera::cam_snd
40 0
48 0
56 0
64 (int (*)(...))-16
72 (int (*)(...))(& _ZTI6camera)
80 (int (*)(...))hardware::had_rst
88 (int (*)(...))hardware::had_snd
96 (int (*)(...))hardware::had_thd
VTT for camera
camera::_ZTT6camera: 2 entries
0 ((& camera::_ZTV6camera) + 24)
8 ((& camera::_ZTV6camera) + 80)
Class camera
size=32 align=8
base size=9 base align=8
camera (0x0x6fffe53be20) 0
vptridx=0 vptr=((& camera::_ZTV6camera) + 24)
hardware (0x0x6fffe280000) 16 virtual
vptridx=8 vbaseoffset=-24 vptr=((& camera::_ZTV6camera) + 80)
Vtable for phone
phone::_ZTV5phone: 20 entries
0 32
8 (int (*)(...))0
16 (int (*)(...))(& _ZTI5phone)
24 (int (*)(...))phone::com_rst
32 (int (*)(...))communicator::com_snd
40 (int (*)(...))phone::ph_virtual
48 (int (*)(...))phone::cam_rst
56 16
64 (int (*)(...))-16
72 (int (*)(...))(& _ZTI5phone)
80 (int (*)(...))phone::_ZThn16_N5phone7cam_rstEv
88 (int (*)(...))camera::cam_snd
96 0
104 0
112 0
120 (int (*)(...))-32
128 (int (*)(...))(& _ZTI5phone)
136 (int (*)(...))hardware::had_rst
144 (int (*)(...))hardware::had_snd
152 (int (*)(...))hardware::had_thd
Construction vtable for communicator (0x0x6fffe53c1c8 instance) in phone
phone::_ZTC5phone0_12communicator: 13 entries
0 32
8 (int (*)(...))0
16 (int (*)(...))(& _ZTI12communicator)
24 (int (*)(...))communicator::com_rst
32 (int (*)(...))communicator::com_snd
40 0
48 0
56 0
64 (int (*)(...))-32
72 (int (*)(...))(& _ZTI12communicator)
80 (int (*)(...))hardware::had_rst
88 (int (*)(...))hardware::had_snd
96 (int (*)(...))hardware::had_thd
Construction vtable for camera (0x0x6fffe53c230 instance) in phone
phone::_ZTC5phone16_6camera: 13 entries
0 16
8 (int (*)(...))0
16 (int (*)(...))(& _ZTI6camera)
24 (int (*)(...))camera::cam_rst
32 (int (*)(...))camera::cam_snd
40 0
48 0
56 0
64 (int (*)(...))-16
72 (int (*)(...))(& _ZTI6camera)
80 (int (*)(...))hardware::had_rst
88 (int (*)(...))hardware::had_snd
96 (int (*)(...))hardware::had_thd
VTT for phone
phone::_ZTT5phone: 7 entries
0 ((& phone::_ZTV5phone) + 24)
8 ((& phone::_ZTC5phone0_12communicator) + 24)
16 ((& phone::_ZTC5phone0_12communicator) + 80)
24 ((& phone::_ZTC5phone16_6camera) + 24)
32 ((& phone::_ZTC5phone16_6camera) + 80)
40 ((& phone::_ZTV5phone) + 136)
48 ((& phone::_ZTV5phone) + 80)
Class phone
size=48 align=8
base size=26 base align=8
phone (0x0x6fffe458ce0) 0
vptridx=0 vptr=((& phone::_ZTV5phone) + 24)
communicator (0x0x6fffe53c1c8) 0
primary-for phone (0x0x6fffe458ce0)
subvttidx=8
hardware (0x0x6fffe2808a0) 32 virtual
vptridx=40 vbaseoffset=-24 vptr=((& phone::_ZTV5phone) + 136)
camera (0x0x6fffe53c230) 16
subvttidx=24 vptridx=48 vptr=((& phone::_ZTV5phone) + 80)
hardware (0x0x6fffe2808a0) alternative-path
根据内存地址构建的对象内存结构如下(不同编译器实现不同,windows平台的cl编译器并没有深红色的部分(vs2015),这部分具体是什么,我不是很确定,又看到部分博客说是和虚基类的偏移,具体没有比较权威的资料,不做过多的解释,之后看到相关解释再补充):
可以看到基类只保留了一份内存。