DATA 语义学
这段代码输出什么?
#include <iostream>
using namespace std;
class A{ public:A(){ac='s';}private:char ac;};
class B:virtual public A{public:B(){a='e';}char a; };
class C:virtual public A{ };
class D:public B,public C
{
public:
D():A(),B(),C(){b=13;}
int b;
};
int main(){
D d;
cout<<"sizeof(A)="<<sizeof(A)<<endl;
cout<<"sizeof(B)="<<sizeof(B)<<endl;
cout<<"sizeof(C)="<<sizeof(C)<<endl;
cout<<"sizeof(D)="<<sizeof(D)<<endl;
cout<<endl;
cout<<"address of A's subobject in d"<<(A*)&d<<endl;
cout<<"address of B's subobject in d"<<(B*)&d<<endl;
cout<<"address of C's subobject in d"<<(C*)&d<<endl;
cout<<"address of D's subobject in d"<<(D*)&d<<endl;
cout<<endl;
int* p = (int*)(*((int*)&d));
cout<<"address of b's virtual base table="<<p<<endl;
cout<<"first member in b's virtual base table="<<*p<<endl;
cout<<"second member in b's virtual base table="<<*(p+1)<<endl;
cout<<"third member in b's virtual base table="<<*(p+2)<<endl;
cout<<endl;
p= (int*)*((int*)((C*)&d));
cout<<"address of c's virtual base class table= "<<p<<endl;
cout<<"first member in c's virtual base table="<< *p<<endl;
cout<<"second member in c's virtual base table="<<*(p+1)<<endl;
cout<<"third member in c's virtual base table="<<*(p+2)<<endl;
char *pchar= (char*)(&d)+4; //char型加4 注意A中的ac其实是私有变量,B不应该可以访问到的,实际上通过强制转换可以非法触及她-。-
cout<<*pchar<<endl;
cout<<*(pchar+12)<<endl;
B b;
int *pint =(int*)(&b)+1; //int型+1
cout<<*((char*)(pint))<<endl;
pint = (int*)(&d)+3;
cout<<*(pint)<<endl;
}
结果是:
sizeof(A)=1
sizeof(B)=9
sizeof(C)=5
sizeof(D)=17
address of A's subobject in d0012FF74
address of B's subobject in d0012FF64
address of C's subobject in d0012FF6C
address of D's subobject in d0012FF64
address of b's virtual base table=00403350
first member in b's virtual base table=0
second member in b's virtual base table=16
third member in b's virtual base table=0
address of c's virtual base class table= 00403358
first member in c's virtual base table=0
second member in c's virtual base table=8
third member in c's virtual base table=0
e
s
e
13
1.语言本身造成的负担:如virtual baseclass
2.对齐造成的负担(对齐会单独开题讨论)
3.编译器优化处理 A虽然是空的 but 为了让A的两个object在内存中得到不同的地址,编译器给他加上了一个byte,但是B和C都没有这一个byte呢?这是编译器做了优化,允许省掉这一个byte
看上面的代码”:
环境是vs2008 对齐设置为4字节
A的大小为1 因为有一个char 没有对齐因为不是结构型对象,如果A没有这个char大小依然是1的
B的大小为9 首先开始是它的virtual base class ptr,4个字节的指针,然后是他自己的member 1个char 此时为了保证其对象完整性编译器对齐到4字节处也就是在第九个字节内放入基类的member
这样如果我们把A=B B赋给A,传输可以从整4字节开始割出A即可
C的大小是5 没什么好解释
D的大小是17首先是B的8字节(4字节vbptr+1字节char member+3字节对齐)然后是C的4字节vbptr,然后是自己的member4字节最后是1字节的base class member,可以看到B和C的base class table中的项都是自己位置与这个member的offset 值
不同编译器可能结果不同的,因为c++ standard 并没有强制规定 base class subobjects的顺序等
data member 是程序执行过程中的某种状态:
static data member 是整个class 的状态
non-static data member 是个别class-object 的状态
c++对象尽量以空间优化和存取速度的考虑来表现non-static members ,并且和c的struct 数据配置的兼容性。
static data member 放在程序的一个global data segment 中,不会影响个别的class-object 大小
,在class没有object 时已经存在,但是template中有些不同
-----DATA member 的绑定
始终把nested type 声明 放在class 起始处,argument list 中的名称会在第一次遇到时候被适当的决议完成,因此extern 和nested type names 之间的非直觉绑定操作还是会发生。
---- DATA 的存取
Point3d origin,*pt=&origin;
origin.x = 0.0; 和 pt->x=0.0 ; 有什么区别么??
如果x是静态data member 则完全没有区别 因为他们都在data segment 中和object无关
~nonstatic data member---------
如果point3d不包含虚拟继承那么没有差异
否则我们不能确定pt中必然指向哪一种因此不能在编译器确定offset需要一些运行时候的计算抉择,而origin则不同一定是某一个类型的所以没有问题
多继承或者单继承都不会带来访问上的影响,因为他们都可以向c的结构体那样在编译时期确定各个member的offset。即使是多继承pt指向第二个baseclass的data,由于member的位置在编译时期就已经固定了,因此存取member只是一个offset运算,像单一继承那样简单,不管是指针,reference或者object都一样
只有virtual base class 会带来一些损耗,因为他使得对象模型变得复杂了
如果我们在一个类中加入了virtual func 会发生什么~~~
1. 会产生一个virtual table,存放每一个virtual func地址以及rtti支持的type_info
2.class object 内都加入一个vptr,提供执行期的链接
3.加强ctor 设定vpr初值
4.加强dtor 消除vptr 从dirived class 到 base class
虚拟继承:
他必须支持某种形式的“shared subobject”继承
那么一个object会分成一个不变局部和一个共享局部的数据,共享局部就是virtual base class subobject,他的位置会因为每次派生操作发生变化(例如一个virtual base class subobject的位置在不同级别的继承体系中的位置是不确定的,不像多继承单继承那样有固定的offset),所以他只能间接存取,因此产生了效率缺损.(间接是指的是他只能首先读出他的指针,然后根据指针的内容取到他)
所以在虚拟继承基类中最好不要有data member
-------指向DATAmember 的指针
#include <iostream>
using namespace std;
class A
{
public:
int a;
};
int main(){
int A::* p=&A::a;
cout<<p<<endl;
}
输出 1
因为为了防止&A::a和 int A::*a = 0;一样把他加了1。
虚拟继承带来的主要冲击是,妨碍了优化的有效性,因为每一层虚拟继承都带来了一个额外层次的间接性,在编译器中存取 类似point::x的操作pb.*bx
会被转化为 &pb->vbcPoint+bx
而不是转换成 &pB +bx
额外的间接性会降低“把所有操作都搬移到缓存器中执行”优化能力,也就是降低了局部性~