pio*_*rek 3 c++ visual-studio-2010 visual-c++
visual c ++是否为只有一个实现的纯类虚拟化一个函数?例如:
class ICar
{
public:
virtual void Break() = 0;
};
class CarImpl : public ICar
{
public:
virtual void Break(){ .... }
};
Run Code Online (Sandbox Code Playgroud)
OP问题自然会分为3个问题:
以下是详细信息:
为了证明未完成此优化,我们需要在项目属性/配置属性/ C/C++ /输出文件中启用汇编语言列表:将汇编器输出设置为"使用源代码汇编"(/ FAs)".
这是来自OP的略微修改的C++代码(我将ICar从抽象类更改为普通类,它不会改变问题的要点):
#include "stdafx.h"
class ICar
{
public:
virtual void Accelerate(){printf("%s", "a\n");};
virtual void Break(){printf("%s", "b\n");};
};
class CarImpl : public ICar
{
public:
virtual void Accelerate(){ printf("%s", "accelerate\n"); }
virtual void Break(){ printf("%s", "break\n"); }
void Fly() { printf("%s", "fly\n"); }
};
int _tmain(int argc, _TCHAR* argv[])
{
ICar *pCar = new CarImpl();
pCar->Break();
CarImpl *pCarImpl = new CarImpl();
pCarImpl->Fly();
CarImpl carImpl;
carImpl.Break();
carImpl.Fly();
return 0;
}
Run Code Online (Sandbox Code Playgroud)
首先,(注1)让我们注意carImpl.Break();不使用虚函数.这不是优化的结果 - 它是C++的一个特性:如果在编译期间已知对象的类型,则不使用虚函数的机制.虚函数的机制仅在涉及指针或引用时使用.
其次,让我们启用优化/ O2并查看为pCar->Break();(虚方法)和pCarImpl->Fly();(非虚方法)生成的汇编程序.
对于Break()的调用,我们将看到:
; 24 : pCar->Break();
mov edx, DWORD PTR [eax]
mov ecx, eax
mov eax, DWORD PTR [edx+4]
call eax
Run Code Online (Sandbox Code Playgroud)
EAX包含一个指向CarImpl对象的指针(从前面的汇编程序行中可以清楚地看出这里没有显示).在第一个mov指令中,CarImpl对象的第一个双字被加载到EDX中(对象的第一个双字通常是vtbl的地址),然后this在ECX中加载CarImpl(这对我们来说不重要),然后是dword从EDX指向的点(虚函数表中的第二个函数)加载到EAX中的偏移量4,然后完成调用.
如果是Fly(),我们会看到:
; 27 : pCarImpl->Fly();
push OFFSET ??_C@_04PPJAHJOB@fly?6?$AA@
push OFFSET ??_C@_02DKCKIIND@?$CFs?$AA@
call _printf
Run Code Online (Sandbox Code Playgroud)
这只是传递给它的两个参数的printf的内联.
因此,显然在Break()的情况下没有优化vtable的使用.
原则上它可以进行优化.我在M.Ellis的"The Annotated C++ Reference Manual"中找到了以下陈述,B. Stroustrup,Addison-Wesley 1990:第10.2章(我翻译了这本书,我正在翻译成英文:-)所以它可能不是Stroustroup的确切措辞.)
当在编译时知道确切类型的对象时,不需要虚函数的机制.相反,实现可以生成类成员函数的普通调用.(DK:我们的代码中carImpl.Break()的情况,请参阅我的注释1)...当通过指针或引用调用虚函数时,实际的对象类型可能不是静态知道的,因此虚拟机制应该使用函数.具有足够控制流知识的编译器可以删除对虚函数的调用,即使在某些情况下,例如通过以下代码中的bp调用:
struct base {
virtual void vf1();
}
class derived : public base{
public:
void vf1();
}
void g()
{
derived d;
base* bp = &d;
bp->vf1();
}
Run Code Online (Sandbox Code Playgroud)
...内联虚拟功能非常有意义并且经常使用.当然,内联仅用于将内联函数应用于已知类型的对象的位置.(DK:我想在这里B. Stroustrup也指我们carImpl.Break()的情况;即在NOTE1中描述的情况.
尽管在OP中没有提到这个问题,但也许这是一个隐藏的问题.我同意Alex Cohn的评论之一(说得好):
它可以,但事实并非如此.可能这种情况不足以证明可靠地优化此类呼叫所需的资源.
| 归档时间: |
|
| 查看次数: |
1797 次 |
| 最近记录: |