【问题标题】:Polymorphic call through non-virtual method通过非虚方法进行多态调用
【发布时间】:2013-09-23 01:50:15
【问题描述】:

我对多态性有一点问题。我的简单代码:

动物.h

class Animal {
public:
    Animal();
    Animal(const Animal& orig);
    virtual ~Animal();
    virtual void get();
};


动物.cpp

#include "Animal.h"
#include <iostream>
using namespace std;

Animal::Animal() {
    cout << "Animal is born" << endl;
}

void Animal::get() {
    cout << "get() from an Animal!" << endl;
}



鸟.h

class Bird : public Animal {
public:
    Bird();
    Bird(const Bird& orig);
    virtual ~Bird();
    void get();
};


鸟.cpp

#include "Bird.h"
#include <iostream>
using namespace std;

Bird::Bird() {
    cout << "Bird is born" << endl;
}

void Bird::get() {
    cout << "get() from a Bird!" << endl;
}



鸡.h

#include "Bird.h"

class Chicken : public Bird {
public:
    Chicken();
    Chicken(const Chicken& orig);
    virtual ~Chicken();
    void get();
};


鸡.cpp

#include "Chicken.h"
#include <iostream>
using namespace std;

Chicken::Chicken() {
    cout << "Chicken is born" << endl;
}

void Chicken::get() {
    cout << "get() from a Chicken!" << endl;
}



还有一个工厂方法根据输入返回一个指向具体实现的 Animal* 指针:


工厂.h

#include "Animal.h"
#include "Bird.h"

class Factory {
public:
    Factory();
    Factory(const Factory& orig);
    virtual ~Factory();
    Animal* generateAnimal();
    Bird* generateBird();
};


工厂.cpp

#include "Factory.h"

#include "Animal.h"
#include "Bird.h"
#include "Chicken.h"

#include <iostream>
#include <string>
#include <stdlib.h>
using namespace std;

Animal* Factory::generateAnimal() {
    string choice;
    cout << "What do you want? 1-Animal, 2-Bird, 3-Chicken" << endl;
    cin >> choice;
    Animal* animal;

    if (choice.at(0) == '1') {
        cout << "You chose Animal" << endl;
        animal = new Animal();
        return animal;
    } else if (choice.at(0) == '2') {
        cout << "You chose Bird" << endl;
        animal = new Bird();
        return animal;
    } else if (choice.at(0) == '3') {
        cout << "You chose Chicken" << endl;
        animal = new Chicken();
        return animal;
    } else {
        cout << "Wrong input" << endl;
        exit(1);
    }
}

Bird* Factory::generateBird() {
    string choice;
    cout << "What do you want? 1-Animal, 2-Bird, 3-Chicken" << endl;
    cin >> choice;
    Bird* bird;

    if (choice.at(0) == '2') {
        cout << "You chose Bird" << endl;
        bird = new Bird();
        return bird;
    } else if (choice.at(0) == '3') {
        cout << "You chose Chicken" << endl;
        bird = new Chicken();
        return bird;
    } else {
        cout << "Wrong input" << endl;
        exit(1);
    }
}



我省略了ctors和dtors。
main.cpp

#include <cstdlib>
#include <iostream>
#include "Factory.h"
#include "Animal.h"
#include "Bird.h"
#include "Chicken.h"

using namespace std;

int main(int argc, char** argv) {
    Factory factory;
    Animal* animal = factory.generateAnimal();
    animal->get();

    return 0;
}



Animal 类的具体实现在运行时解决。 很明显,从 Animal 类中删除 virtual 关键字会导致调用 get() 方法的 Animal 实现,无论 animal* 指向 Bird 还是 Chicken。

What do you want? 1-Animal, 2-Bird, 3-Chicken
3
You chose Chicken
Animal is born
Bird is born
Chicken is born
get() from an Animal!

很明显,调用虚拟get() 方法会导致对具体子类的多态调用。 我担心的是这种情况: 而不是

Animal* animal = factory.generateAnimal();
animal->get();

我们有

Bird* bird = factory.generateBird();
bird->get();

我们有一个指向 Bird 类的指针,其中的 get() 方法没有被声明为虚拟的。输出是:

What do you want? 1-Animal, 2-Bird, 3-Chicken
3
You chose Chicken
Animal is born
Bird is born
Chicken is born
get() from a Chicken!



它是如何发生的,对非虚拟函数的调用导致对子类的虚拟调用? “虚拟主义”是否继承?如果是,是否有可能以某种方式对指针类执行非虚拟调用,而不是实现类?

【问题讨论】:

  • 请尽量减少例子:-)

标签: c++ inheritance polymorphism virtual-functions


【解决方案1】:

假设你有一个类层次结构

class A{
    void f();
};
class B : public A{
    void f();    
};
class C : public B{
    void f();    
};
\\...
class O : public N{
    void f();    
};
class P : public O{
    virtual void f();    
};
class Q : public P{
    virtual void f();    
};
class R : public Q{
    void f();    
};
\\...
class Z : public Y{
    void f();    
};

一旦遍历层次结构,一个成员就被声明为虚拟的,对于进一步的派生类也是如此。如果您想知道,如果 Q::f() 是虚拟的,则无法将 Z::f() 设为非虚拟。

这段代码解释了这个意思:

Z z;
A& a = z;
O& o = z;
P& p = z;
Q& q = z;
Z& z = z;
a.f(); //calls A::f()
o.f(); //calls O::f()
p.f(); //calls Z::f()
q.f(); //calls Z::f()
z.f(); //calls Z::f()
z.A::f(); //calls A::f()
z.R::f(); //calls R::f()

当然,这假设 O::f() 已被覆盖。

有关相关问题,另请参阅 my answer

【讨论】:

    【解决方案2】:

    即使您未指定 virtual 关键字,virtual 方法仍会在继承的类中保留 virtual。实际上,在 C++11 中,您有一种方法可以指定该方法被覆盖:

    class Bird {
      void get() override;
    }
    

    您甚至可以在被覆盖的方法上指定 virtual 关键字只是为了自己记住它,但您不能“删除”方法的动态分派。

    您唯一可以做的就是通过指定来选择实现:

    Bird *bird = new Bird();
    bird->Animal::get();
    

    【讨论】:

    • 您是否检查过,您的最后几行代码是否符合您的预期。我相信无论如何都会有一个虚拟调度。
    • @RalphTandetzky 在那种情况下没有虚拟发送。示例:ideone.com/XhM2cV。出于同样的原因,在 derived::foo 中调用 base::foo() 不会调用虚拟调度(想象一下它是 this-&gt;base::foo())。
    • 顺便问一下,Java可以做同样的事情吗?
    【解决方案3】:

    简而言之,是的,virtual 是“继承的”。换句话说,当您从基类继承时,您不能将其从virtual“改回”为非虚拟。这将使系统非常容易出现拼写错误(忘记虚拟,突然之间,您根据到达对象的路线调用不同的成员)。

    【讨论】:

      【解决方案4】:

      我们有一个指向 Bird 类的指针,其中的 get() 方法不是虚拟的。

      这是错误的。它是虚拟的。派生类不能使虚函数成为非虚函数。 virtual 关键字在这里只是可选的,但没有任何作用。

      【讨论】:

      • 是的,我的错误,我的意思是“未声明”。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-27
      • 2013-02-02
      • 1970-01-01
      • 1970-01-01
      • 2016-07-23
      • 1970-01-01
      相关资源
      最近更新 更多