C++中“virtual”关键字在对象而不是指针上的行为

Behaviour of the `virtual` keyword in C++ on objects instead of pointers

本文关键字:指针 对象 virtual 关键字 C++      更新时间:2023-10-16

我试图理解虚拟函数。

考虑以下代码,

#include <iostream>
#include <memory>
#include <vector>
class Animal 
{
public:
     virtual void eat() 
    {
        std::cout << "I eat like a generic animal.n";
    }
};
class Wolf : public Animal 
{
public:
    void eat() 
    {
        std::cout << "I eat like a wolf!n";
    }
};

int main() 
{
  Animal      a;
  Wolf        w;
  a.eat();
  w.eat();

}

使用virtual关键字,我得到输出

I eat like a generic animal.
I eat like a wolf!

正如它应该做的那样。

但如果我删除虚拟关键字,我仍然会得到相同的输出!来自我的对虚拟函数的初步理解,如果没有virtual,我应该得到输出

I eat like a generic animal.
I eat like a generic animal.

我缺了什么初级的东西吗?

我在Linux 上使用g++编译器

不,这是正确的行为。需要虚拟函数来引入多态性。要启用多态行为,您需要使用这样的指针:
 Animal * a = new Animal();
 Animal * w = new Wolf();
 a->eat();
 w->eat();
 <...>
 delete a;
 delete w;

按照现在的方式,行为是正确的,因为这两个变量显然有不同的类型。

多态性通过识别实例实际引用的对象类型来工作。

在您的情况下,您的实际动物如下:

Animal      a;  //a is an animal.
Wolf        w;  //w is a wolf.

所以,您根本没有使用多态性

你需要做的更像这样:

//Create a couple animal pointers.
Animal* a;
Animal* b;
//Create an animal instance and have a point to it.
a = new Animal();
//Create a wolf instance and have b point to it.
b = new Wolf();
//Calls Animal::eat as a is an animal.
a->eat();
//Calls Wolf::eat as a is a wolf.
b->eat();

请注意,您可以使用指针或引用来实现多态性的使用

这就是为什么在处理类类型时,通常应该通过const引用传递对象。

//Will call Animal::eat or Wolf::eat depending on what animal was created as.
void foo(const Animal& animal) {
    animal.eat();
}
//Will always call Animal::eat and never Wolf::eat since this isn't a reference or
//a pointer.  Will also "slice" a Wolf object.
void foo(Animal animal) {
    animal.eat();
}

请注意,切片意味着它将不分青红皂白地将一个派生程度更高的类(狼)变成该类(动物)的派生程度更低的副本,这可能会非常误导和出乎意料。

即使没有虚拟,它仍然是一个方法。虚拟关键字允许在以下情况下进行多态行为:

Animal* wolf = new Wolf; // I eat like a wolf! (as long as eating is virtual)

通过使用virtual关键字,您将告诉编译器根据派生类型在运行时选择要调用的适当实现。