0

The Question

What is the correct way to have a member function return type be a superclass of the actual object you wan to return ?.

The Problem

Clang tidy warns me that returning subClassA or subClassB discards my override for myMethod. I also, get no output - presumably because I've accidently discarded the overrided myMethod.

The Code

#include <iostream>

using namespace std;

class SuperClass {
public :
    SuperClass() {
        cout << "I'm the superclass" << endl;
    };

    virtual std::string myMethod();
};

class SubClassA : public SuperClass {
public:
    SubClassA() : SuperClass() {
        cout << "I'm subclass A" << endl;
    }

    std::string myMethod() override {
        return "A";
    }
};

class SubClassB : public SuperClass {
public:
    SubClassB() : SuperClass() {
        cout << "I'm subclass B" << endl;
    }

    std::string myMethod() override {
        return "B";
    }
};

class Client {
private:
    std::string which;

    SuperClass letterFactory(){
        if (which == "A") {
            SubClassA subClassA;
            return subClassA;
        } else if (which == "B") {
            SubClassB subClassB;
            return subClassB;
        } else {
            throw std::invalid_argument("Bad");
        }
    }

public:
    explicit Client(std::string &which) : which(which) {
        letterFactory();
    };
};

int main() {
    Client client();
    return 0;
};
Ghasem Ramezani
  • 1,490
  • 6
  • 24
CiaranWelsh
  • 5,590
  • 7
  • 33
  • 73
  • 1
    Your `Client` constructor is actually creating and then discarding a `SuperClass` instance, which is actually suffering from slicing because you are returning by value. – AndyG Dec 20 '19 at 15:29
  • What do you expect the call to `letterFactory` to accomplish? – AndyG Dec 20 '19 at 15:30
  • Please, consider forming the question in a more legible way. – shargors Dec 20 '19 at 15:34
  • @AndyG This is my attempt at abstracting my poker hand evaluator into a MWE. In reality, the `letterFactory` will call each hand type class which is derived from a `Hand` and return the best hand that can be made (i.e. pair two pair etc). – CiaranWelsh Dec 20 '19 at 15:37
  • @shargors Personally I think the question is legible, but always willing to take tips if you think it can be improved? – CiaranWelsh Dec 20 '19 at 15:37
  • This question seems fine to me. I don't see why it was downvoted. – François Andrieux Dec 20 '19 at 15:39

1 Answers1

5

You are not returning the derived class objects you are creating in letterFactory. Instead you are returning a base class object as a sliced copy of the derived class object that you declared in the function.

Calling myMethod on this returned object will call the implementation in SuperClass, because its most-derived type will be SuperClass.

Polymorphism only works on pointers and references. If you want your class to return either of the derived class types, it cannot return by-value. You can return a pointer to a dynamically allocated instance instead:

std::unique_ptr<SuperClass> letterFactory(){
    if (which == "A") {
        return std::make_unique<SubClassA>();
    } else if (which == "B") {
        return std::make_unique<SubClassB>();
    } else {
        throw std::invalid_argument("Bad");
    }
}
walnut
  • 20,566
  • 4
  • 18
  • 54
  • 4
    Relevant link : [What is object slicing?](https://stackoverflow.com/questions/274626/what-is-object-slicing). – François Andrieux Dec 20 '19 at 15:34
  • Hi @walnut, thanks for the answer - knew it would be something to do with pointers. Would you mind elaborating on what `std::make_unique` is doing here? – CiaranWelsh Dec 20 '19 at 15:40
  • 1
    @CiaranWelsh `std::make_unique` dynamically allocates and creates an object of the given type for you and puts it under control of a `std::unique_ptr` instance which it then returns. That way, you don't have to write `return std::unique_ptr{new SubClassA};` which is cumbersome as it repeats the type name twice, requires use of `new` which should be discouraged where possible, and also is easier to make mistakes with especially with regards to exception safety. – walnut Dec 20 '19 at 15:43
  • 1
    @CiaranWelsh [What is a smart pointer and when should I use one?](https://stackoverflow.com/questions/106508/what-is-a-smart-pointer-and-when-should-i-use-one) – François Andrieux Dec 20 '19 at 15:47