visual c ++中的虚拟化

时间:2012-10-18 20:04:22

标签: c++ visual-studio-2010 visual-c++

visual c ++是否为只有一个实现的纯类虚拟化一个函数? 例如:

class ICar
{
public:
virtual void Break() = 0;
};

class CarImpl : public ICar
{
public:
virtual void Break(){ .... }
};

2 个答案:

答案 0 :(得分:6)

OP问题自然会分为3个问题:

  1. VC ++ 2010是否执行所描述的虚拟化 - 答案是:
  2. 它可以做到吗 - 答案是:理论上它本来可以 在某些情况下完成。
  3. 为什么VC ++不这样做。 - 我们只能猜测......
  4. 以下是详细信息:

    1。这样的优化是由VC ++

    完成的

    为了证明未完成此优化,我们需要在项目属性/配置属性/ 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;
    }
    

    首先,(注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
    

    EAX包含一个指向CarImpl对象的指针(从前面的汇编程序行中可以清楚地看出这里没有显示)。在第一个mov指令中,CarImpl对象的第一个双字被加载到EDX中(对象的第一个双字通常是vtbl的地址),然后CarImpl的this被加载到ECX中(这对我们来说并不重要) ,然后从EDX指向的点(虚函数表中的第二个函数)的偏移量4加载到EAX中,然后调用完成。

    如果是Fly(),我们会看到:

    ; 27   :     pCarImpl->Fly();
    
        push    OFFSET ??_C@_04PPJAHJOB@fly?6?$AA@
        push    OFFSET ??_C@_02DKCKIIND@?$CFs?$AA@
        call    _printf
    

    这只是传递了两个传递给它的printf的内联。

    所以,显然在Break()的情况下没有优化vtable的使用。

    2。可以完成这样的优化

    原则上可以进行优化。我在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();
    }
    

    ... 内联虚拟功能非常有意义并且经常使用。当然,内联仅用于将内联函数应用于已知类型的对象的位置。 (DK:我想在这里B. Stroustrup也指我们carImpl.Break()的情况;即NOTE1中描述的情况。

    3。为什么没有完成

    尽管在OP中没有提到这个问题,但也许这是一个隐藏的问题。我同意Alex Cohn的评论之一(说得好):

    可以,但事实并非如此。可能这种情况不足以证明可靠地优化此类呼叫所需的资源。

答案 1 :(得分:3)

编辑:我的第二个回答是2012-10-20,这个答案已经过时了。我没有删除它以保留评论。

VC ++不可能,因为其他派生类可以链接到已编译的dll或exe模块。