C ++:为什么struct \ class需要一个虚方法才能变成多态?

时间:2011-04-29 12:13:51

标签: c++ polymorphism virtual-method

关注this question,我想知道为什么C ++中的struct \ class必须有一个虚方法才能变成多态。

强制虚拟析构函数是有道理的,但如果根本没有析构函数,为什么必须使用虚方法?

8 个答案:

答案 0 :(得分:19)

因为C ++中多态对象的类型基本上是从指向vtable的指针确定的,vtable是虚函数表。但是,只有在至少有一个虚拟方法时才会创建vtable。为什么?因为在C ++中,你永远不会得到你没有明确要求的东西。他们称之为“你不需要支付你不需要的东西”。不需要多态?你刚刚保存了一张vtable。

答案 1 :(得分:8)

  

强制虚拟析构函数有意义

完全。要通过其基类手动(通过delete)破坏虚拟类,您需要虚拟析构函数。 (现在,正如我在评论中提醒的那样,通常不需要这样做:不使用手动内存管理,而是依赖于现代智能指针,这些指针也可以与非虚拟析构函数一起正常工作。)

因此,任何充当多态基类的类通常都需要虚拟析构函数或虚函数。

由于运行时多态性增加了开销(该类需要存储一个指向其虚方法表的附加指针),默认情况下不添加它,除非必要,否则:C ++的设计哲学是“你只为你需要的东西买单”。让每个类都有一个虚拟方法表会违反这个原则。

答案 2 :(得分:3)

因为它在标准中被定义为。

从10.3 / 1 [class.virtual]

  

虚函数支持动态绑定和面向对象编程。声明或继承虚函数的类称为多态类。

如果你使用继承,那么你至少有一个虚方法是有意义的。如果您没有任何虚拟方法,则可以使用合成。

答案 3 :(得分:2)

多态性是允许您的子类覆盖基类函数的默认行为,因此除非您的基类中有虚方法,否则不能覆盖基类中的方法。

答案 4 :(得分:2)

  

我想知道为什么C ++中的struct \ class必须有一个虚方法才能变成多态?

因为这就是多态类的含义。

在C ++中,运行时多态性是通过虚函数实现的。基类声明了许多派生类实现的一些虚函数,客户端使用静态类型的基类的指针(或引用),并且可以使它们指向派生类的对象(通常是不同的派生类),然后再使用on,通过基指针调用派生类的实现。这就是实现运行时多态性的方式。并且由于中心角色由函数virtual扮演,这使得运行时多态性成为可能,因此具有虚函数的类被称为多态类。

答案 5 :(得分:1)

没有任何虚拟方法,就不需要为类的每个对象维护虚拟指针(缩写为vptr)。虚拟指针是一种在运行时解析虚方法调用的机制;根据对象的类,它可能指向包含虚拟方法的实际地址的不同虚拟方法表(缩写为vtable)。

因此,通过检查vptr指向的vtable,编译器可以确定对象的类,例如在dynamic_cast中。没有vptr的对象不能以这种方式确定其类型,也不是多态的。

答案 6 :(得分:1)

A C++ design philosophy是“你不为你不使用的东西买单”。您可能已经知道virtual函数会产生一些开销,因为类必须维护指向其实现的指针。实际上,一个对象包含对一个名为vtable的函数指针表的引用。

考虑以下示例:

class Base
{
public:
    virtual f() { /* do something */ }
}; 

class Derived : public Base
{
public:
    virtual f() { /* do something */ }
}; 

Base* a = new Derived;
a->f(); // calls Derived::f()

请注意,变量a指向Derived个对象。当f()被声明为virtual时,a的vtable将包含指向Derived::f()的指针并执行该实现。如果f()不是virtual,则vtable将为空。因此,Base::f()的执行类型为a {/ 1}}。

析构函数的行为与其他成员函数一样。如果析构函数不是Base,则只会调用virtual类中的析构函数。如果Base类实现RAII,这可能会导致内存/资源泄漏。如果一个类要被分类,那么它的析构函数应该是Derived

在Java等语言中,所有方法都是虚拟的。因此,即使不是多态的对象也会消耗内存来维护函数指针。换句话说,你被迫支付你不使用的东西。

答案 7 :(得分:0)

由于其他人描述的原因,类只需要虚拟方法才能动态多态。但是,您仍然可以通过模板获得静态多态性。