Search code examples
c++inheritancepolymorphismcovariancevirtual-functions

C++ Polymorphism does not work as expected


Consider the following code:

#include <iostream>

class Base{
public:
    int iB;
    Base() :iB(0){};
    virtual ~Base(){}
    virtual void foo(Base&, const int&, const int&) const = 0;
};


class Derived : public Base{
public:
    int iD;
    Derived() :iD(1){};
    void foo(Derived&, const int&, const int&) const;
};
void Derived::foo(Derived& d, const int& i1, const int& i2) const{ 
    std::cout << d.iD; 
}


class BaseWrap{
public:
    BaseWrap(){};
    virtual ~BaseWrap(){};
    virtual Base* bar() const = 0;
protected:
    Base* b;
};


class DerivedWrap : public BaseWrap{
public:
    DerivedWrap(){};
    Derived* bar() const;
private:
    Derived* d;
};
Derived* DerivedWrap::bar() const { 
    return new Derived; 
}

int main(){

    return 0;
}

This results in a compiler error "Error: object of abstract class type "Derived is not allowed" pure virtual function Base::foo" has no overrider. I assumed that thanks to polymorphism I can always put a pointer to a derived class where a pointer to Base class is expected. Anyway, I tried changing the Derived class to the below:

class Derived : public Base{
public:
    int iD;
    Derived() :iD(1){};
    void foo(Base&, const int&, const int&) const;
};
void Derived::foo(Base& d, const int& i1, const int& i2) const{
    std::cout << d.iD; 
}

However, now I I get the error "Error: class "Base" has no member "iD".

EDIT:

foo takes a ref to derived because in my real implementation i.e. I want to be able to do this:

Derived d1;
Derived d2;
d1.foo(d2, 0, 1);

Furthermore, I probably should have been more clear on what I am actually asking. I realize that removing the pure virtual function declatation

virtual void foo(Base&, const int&, const int&) const = 0;

fixes the issue. However, in all derived class implementation the code is exactly the same, and only varies in the type of the first argument (derived classes from Base). So it feels like there should be a pure virtual function to enforce existence of foo.


Solution

  • The problem

    In your base class you define the pure virtual member function:

    virtual void foo(Base&, const int&, const int&) const = 0;
    

    but you provide in the derived a function with another signature:

    void foo(Derived&, const int&, const int&) const;
    

    So you have one more member function in the derived class (an overload: same name but different parameter types), but still the inherit the pure virtual function. So the derived class is as abstract as the base class and you're not allowed to instantiate it.

    The solution

    In the derived class change the signature so that it matches the pure virtual bas e member function:

    void foo(Base&, const int&, const int&) const;
    

    By the way, whenever you use virtual functions, use override keyword to spot these kind of subtle errors at compilation, even for ordinary virtual functions:

    void foo(Base&, const int&, const int&) const override;
    

    More infos

    Indeed, once you define your foo() with a Base parameter, you can't use easily the iD member.

    The first solution is to use a dynamic_cast to tell your code that the base is in fact a derived object. Of course you have to check if this assumption is correct:

    void Derived::foo(Base& d, const int& i1, const int& i2) const{ 
        Derived *dp = dynamic_cast<Derived*>(&d);
        if (dp) {
            std::cout << dp->iD; 
        }
    }
    

    However, what is not clear to me is why you need this first in a first place. Why not get rid of this first type dependent parameter and use the member variables of the current object:

    void Derived::foo(const int& i1, const int& i2) const{ 
        std::cout << iD; 
    }