准确了解对象在内存中的映射方式
understanding exactly how an object is mapped in memory
我阅读了 this question 和其他一些关于 on object 布局的内容,但我仍然不明白它到底是什么样子。
这是我的具体问题:
对于每个 class(意味着如果我有 2 个超级classed 我将有 2 个指针),虚函数有 1 vtable pointer
给他们。它在对象内部的什么位置?假设如下:class A{void virtual f(){}; int x;};
对象 A a
的地址是否与 a.x
或 a.f
[ 的地址相同或者可能指向默认方法,例如 C-tor / D-tor
Incorrect,因为 class 方法未存储在对象内部正如所解释的 here]
示例:
class A{
int x;
void f(){}
void virtual g(){}
void virtual h(){}
};
A a;
std::cout << sizeof a; // result = 8
class A{
int x;
void f(){}
void virtual g(){}
};
A a;
std::cout << sizeof a; // result = 8
class A{
int x;
void f(){}
//void virtual g(){}
};
A a;
std::cout << sizeof a; // result = 4
从这些例子可以看出,当遇到一定数量(n > 0)的虚函数时,会在对象中添加一个指针(4字节,在我的32位机器上)。它会被添加到其他数据成员之前吗?
指向什么:
A a;
int *p = (int*)&a;
我用这个检查了一下。从以下假设 vtable 指针总是先于其他 class 成员是否正确?:
class A{
public:
A(int y){x=y;}
virtual void g(){}
int x;
virtual void f(){}
};
int main ()
{
A a(42);
int *p = (int*)&a;
std::cout << *p << std::endl; // = 4215116 (vtable address?)
std::cout << *(p+1) << std::endl; // = 42
return 0;
}
这完全取决于实现(编译器),大多数实现倾向于将 vptr 作为第一个元素插入。由于它是对象地址的第一个元素和开头,因此虚拟函数调用的间接调用将更容易,因为没有进一步的偏移计算来识别 vptr。之前在Whosebug上问过类似的问题,发现下面这个很有用。
我阅读了 this question 和其他一些关于 on object 布局的内容,但我仍然不明白它到底是什么样子。
这是我的具体问题:
对于每个 class(意味着如果我有 2 个超级classed 我将有 2 个指针),虚函数有 1 vtable pointer
给他们。它在对象内部的什么位置?假设如下:class A{void virtual f(){}; int x;};
对象 A a
的地址是否与 a.x
或 a.f
[ 的地址相同或者可能指向默认方法,例如 Incorrect,因为 class 方法未存储在对象内部正如所解释的 here]C-tor / D-tor
示例:
class A{
int x;
void f(){}
void virtual g(){}
void virtual h(){}
};
A a;
std::cout << sizeof a; // result = 8
class A{
int x;
void f(){}
void virtual g(){}
};
A a;
std::cout << sizeof a; // result = 8
class A{
int x;
void f(){}
//void virtual g(){}
};
A a;
std::cout << sizeof a; // result = 4
从这些例子可以看出,当遇到一定数量(n > 0)的虚函数时,会在对象中添加一个指针(4字节,在我的32位机器上)。它会被添加到其他数据成员之前吗?
指向什么:
A a;
int *p = (int*)&a;
我用这个检查了一下。从以下假设 vtable 指针总是先于其他 class 成员是否正确?:
class A{
public:
A(int y){x=y;}
virtual void g(){}
int x;
virtual void f(){}
};
int main ()
{
A a(42);
int *p = (int*)&a;
std::cout << *p << std::endl; // = 4215116 (vtable address?)
std::cout << *(p+1) << std::endl; // = 42
return 0;
}
这完全取决于实现(编译器),大多数实现倾向于将 vptr 作为第一个元素插入。由于它是对象地址的第一个元素和开头,因此虚拟函数调用的间接调用将更容易,因为没有进一步的偏移计算来识别 vptr。之前在Whosebug上问过类似的问题,发现下面这个很有用。