简体   繁体   中英

Polymorphic call through non-virtual method

I have a little problem with polymorphism. My simple code:

Animal.h

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


Animal.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;
}



Bird.h

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


Bird.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;
}



Chicken.h

#include "Bird.h"

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


Chicken.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;
}



There is also a factory method returning an Animal* pointer to the concrete implementation based on input:


Factory.h

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

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


Factory.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);
    }
}



I omitted 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;
}



The concrete implementation of an Animal class is resolved during runtime. It's obvious, that removing the virtual keyword from Animal class results in calling Animal implementation of get() method, whether animal* points to Bird or 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!

It's also obvious, that calling the virtual get() method results in polymorphic call to the concrete subclass. What concerns me is this situation: Instead of

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

we have

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

We have a pointer to Bird class, in which the get() method is NOT declared virtual. The output is:

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!



How does it happen, that call to non-virtual function results in virtual call to the subclass? Is "virtualism" inherited? If it is, is it somehow possible to perform a non-virtual call to the pointer class, instead of implementation class?

A virtual method remains virtual in inherited classes even if you don't specify the virtual keyword. Actually in C++11 you have a way to specify that method is overridden:

class Bird {
  void get() override;
}

You can even specify the virtual keyword on overridden methods just to remember it yourself but you can't "remove" the dynamic dispatch of the method.

The only thing you are allowed to do is to choose the implementation by specifying it:

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

We have a pointer to Bird class, in which the get() method is NOT virtual.

That's wrong. It is virtual. Virtual functions cannot be made non-virtual by derived classes. The virtual keyword is just optional there, but it has no effect.

In short, yes, virtual is "inherited". In other words, you can't "change it back" from virtual to non-virtual when you inherit from a base-class. That would make the system very fragile to typos (forget virtual, and all of a sudden you call a different member depending on which route you came to the object).

Say you have a class hierarchy

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

As soon as, traversing the hierarchy, a member is declared to be virtual, it will be so for further derived classes too. In case you are wondering, there is no way for making Z::f() non-virtual if Q::f() is virtual.

What this mean is explained in this code:

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()

of course this assumes that O::f() is overridden.

See also my answer to a related question.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM