Invoke child class method in C++

Asked

Viewed 259 times

2

I have a parent class/structure and a daughter class/structure in C++. The parent class defines a method, and the daughter class burdens 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 a Pai, or any other class inheriting the same:

Pai obj = Filha{};

My problem occurs in invoking the method of that instance. In languages like Java or C#, it is expected that the method invoked is the one belonging to the instance of the class stored in the variable "obj", that is, it 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 class method Pai, printing "Parent class".

There is something I can do so that when invoking the "print" method, the method declared in the child class is invoked?

Here has code running for better viewing.

2 answers

3

There is something I can do so that when invoking the "print" method, the method declared in the child class is invoked?

Pai obj = Filha{};

Yeah, but it’s complicated. When you create a Son object and hold it in a Father variable, all inherent information of the Son is lost. Sometimes, when this is desirable, one can use abstract classes or virtual functions in Father, selecting overload points, so that then Father is composed of Son members:

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 key 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 that Father has information about Son 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, Father has information about Son and can invoke Son methods. Son inherits Father using Son as template. The print function is required so that there is a generic template for all Father and Son combinations.

0


You’re trying to make inheritance on a type by value, it doesn’t happen as you expect. To do polymorphism in type by value use a mechanism of template or something more complex, but don’t try to dominate all aspects of C++ programming as memory behaves in these situations. Almost always this kind of polymorphism is a mistake, and only seems to be right in such an artificial example where the names of the types do not even have meaning.

Object-oriented programming is giving meaning to things, when you don’t give names, it has no meaning and you don’t use OOP the right way, never.

So to do the same Java or C# you need to do this with an object by reference, because that’s how these languages do.

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

Then I’d 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;
}

Behold working in the ideone. And in the repl it.. Also put on the Github for future reference.

But I’m not recommending creating something real that 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.

  • I am particularly concerned with the destructor method, it is possible to declare this "override" in the destructor class Daughter also?

  • That is another question, but it can, and in many cases it must if it is necessary. I am only afraid of a destructor in a kind by value.

  • Well, "smart pointers" implement destructors even though they’re a guy by value, right? The example I gave was used only to avoid polluting the question.

  • That’s why I’m afraid and not that it’s forbidden. It is very rare to be useful, almost always will be an invention without the person understanding why is doing it. The smart Pointer, should not be inherited. The problem of programming is that everything can be composed, what is beautiful alone does not work with other things. Join destructor polymorphism in type that should be by value but the object was created with reference to work...

Browser other questions tagged

You are not signed in. Login or sign up in order to post.