由于转载了另外的转载,且原文暂时未找到,此处无法列出原文地址。
在C语言中,如果对一个指针做类型转换,不会改变这个指针的值,改变的只是对指针的解释方式。但是在C++中,由于一些特性的引入,在对指针做类型转换时,编译器有时不得不对指针做一个偏移,以支持这些特性。下面将具体讨论这些情况。
1. 由虚函数引起的指针偏移
通常在有虚函数的类中,编译器会安插一个vptr,但是对vptr的位置C++语言并未做出明确的规定,就目前的实现来看,有的编译器将vptr放在类的开头,如microsoft的c++编译器,而有的编译器将vptr放在类的末尾,如cfront。
对于将vptr放在类的开头这种编译器,我们考虑如下两个类:
class X { int i; }; class Y : public X { int j; public: virtual void vf(); };
X的内存布局很简单,由于它没有虚函数,所以没有vptr,只有一个int类型的变量i;
而Y的内存布局可能像这样:
vptr |
int i |
int j |
Y y;
X* pX = &y;
编译器为了保证将Y类型的指针转换为X类型的指针后,指向的是一个有效的X对象,它将不得不做一个偏移,实际的代码可能像这样:
X* pX = (X*)((char*)&y + sizeof(vptr))
2. 由多重继承引起的指针偏移
首先需要说明一下多重继承的内存布局,虽然C++语言未明确做出规定,但目前的多数实现会按照基类声明的顺序依次将每个基类的数据成员顺次放入派生类。例如:
class A { int i; }; class B { int j; }; class C : public A, public B { int k };
C的内存布局可能像这样:
int i |
int j |
int k |
C c;
B* pb = &c;
猜想实际的代码可能是:
B* pb = (B*)((char*)&c + sizeof(A));
对于例子中的情况,上面的猜想是成立的,但对于一般的情况就不成立了。在将C类型的指针转换的时候,这个C类型的指针有可能为空,按上面的转换思路,那空指针也得加一个偏移量了,为了避免这种情况,实际的代码可能会是这样:
B* pb = (pc) ? (B*)((char*)pc + sizeof(A)) : 0; //假设pc是一个指向C的指针
3.由虚继承引起的指针偏移
对 于一般的继承(这里仅指单继承),在不涉及虚函数的情况下,由于编译器的实现多会将基类数据成员排在派生类之前,所以从派生类指针转换为基类指针时,一般 不需要做偏移。但是对于虚继承,情况就不一样了。由于虚基类的地址是在运行时确定的,所以由派生类的指针转换为虚基类的指针时必然会引起偏移。虚基类有多 种实现方式,所以在这儿就先不举例了。可以参考<Inside the C++ Object Model>.