我有以下代码:
class Class {
public:
virtual void first() {};
virtual void second() {};
};
Class* object = new Class();
object->first();
object->second();
delete object;
我使用带有/O2的Visual C++ 10进行了编译,并进行了以下反汇编:
282: Class* object = new Class();
00403953 push 4
00403955 call dword ptr [__imp_operator new (4050BCh)]
0040395B add esp,4
0040395E test eax,eax
00403960 je wmain+1Ch (40396Ch)
00403962 mov dword ptr [eax],offset Class::`vftable' (4056A4h)
00403968 mov esi,eax
0040396A jmp wmain+1Eh (40396Eh)
0040396C xor esi,esi
283: object->first();
0040396E mov eax,dword ptr [esi]
00403970 mov edx,dword ptr [eax]
00403972 mov ecx,esi
00403974 call edx
284: object->second();
00403976 mov eax,dword ptr [esi]
00403978 mov edx,dword ptr [eax+4]
0040397B mov ecx,esi
0040397D call edx
285: delete object;
0040397F push esi
00403980 call dword ptr [__imp_operator delete (405138h)]
请注意,在
00403968
处,对象起始地址(存储vptr
的地址)被复制到esi
寄存器中。然后在0040396E
处,此地址用于检索vptr
,并且vptr
值用于检索first()
的地址。然后在00403976
处再次检索vptr
并用于检索second()
的地址。为什么vptr被检索两次?该对象是否可能在两次调用之间更改其
vptr
或仅仅是优化不足? 最佳答案
考虑:
object->first();
此调用可能会破坏对象,并在同一块内存中创建一个新对象。因此,在此调用之后,无法对状态做出任何假设。例如。:
#include <new>
struct Class {
virtual void first();
virtual void second() {}
virtual ~Class() {}
};
struct OtherClass : Class {
void first() {}
void second() {}
};
void Class::first() {
void* p = this;
static_assert(sizeof(Class) == sizeof(OtherClass), "Oops");
this->~Class();
new (p) OtherClass;
}
int main() {
Class* object = new Class();
object->first();
object->second();
delete object;
}
如果该函数是内联函数和/或使用链接时代码生成,则编译器可以优化不必要的寄存器负载。
正如DeadMG和Steve Jessop指出的那样,以上代码表现出不确定的行为。根据C++ 2003 Standard的3.8/7:
上面的代码不满足上面列表中的要求2。