我在接受采访时被问到这些问题。
2
class A
{
private :
int i;
public:
void show()
{
printf("hello");
}
};
int main()
{
A* a = NULL; (what happens in object table?)
A* aa = new A(); (what happens in object table?)
a->show();
aa->show();
delete aa;
return 0;
}
aa和a的确切区别以及对象在内存中的确切行为。
答案 0 :(得分:2)
代码段是只读的,而数据段是读/写的,如果将这两个部分混合在一起,更新数据同时保持代码安全成为一个挑战:一个例子就像Lol4t0所指出的那样:
对于OS内存管理,代码段将被换出 它在文件系统上的原始可执行文件,而数据 被视为更改的段被换出到页面文件。如果你混合它们 在一起,您可能会失去将可执行文件重用为分页文件的优势。
而且,代码段通常会加载到只读(VirtualAlloc(PAGE_READONLY))
的内存页面中a和aa本身只是A *类型的堆栈变量,但是指向NULL,而aa指向堆中分配的对象。
a-> show()被翻译成:
A_show(a)
//which is:
A_show(NULL)
因为在show()中没有引用成员变量,所以这应该可以正常工作。
aa> show()被翻译成:
A_show(aa)
这里aa是一个有效的地址,所以即使你在show()中引用成员变量,也可以。
注意与虚拟函数不同,虚函数在运行时解析,因此每个对象需要一个vptr,成员函数只是普通函数,它将 this 作为第一个参数,并在编译时由编译器解析。
答案 1 :(得分:1)
分开代码的另一个原因&数据是内存管理。
当你获得较低的物理内存时,你可以“忘记”代码页,并在需要时从光盘重新读取它。但是你不能对数据做同样的事情。在这种情况下,您应该将页面移动到swap / page文件。现在,如果你有混合代码&数据页面,您必须将它们保存在交换/页面中,因此该策略可以节省资源。
好吧,我不假装完整。
答案 2 :(得分:1)
我猜第二部分是一个棘手的问题。您会认为会出现分段错误,但由于A::show
不引用该类的任何数据部分,因此它将被优化为二进制代码的一部分(几乎像静态方法)并且您将获得“你好你好”。只需将printf更改为printf("hello %d ", i);
,您就会遇到分段错误。