我写了这个简短的程序,看看虚拟化是如何工作的.编译器应该能够推导出正确的类型:
#includeusing std::cout; using std::endl; class Base { public: void foo() { cout << "Base::foo" << endl; } virtual void bar() { cout << "Base::bar" << endl; } virtual ~Base() = default; }; class Child : public Base { public: void foo() { cout << "Child::foo" << endl; } void bar() { cout << "Child::bar" << endl; } }; int main() { Base* obj = new Child; obj->foo(); obj->bar(); delete obj; }
-O2 -std=c++11
通过https://gcc.godbolt.org/使用gcc 5.3和clang 3.7进行编译.
结果是,两个编译器都无法优化所有内容 - gcc内联foo()
并进行虚拟调用,bar()
而clang调用foo()
和虚拟化以及内联调用bar()
.
同时,如果我打电话obj->bar();
然后obj->foo();
,编译器在优化方面没有问题 - clang内联两个调用和gcc正常调用bar()
而不是虚拟调用和内联调用foo()
.
谁能解释这种行为?
这可能是因为编译器认为内联没有帮助,因为cout
与函数调用的开销相比,它太昂贵了.如果用更简单的东西替换它,例如对成员的分配,它将被内联.请参阅下面的输出
#includeusing std::cout; using std::endl; class Base { public: void foo() { i = 1; } virtual void bar() { i = 2; } virtual ~Base() = default; int i = 0; }; class Child : public Base { public: void foo() { i = 3; } void bar() { i = 4; } }; int main() { Base* obj = new Child; obj->foo(); obj->bar(); std::cout << obj->i << std::endl; //delete obj; }
部件:
Base::bar(): movl $2, 8(%rdi) ret Child::bar(): movl $4, 8(%rdi) ret Base::~Base(): ret Child::~Child(): ret Child::~Child(): jmp operator delete(void*) Base::~Base(): jmp operator delete(void*) main: subq $8, %rsp movl $16, %edi call operator new(unsigned long) movl $4, %esi movl std::cout, %edi call std::basic_ostream>::operator<<(int) movq %rax, %rdi call std::basic_ostream >& std::endl >(std::basic_ostream >&) xorl %eax, %eax addq $8, %rsp ret subq $8, %rsp movl std::__ioinit, %edi call std::ios_base::Init::Init() movl $__dso_handle, %edx movl std::__ioinit, %esi movl std::ios_base::Init::~Init(), %edi addq $8, %rsp jmp __cxa_atexit