Search code examples
c++inheritanceoverridingoverloadingoverload-resolution

Overriding overloaded member functions that call each other in cpp


I have designed a class which uses overloaded member functions that call each other such that functions with additional arguments modify member variables that the original function relies on. It works as expected/intended when inheritance is not involved, but I found myself reusing the code often enough to want to create an abstract class.

I did this by having the original member function exit with an error in the abstract class, but still implementing the overloaded functions that modify member variables before calling the original function. This is done with the intent of only having to override the original function in my derived classes. However, when I create a derived class from this abstract class and supposedly override the member function, the functions that are not overridden still seem to be using the abstract class's definition of the member function-- the one that exits with an error. Here is a minimal example that causes the unexpected behavior.

#include <iostream>
#include <string>
#include <cstdlib>



class Base
{
    // this class exists only to be derived from
public:
    void write_message()
    {
        std::cerr << "Base is an abstract class not intended for direct use in"
                  << " programs." << std::endl;
        exit(1);
    }

    void write_message(std::string NewMessage)
    {
        Message = NewMessage;
        write_message();
    }

protected:
    std::string Message = "This is the default message";
};



class Derived : public Base
{
public:
    using Base::write_message;
    void write_message()
    {
        std::cout << Message << std::endl;
    }
private:
    // intentionally blank
};



int main() {
    Derived MyObject;
    MyObject.write_message("Here is a modified message");

    return 0;
}

As I'm sure is clear from this example, I am already aware of the common pitfall involving inheriting overloaded member functions which is topic of other questions on this site, which is needing to have using Base::write_message in my derived class. A couple of those questions include: this and this. What those questions don't address is this issue involving the overriding of write_message not working the way I expect it to.

What is missing here? I can't reason out why Base::write_message() would be used of Derived::write_message when calling MyObject.write_message("Here is a modified message"); in the code above.

I am using cpp17 and g++12.2.1 on linux.


Solution

  • What you should use is the virtual methods. Without them, it's not the overriding, but so called hiding. That's, you may define a method within the derived class which will have the same calling semantics as in base class, but it will hide the method from the base class.

    Here is rewritten your example:

    #include <iostream>
    #include <string>
    #include <cstdlib>
    
    
    
    class Base
    {
        // this class exists only to be derived from
    public:
        virtual ~Base() = default;  // It's better to have a virtual destructor if there is at least one virtual method.
    
        virtual void write_message() = 0;   // Pure virtual method. It defines the calling interface only and must be overridden to be used.
    
        void write_message(const std::string& NewMessage)   // It's better to use the const-reference in order to escape unneeded copying.
        {
            Message = NewMessage;
            write_message();    // Call the _implementation_
        }
    
    protected:
        std::string Message = "This is the default message";
    };
    
    
    
    class Derived : public Base
    {
    public:
        using Base::write_message;
    
        void write_message() override   // 'override' key-word ensures that we are overriding 'right' method and helps against of typos etc.
        {
            std::cout << Message << std::endl;
        }
    private:
        // intentionally blank
    };
    
    
    
    int main() {
        // Yes, it points to the Base, but because of virtuality...
        Base* MyObject = new Derived;
        MyObject->write_message("Here is a modified message"); // ... it invokes the Derived::write_message() here.
        delete MyObject;    // Don't forget to free the memory.
    
        return EXIT_SUCCESS;
    }