Invoke child class method in C++

I have a parent class / structure and a child class/structure in C++. The parent class defines a method, and the child class overloads that method.

Trivial example:

struct Pai {
    
    void imprimir() {
        std::cout << "Classe pai" << "\n";
    }
    
};

struct Filha : Pai {

    void imprimir() {
        std::cout << "Classe filha" << "\n";
    }

};

I want to declare a variable of type Pai, this variable can be initialized with an instance of Pai, or any other class that inherits the same:

Pai obj = Filha{};

My problem occurs when invoking the method of this instance. In languages such as Java or C#, it is to be expected that the invoked is the one belonging to the instance of the class stored in the variable "obj", i.e. should print"child class".

But in C++ What happens is that the program invokes the method of the same class as the variable is declared, invokes the method of the Class Pai, printing "parent class".

Is there anything I can do so that when invoking the "print" method, the method declared in the child class is invoked?

Here has the code running for better viewing.

Author: Maniero, 2020-08-02

2 answers

You're trying to inherit a type by value, it doesn't happen as you expect. To do polymorphism in Type By Value need to use a mechanism template or something more complex, but do not try without mastering too much all aspects of programming in C++, how memory behaves in these situations. Almost always this kind of polymorphism is a mistake, and it only seems to be right in such an artificial example where type names do not even have meaning.

Programming object-oriented is to give meaning to things, when you don't give names, you don't have meaning and you don't use OOP the right way, ever.

So to do equal Java or C # you need to do this with an object by reference, because that's how these languages do it.

And need to say that the method is virtual because without it the polymorphism is not activated, equal to C# (it is not equal to Java because it is always activated, which is a language error).

Then would have to do something like this:

#include <iostream>
using namespace std;

struct Pai {
    virtual void imprimir() {
        cout << "Classe pai\n";
    }
};

struct Filha : Pai {
    void imprimir() override {
        cout << "Classe filha\n";
    }
};

int main() {
    Pai *obj = new Filha{};
    obj->imprimir();
    delete obj;
}

See working on ideone. E no repl.it. also I put on GitHub for future reference .

But I'm not recommending creating something real this way, I'm just demonstrating how the mechanism would work if it were the right one. Nor is it the best way to write that code.

 0
Author: Maniero, 2020-08-03 12:37:35

Is there anything I can do so that when invoking the "print" method, the method declared in the child class is invoked?

Pai obj = Filha{};

Yes, but it's complicated. When vc creates a child object and stores it in a parent variable, all inherent Child information is lost. Sometimes, when this is desirable, one can use Abstract classes or virtual functions in parent by selecting overload points, so that then parent is composed of members of Child:

struct Pai {
    virtual void imprimir()=0;
};

struct Filha : Pai {
    void imprimir() override {
        std::cout << "Classe filha" << "\n";
    }
};

//...

Pai* obj = new Filha{};
obj->imprimir();

This technique is known as type erasure and is a fundamental part of some STL components, such as std::function. Virtual functions come with a considerable performance penalty and are not always the best solution for every situation, so this functionality does not occur automatically as in purely object-oriented languages (Java, C#, etc).

Through templates it is possible for parent to have information about child without resorting to virtual functions with a technique called CRTP (Curiously Recurring Template Pattern).

template <typename TipoFilho>
struct PaiCRTP {
    void imprimir() {
        auto filho = static_cast<TipoFilho*>(this);
        filho->imp();
    }
};
struct FilhaCRTP : PaiCRTP<FilhaCRTP> {
    void imp() {
        std::cout << "Classe filha" << "\n";
    }
};
template <typename TipoFilho>
void imprime(PaiCRTP<TipoFilho>& pai) {
    pai.imprimir();
}

int main()
{
    FilhaCRTP filha;
    imprime(filha);
}

In this implementation, parent has the information about child and can invoke child methods. Child inherits parent using child as template. The print function is required for a generic template to exist for all parent and child combinations.

 3
Author: aviana, 2020-08-02 21:44:03