C++ c+中下行广播期间的内存布局+;
当然,对象内部不包含成员函数,只包含数据成员。上面的图表只是为了解释。通常,编译器将类分解为C结构和函数,这些C结构和函数将C++ c+中下行广播期间的内存布局+;,c++,C++,当然,对象内部不包含成员函数,只包含数据成员。上面的图表只是为了解释。通常,编译器将类分解为C结构和函数,这些C结构和函数将这个指针作为参数。然后,它以编译C的相同方式编译代码—结构+全局函数。这是编译面向对象代码的常用方法。当然,还有更多的“窍门”,比如虚拟表等等 由于您的成员函数不接触任何成员函数/变量(=使用此指针),因此它工作正常,因为尽管此完全错误,但未使用它 在汇编方面,您的代码如下所示 Parent Child ----------- -----------
这个指针作为参数。然后,它以编译C的相同方式编译代码—结构+全局函数。这是编译面向对象代码的常用方法。当然,还有更多的“窍门”,比如虚拟表等等
由于您的成员函数不接触任何成员函数/变量(=使用此
指针),因此它工作正常,因为尽管此
完全错误,但未使用它
在汇编方面,您的代码如下所示
Parent Child
----------- -------------
| | | |
| sleep | | sleep |
| | | gotoSchool|
----------- -------------
void gotoSchool(Child*this){cout
但我的问题是为什么它会起作用
因为编译器的实现方式恰好导致了这种行为
不应该这样
如果您的意思是编译器不应该允许这种行为,那么……标准并不要求这样做
它可能,它可以,它确实做到了
工作原理
我手头没有visual studio的源代码,因此我无法确切地告诉您它是如何工作的。但一般来说,成员函数是作为常规函数实现的,除非有一个包含对象地址的额外参数。函数本身在内存中的位置与对象的内存无关.编译器确切地知道函数的代码在哪里
您甚至可以执行((Child*)nullptr)->gotoSchool();
并观察相同的(未定义的)行为
没有明显的理由说明它不能观察到这种行为
我无法在内存中可视化布局对象
对象的内存布局与函数无关,因为函数从不使用对象的任何内存。之所以有效,是因为您实际指向的是子对象。将指针投射到子对象
不会将其更改为父对象*
,它只会告诉编译器查看当前的内存指针作为父对象*
。再次将同一指针投射回子对象*
不会改变任何东西。如果你“向上投射”到一个不存在的东西,那么底层内存将不正确,但向上投射到你知道它存在的东西会起作用
lower address <---where a pointer to the object points to
[PTR to VTABLE]
[data members for parent]
[data members for child]
higher address
但是,要回答您关于内存布局的问题,通常是:
void gotoSchool(Child* this){cout<<"chil::gotoSchool";}
gotoSchool(&parent);
loweraddress它可以工作,因为您实际上不使用实例成员中的任何内容,只需打印一个静态字符串
该函数不是虚拟函数,因此当您调用gotoSchool时,它直接调用它
(使用VS2015进行测试)您也可以在空指针上调用gotoSchool,只要您实际上不尝试引用任何成员,它就可以工作
lower address <---where a pointer to the object points to
[PTR to VTABLE]
[data members for parent]
[data members for child]
higher address
编译器知道pChild是Child类型,并且知道gotoSchool不是virtual,因此它直接调用它,而不管pChild中的值如何
内存布局是什么?
从逻辑上讲,它是空的。
您既没有需要vtable的虚拟成员,也没有成员变量
实际上,它可能包含RTTI的类型信息和/或一些填充字节,但这些都与gotoSchool函数无关
非虚拟功能不是内存布局的一部分!
C++类的实现
有很多更好的解释,但简而言之,成员函数是作为普通函数实现的,带有隐藏的this参数
您的函数在内部是正确的
Child* pChild = (Child*)0;
pChild->gotoSchool();
在您的示例中,这个
引用了一个父级,但您从未使用过它,所以这无关紧要
未定义的实际实现
您认为它不应该工作,因为它未定义。
但undefined的字面意思是,任何事情都是可能的,并且允许未定义的行为。
我推荐陈雷蒙的这篇文章(以及其他一些文章):
然后,它编译的代码和C++编译的方式一样——结构+全局函数。“嗯……不是。我是说,但不是真的。只有当你在下面调用一个常规的层讨论。嗯,还有其他的行为,特别是如果涉及到其他C++特定的特性,比如模板,但是这种”类型的“。这就是我的意思-函数将this作为函数和成员变量之间的桥梁。@David:即使它是这样传递的,我也不知道它是如何工作的。你可以这样调用它pChild->gotoSchool(pChild)
并以这种方式接收它void gotoSchool(Child*mythis){mythis=this;}
。在分配mythis=this
之前,您可以看到这两个函数都指向同一个地址,因此没有任何区别。如果您想真正了解函数调用的工作原理,您必须学习汇编。但无论如何,即使有分配等,您的成员函数也不会使用this
。这是它失败的唯一原因正在工作(现在仍然是UB)。一旦函数开始使用这个
你就会出现更奇怪的行为。不,我的问题与编译器为什么允许或它为什么工作无关,而是它是如何工作的。导致打印语句child::gotoSchool的内存布局是什么。@anurag86我没有源代码,所以我无法确切地告诉你visua是如何工作的l studio可以工作,但我已经扩展到回答这个问题。它可以工作,因为您实际上是指向子对象。不,我不是指向子对象。我只是让编译器相信我指向的是子对象。其次,您显示的布局仅适用于数据成员,在这种情况下,作为成员它并不重要正在访问Action,而不是数据成员。最后,这里不存在VTABLE。啊,我的错,我没有仔细阅读。我以为你是先向父级强制转换,然后再返回。它可以工作,因为你调用的成员函数不访问任何子级da
gotoSchool(Child* this)