繁体   English   中英

为什么要在c ++中使用虚函数呢?

[英]Why bother with virtual functions in c++?

这不是关于它们如何工作和宣布的问题,我认为这对我来说非常清楚。 问题是为什么要实现这个? 我想实际的原因是为了简化其他代码的关联并声明它们的基类型变量,从许多其他子类处理对象及其特定方法?

这可以通过模板化和类型测试来完成,就像我在Objective C中做的那样吗? 如果是这样,什么更有效? 我发现将对象声明为一个类并将其实例化为另一个类是令人困惑的,即使它是它的子类。

SOrry愚蠢的问题,但我还没有用C ++完成任何真正的项目,因为我是活跃的Objective C开发人员(它是一个小得多的语言,因此很大程度上依赖于SDK的功能,如OSX,iOS)我需要有任何并行的清晰视图表兄弟的方式。

是的,这可以通过模板完成,但是调用者必须知道对象的实际类型(具体类),这会增加耦合。

使用虚函数,调用者不需要知道实际的类 - 它通过指向基类的指针操作,因此您可以编译客户端一次,实现者可以根据需要更改实际实现,并且客户端不会只要界面不变,就必须知道这一点。

我不知道关于Objective-C的第一件事,但是这就是为什么你要“将一个对象声明为一个类并将其实例化为另一个”: Liskov Substitution Principle

由于PDF 文档,而OpenOffice.org文档文档,而Word文档文档,所以写起来很自然

Document *d;
if (ends_with(filename, ".pdf"))
    d = new PdfDocument(filename);
else if (ends_with(filename, ".doc"))
    d = new WordDocument(filename);
else
    // you get the point
d->print();

现在,为了使其工作, print必须是virtual ,或者使用virtual函数来实现,或者使用重新发明virtual轮的粗糙黑客来实现。 程序需要在运行时知道要应用哪种print方法。

模板解决了一个不同的问题,您可以在编译时确定要存储一堆元素时要使用的各种容器(例如)。 如果对具有模板功能的容器进行操作,则在切换容器或向程序中添加其他容器时无需重写它们。

虚函数实现多态。 我不知道Obj-C,所以我无法比较两者,但激励用例是你可以使用派生对象代替基础对象,代码将起作用。 如果你有一个编译和工作函数foo ,它运行在对base的引用上,你不需要修改它以使它与derived实例一起工作。

您可以通过获取参数的实际类型然后通过短路切换直接调度到相应的函数来执行此操作(假设您有运行时类型信息),但这需要手动修改每个新类型的开关(高维护成本)或具有反射(在C ++中不可用)来获取方法指针。 即使这样,在获得方法指针后,您也必须调用它,这与虚拟调用一样昂贵。

至于与虚拟调用相关的成本,基本上(在虚拟方法表的所有实现中)对对象oo.foo()应用的虚函数foo的调用被转换为o.vptr[ 3 ]() ,其中3是虚拟表中foo的位置,这是编译时常量。 这基本上是双重间接:

从对象o获取指向vtable的指针,索引该表以获取指向该函数的指针然后调用。 与直接非多态调用相比的额外成本只是表查找。 (实际上,在使用多重继承时可能存在其他隐藏成本,因为可能必须移动隐式this指针),但虚拟调度的成本非常小。

虚函数在继承中很重要。 想想一个例子,你有一个CMonster类,然后是一个继承自CMonster的CRaidBoss和CBoss类。

两者都需要绘制。 CMonster具有Draw()函数,但绘制CRaidBoss和CBoss的方式不同。 因此,通过利用虚拟函数Draw将实现留给他们。

好吧,这个想法只是让编译器为你执行检查。

这就像很多功能:隐藏你不想自己做的事情的方法。 这是抽象。

继承,接口等允许您为编译器提供接口以使实现代码匹配。

如果你没有虚函数机制,你必须写:

class A
{
    void do_something();   
};

class B : public A
{
    void do_something(); // this one "hide" the A::do_something(), it replace it.
};


void DoSomething( A* object )
{
    // calling object->do_something will ALWAYS call A::do_something()
    // that's not what you want if object is B...
    // so we have to check manually:

    B* b_object = dynamic_cast<B*>( object );

    if( b_object != NULL ) // ok it's a b object, call B::do_something();
    {
        b_object->do_something()
    }
    else
    {
        object->do_something(); // that's a A, call A::do_something();
    }
}

这里有几个问题:

  1. 你必须为在类层次结构中重新定义的每个函数编写这个。
  2. 对于每个子课,你还有一个额外的。
  3. 每次向整个层次结构添加定义时,都必须再次触摸此功能。
  4. 它是可见代码,每次都可以轻松搞定

因此,标记函数virtual以隐式方式 正确地执行此操作,以动态方式自动重新路由,函数调用正确的实现,具体取决于对象的最终类型。 你不必编写任何逻辑,这样你就不会在这段代码中出错,还有一件事需要担心。

这是你不想打扰的事情,因为它可以由编译器/运行时完成。

模板的使用在技术上也被称为理论家的多态性。 是的,两者都是解决问题的有效方法。 所采用的实施技术将解释他们的表现更好或更差。

例如,Java实现模板,但是通过模板擦除。 这意味着它只是显然使用模板,表面下是普通的旧多态。

C ++有非常强大的模板。 模板的使用使代码更快,尽管每次使用模板都会为给定类型实例化它。 这意味着,如果对int,double和字符串使用std :: vector,则会有三个不同的向量类:这意味着可执行文件的大小会受到影响。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM