4

我试图理解虚函数。

考虑以下代码,

#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 关键字,我仍然会得到相同的输出!从我对虚函数的基本理解来看,没有virtual我应该得到输出

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

我缺少什么基本的东西吗?

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

4

3 回答 3

5

不,这是正确的行为。需要虚函数来引入多态性。要启用多态行为,您需要使用如下指针:

 Animal * a = new Animal();
 Animal * w = new Wolf();

 a->eat();
 w->eat();

 <...>

 delete a;
 delete w;

如果您现在拥有它的方式,那么行为是正确的,因为这两个变量显然具有不同的类型。

于 2012-07-16T17:53:32.270 回答
4

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

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

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-reference 传递对象。

//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();
}

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

于 2012-07-16T17:59:27.197 回答
1

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

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

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

于 2012-07-16T17:56:34.877 回答