Search code examples
c++derived-classforward-declaration

C++: How can I forward-declare derived classes that appear in a static method of a base class?


Just doing a simple exercise where I'm translating ideas I learned from another language to C++.

I have an abstract class Number, which has two derived classes, PositiveNumber and NegativeNumber. Number has a static method that should create a new instance of either PositiveNumber or Negative number, depending on the sign of its input.

#include <iostream>

class Number
{
public:
protected:
    int magnitude_;
public:
    static Number* fromInt(int x)
    {
        if (x >= 0) { return new PositiveNumber(x); }
        else        { return new NegativeNumber(x); }
    }
    int getMagnitude() { return magnitude_; }
    virtual void print() = 0;
};

class PositiveNumber: public Number
{
protected:
public:
    PositiveNumber(int magnitude) { magnitude_ = magnitude; }
    void print() { std::cout << magnitude_ << "\n"; }
};

class NegativeNumber: public Number
{
protected:
public:
    NegativeNumber(int magnitude) { magnitude_ = magnitude; }
    void print() { std::cout << "-" << magnitude_ << "\n"; }
};


int main (int argc, char* argv[])
{
    Number* x = Number::fromInt(5);
    x->print();

    return 0;
}

I know that I need to tell Number that PositiveNumber and NegativeNumber exist, but I'm not sure how to do that. I tried adding

class PositiveNumber;
class NegativeNumber;

before Number's definition, but that wasn't enough, and resulted in:

use of undefined type 'PositiveNumber'
use of undefined type 'NegativeNumber'

I'm sure this has a simple answer, but I'm pretty new to debugging C++ stuff, so I'm pretty lost. Thanks for reading.


Solution

  • The definition of fromInt() needs to know what constructors PositiveNumber and NegativeNumber have, so forward declarations are not enough. You need to break up the declaration and definition of Number::fromInt(), and then you can move the definition underneath the declarations of PositiveNumber and NegativeNumber.

    Also, don't forget to delete the object that fromInt() new's. Which also means adding a virtual destructor to Number so derived destructors can be called correctly from a base Number* pointer.

    Try this:

    #include <iostream>
    
    class Number
    {
    protected:
        int magnitude_;
    public:
        static Number* fromInt(int x);
    
        virtual ~Number() {}
    
        int getMagnitude() { return magnitude_; }
        virtual void print() = 0;
    };
    
    class PositiveNumber: public Number
    {
    public:
        PositiveNumber(int magnitude) { magnitude_ = magnitude; }
        void print() { std::cout << magnitude_ << "\n"; }
    };
    
    class NegativeNumber: public Number
    {
    public:
        NegativeNumber(int magnitude) { magnitude_ = magnitude; }
        void print() { std::cout << "-" << magnitude_ << "\n"; }
    };
    
    Number* Number::fromInt(int x)
    {
        if (x >= 0) { return new PositiveNumber(x); }
        else        { return new NegativeNumber(x); }
    }
    
    int main (int argc, char* argv[])
    {
        Number* x = Number::fromInt(5);
        x->print();
        delete x;
    
        return 0;
    }
    

    Online Demo