Search code examples
c++c++11polymorphism

How to return child class's object when function return type is parent class?


I guess that Java is possible to substitute child class's object for parent class's object. I want to do it in c++.

I'm trying to do it as following. However, I got "error: return type 'Food' is an abstract class" error. How can I fix it?

Before it works:

#include <iostream>

using namespace std;

class Food {
    public:
        virtual void SetPrice(int myprice) = 0;
        int GetPrice() {
            return price;
        }
    protected:
        int price;
};

class Fruit : public Food {
    public:
        void SetPrice(int myprice) {
            price = myprice - 20;
        }
};

class Fish : public Food {
    public:
        void SetPrice(int myprice) {
            price = myprice / 2;
        }
};

int main(int argc, char* argv[])
{
    Food *pFood;
    Fruit myFruit;
    Fish myFish;

    if (strcmp(argv[1], "fruit") == 0) {
        pFood = &myFruit;
    } else {
        pFood = &myFish;
    }

    pFood->SetPrice(100);
    cout << pFood->GetPrice() << endl;

    return 0;
}

After class definition is omitted. it doesn't work:

Food getFoodObject(string type)
{
    if (strcmp(type, "fruit") == 0) {
        Fruit myFruit;
        return &myFruit; // I don't want to write 2 lines. I want to return the above line. This is my another question...
    }

    Fish myFish;
    return &myFish;
}

int main(int argc, char* argv[])
{
    Food *pFood;

    pFood = getFoodObject(argv[1])

    pFood->SetPrice(100);
    cout << pFood->GetPrice() << endl;

    return 0;
}

Update 1

Thanks to many advices, my issue was solved. I need to use c++11 so I use unique_ptr instead of make_unique.

std::unique_ptr<Food> getFoodObject(string type)
{
    if (type == "fruit") {
        return std::unique_ptr<Fruit>(new Fruit);
    }   
   return std::unique_ptr<Fish>(new Fish);
}

int main(int argc, char* argv[])
{
    std::unique_ptr<Food> pFood = getFoodObject(argv[1]);

    pFood->SetPrice(100);
    cout << pFood->GetPrice() << endl;

    return 0;
}

@dlasalle mentioned about Boost library. I will post update after I can use Boost's smart pointer as my note.


Solution

  • You need to return by pointer type or reference to the base class, not by value. This is required since your base class is an abstract class and you cannot instantiate a abstract class. That said you can't just do

    Food* getFoodObject(string type)
    {
        if (strcmp(type, "fruit") == 0) {
            Fruit myFruit;
            return &myFruit; // I don't want to write 2 lines. I want to return the above line. This is my another question...
        }
    
        Fish myFish;
        return &myFish;
    }
    

    Because then you are returning a pointer to a function local object and as soon as the function exits the object is destroyed leaving you with a dangling pointer. What we can do is change the code to use a std::unique_ptr (using a smart pointer as it manages the memory for us) and then you could have something like

    std::unique_ptr<Food> getFoodObject(string type)
    {
        if (type == "fruit") {
            return std::unique_ptr<Fruit>(new Fruit);
            // or in C++14 return std::make_unique<Fruit>();
        }
        return std::unique_ptr<Fish>(new Fish);
        // or in C++14 return std::make_unique<Fish>();
    }
    

    And you would call the function like

    int main(int argc, char* argv[])
    {
        std::unique_ptr<Food> pFood = getFoodObject(argv[1])
    
        pFood->SetPrice(100);
        cout << pFood->GetPrice() << endl;
    
        return 0;
    }
    

    Also note that if (strcmp(type, "fruit") == 0) is invalid. strcmp deals with c-strings, not std::string. To compare a std::string you can just use == like if (type == "fruit")