为什么一个structclass需要一个虚方法来实现多态?

C++: Why does a structclass need a virtual method in order to be polymorphic?

本文关键字:一个 实现 方法 多态 structclass 为什么      更新时间:2023-10-16

在这个问题之后,我想知道为什么c++中的structclass必须有一个虚拟方法才能多态。

强制使用虚析构函数是有意义的,但是如果根本没有析构函数,为什么强制使用虚方法呢?

因为c++中多态对象的类型基本上是从指向它的虚函数表的指针确定的,虚函数表。然而,只有当至少有一个虚方法时才会创建虚表。为什么?因为在c++中,你永远不会得到没有明确要求的东西。他们称之为"你不必为你不需要的东西付钱"。不需要多态性?你刚刚保存了一个虚表。

强制使用虚析构函数是有意义的

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

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

并且由于拥有运行时多态性会增加一个开销(类需要存储一个指向其虚方法表的额外指针),默认情况下除非必要,否则不会添加它:c++的设计哲学是"您只为您需要的东西付费"。让每个类都有一个虚拟方法表将与这一原则相冲突。

因为在标准中是这样定义的。

From 10.3/1 [class.virtual]

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

如果使用继承,那么至少有一个虚方法是有意义的。如果您没有任何虚方法,那么您可以使用组合来代替。

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

我想知道为什么c++中的结构类必须有一个虚拟方法才能多态?

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

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

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

因此,通过检查vtr指向的虚函数表,编译器可以确定对象的类,例如在dynamic_cast中。没有vvpr的对象不能以这种方式确定其类型,并且不是多态的。

c++的设计理念是"不用为不用的东西买单"。您可能已经知道,virtual函数会产生一些开销,因为类必须维护指向其实现的指针。实际上,对象包含对函数指针表(称为虚表)的引用。

考虑下面的例子:

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的虚函数表将包含一个指向Derived::f()的指针,并执行该实现。如果f()不是virtual,虚表将为空。所以Base::f()被执行,因为a的类型是Base

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

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

类只需要虚方法,以便动态地多态-出于其他人描述的原因。但是,您仍然可以通过模板实现静态多态性。