覆盖vs虚拟

ano*_*ous 39 c++ virtual overriding function

在函数前使用虚拟词virtual的目的是什么?如果我想让子类覆盖父函数,我只需声明相同的函数,如void draw(){}.

class Parent { 
public:
    void say() {
        std::cout << "1";
    }
};

class Child : public Parent {
public:
    void say()
    {
        std::cout << "2";
    }
};

int main()
{
    Child* a = new Child();
    a->say();
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

输出为2.

那么,为什么保留字virtual在标题中是必要的say()呢?

谢谢一堆.

Don*_*nie 37

如果函数是虚函数,那么你可以这样做并仍然得到输出"2":

Parent* a = new Child();
a->say();
Run Code Online (Sandbox Code Playgroud)

这是因为virtual函数使用实际类型,而非虚函数使用声明的类型.阅读多态性,以便更好地讨论您为什么要这样做.

  • 你不是一直这样做,你在适当的时候这样做.如果您正在创建绘图应用程序,并且让人们选择形状画笔,那该怎么办?您需要一个全局(或至少是对象级)变量,但不知道它们会提前选择哪种形状. (3认同)

Jer*_*fin 26

尝试使用:

Parent *a = new Child();
Parent *b = new Parent();

a->say();
b->say();
Run Code Online (Sandbox Code Playgroud)

没有virtual,两者都打印'1'.添加虚拟,孩子将像孩子一样,即使它通过指向一个指针被引用Parent.


kro*_*lth 18

这是我认为多态如何工作的经典问题.主要思想是您要抽象每个对象的特定类型.换句话说:你希望能够在不知道它是孩子的​​情况下调用Child实例!

下面是一个示例:假设您有类"Child"和类"Child2"和"Child3",您希望能够通过它们的基类(Parent)引用它们.

Parent* parents[3];
parents[0] = new Child();
parents[1] = new Child2();
parents[2] = new Child3();

for (int i=0; i<3; ++i)
    parents[i]->say();
Run Code Online (Sandbox Code Playgroud)

你可以想象,这是非常强大的.它允许您根据需要多次扩展Parent,并且使用Parent指针的函数仍然可以工作.为了使其像其他人一样提及,您需要将该方法声明为虚拟方法.


Dav*_*eas 16

如果不使用virtual关键字,则不要覆盖,但rahter在派生类中定义一个隐藏基类方法的无关方法.也就是说,没有virtual,Base::say并且Derived::say是无关的 - 这就是名称巧合.

当您使用虚拟关键字(在基础中需要,在派生类中是可选的)时,您告诉编译器从该基础派生的类将能够覆盖该方法.在这种情况下,Base::sayDerived::say被认为是相同的方法的替代.

当您使用引用或指向基类的指针来调用虚方法时,编译器将添加适当的代码,以便调用最终的覆盖(在大多数派生类中的覆盖,它定义具体实例的层次结构中的方法)正在使用).请注意,如果不使用引用/指针而是使用局部变量,则编译器可以解析调用,并且不需要使用虚拟调度机制.


jok*_*oon 12

我亲自测试了它,因为我们可以考虑很多事情:

#include <iostream>
using namespace std;
class A
{
public:
    virtual void v() { cout << "A virtual" << endl; }
    void f() { cout << "A plain" << endl; }
};

class B : public A
{
public:
    virtual void v() { cout << "B virtual" << endl; }
    void f() { cout << "B plain" << endl; }
};

class C : public B
{
public:
    virtual void v() { cout << "C virtual" << endl; }
    void f() { cout << "C plain" << endl; }
};

int main()
{
    A * a = new C;
    a->f();
    a->v();

    ((B*)a)->f();
    ((B*)a)->v();
}
Run Code Online (Sandbox Code Playgroud)

输出:

A plain
C virtual
B plain
C virtual
Run Code Online (Sandbox Code Playgroud)

我认为一个好的,简单的和简短的答案可能看起来像这样(因为我认为能够理解更多的人可以记住更少,因此需要简短而简单的解释):

虚方法检查指针指向的实例的DATA,而经典方法因此不调用与指定类型相对应的方法.

该功能的要点如下:假设您有一个A的数组.该数组可以包含B,C,(甚至派生类型).如果要按顺序调用所有这些实例的相同方法,则应调用每个重载的方法.

我发现这很难理解,显然任何C++课程都应该解释如何实现这一点,因为大多数时候你刚刚学过虚函数,你使用它们,但直到你理解编译器如何理解它们以及如何执行它们会处理电话,你是在黑暗中.

关于VFtables的事情是我从来没有解释过它添加了什么样的代码,而且显然这里C++需要比C更多的经验,这可能是C++在早期被标记为"慢"的主要原因:事实上,它很强大,但就像所有东西一样,如果你知道如何使用它会很强大,或者你只是"把你的整条腿吹走".