Question

I know this has been posted plenty of times before, but none of the solutions were able to help. I'm trying to create multiple subclasses from one superclass (in this instance, Value->Fraction->RationalFraction and Value->Fraction->IrrationalFraction), but I keep getting this error. I assume it's from a circular inclusion, but I don't know how to make the Fraction code work without it. When I construct Fraction, depending on the constructor, it creates an Irrational or Rational Fraction, but then I need to include Fraction in those subclasses, and it kind of creates a cluster. The error is coming in RationalFraction and IrrationalFraction, and I can't seem to get around it. Is there a smoother way to implement this or at the least a way to fix the error? Sorry if this has been answered already, I'm still new to polymorphism.

Value.h

#ifndef VALUE_H
#define VALUE_H

#include <string>
using namespace std;

class Value
{
public:
    Value();
    virtual ~Value();
    string type;
    virtual string getType() = 0;

protected:
private:
    virtual Value* simplify() = 0;
};

#endif // VALUE_H

Fraction.h

#ifndef FRACTION_H
#define FRACTION_H
#include "Value.h"
#include "RationalFraction.h"
#include "IrrationalFraction.h"
#include <string>

using namespace std;


class Fraction: public Value
    {
    private:
    RationalFraction* rtF;
    virtual Fraction* simplify() = 0;
    IrrationalFraction* irF;
public:
    Fraction(int n, int d);
    Fraction(string n, int d);
    virtual ~Fraction();
    virtual string getType() = 0;
    int numer;
    int denom;
protected:

};

#endif // FRACTION_H

Fraction.cpp

#include "Fraction.h"

#include <iostream>

using namespace std;

Fraction::Fraction(int n, int d) {
    rtF = new RationalFraction(n,d);
}
Fraction::Fraction(string n, int d){
    irF = new IrrationalFraction(n, d);
}

Fraction::~Fraction()
{
    delete rtF;
    delete irF;
}

IrrationalFraction.h

#ifndef IRRATIONALFRACTION_H
#define IRRATIONALFRACTION_H

class IrrationalFraction : public Fraction
{
    public:
        IrrationalFraction(string n, int d);
        virtual ~IrrationalFraction();
    protected:
    private:
        IrrationalFraction* simplify();
};

#endif // IRRATIONALFRACTION_H

RationalFraction.h

#ifndef RATIONALFRACTION_H
#define RATIONALFRACTION_H

using namespace std;


class RationalFraction: public Fraction
{
    public:
        RationalFraction(int n, int d);
        virtual ~RationalFraction();
        int numer;
        int denom;
    protected:
    private:
        RationalFraction* simplify();
};

#endif // RATIONALFRACTION_H

Thanks guys!

Here's the error message: include\RationalFraction.h|8|error: expected class-name before '{' token| include\IrrationalFraction.h|5|error: expected class-name before '{' token| ||=== Build failed: 2 error(s), 0 warning(s) (0 minute(s), 0 second(s)) ===|

Was it helpful?

Solution

1 First of all you need to replace the includes of RationalFraction.h and IrrationalFraction.h with forward class declarations, like this:

class RationalFraction;
class IrrationalFraction;

2 Second, you need to add those includes in file Fraction.cpp.

3 Third, you need to add includes of Fraction.h in RationalFraction.h and IrrationalFraction.h.

4 Fourth, you need to add implementations of getType in both those classes.

5 Fifth, to avoid serious problems with name collisions and such, you need to remove using namespace std; from the headers.

6 Sixth, to avoid double deletions, which is Undefined Behavior, you have to either disallow copying, or handle copying. One way to handle copying is to use smart pointers instead of raw pointers. Another is to define copy constructor and copy assignment operator (that's a bit over-simplified, but you can easily find the details: google for "rule of three").

7 The constructor of Fraction unconditionally calls the constructor of IrrationalFraction, which calls the constructor of Fraction. This is an infinite recursion which must be resolved some way. You'll find that when you start testing.


The design here looks very Java-esque.

Have you considered class templates?

OTHER TIPS

Line 8 of RationalFraction.h says

class RationalFraction: public Fraction

but there's no Fraction declared anywhere in that file. You have to

#include "Fraction.h"

if you plan to use things declared within.

Note that Fraction.h includes RationalFraction.h iteslf. If you don't fix that, you will have a case of circular header dependencies problem, which basically means noithing will work. You probably want to use forward declarations of RationalFraction and IrrationalFraction instead of including their headers.

I don't think your design is very good, but apart form that, there are some obvious issues:

1 your using names that have not been declared, such Fraction in RationalFraction.h.

2 you can use a pointer to an object (like RationalFraction* rtF;) without a definition of the object: all you need is a declaration.

You miss some "#include"s.

When you implement inheritance, the compiler expect that you will Inherit from class. And the compiler don't know "Fraction" is a class, because you dont include his .h file

you can't have classes like RationalFraction and Fraction that embed each others. You should use a technique called forward declaration (What are forward declarations in C++?). In few words, this means that you can declare two classes RationalFraction and IrrationalFraction like below, without include the two headers file. What you need is simply keep two pointers to these classes, so the pointers size is fixed and know by the compiler (4 or 8 bytes, depends by your PC's processor architecture).

#ifndef FRACTION_H
#define FRACTION_H
#include "Value.h"
#include <string>

using namespace std;

class RationalFraction;
class IrrationalFractional;
class Fraction: public Value
    {
    private:
    RationalFraction* rtF;
    virtual Fraction* simplify() = 0;
    IrrationalFraction* irF;
public:
    ...
    ...
};

You must include Fraction.h in RationalFraction.h and IrrationalFraction.h. Additionally, when you include RationalFraction.h and IrrationalFraction.h in Fraction.h, the compiler happens to find the class declaration

class  RationalFraction: public Fraction

before having seen the declaration of Fraction. You can only use fully declared classes as base-classes. You could solve this with forward declaring RationalFraction and IrrationalFraction in Fraction.h:

#ifndef FRACTION_H
#define FRACTION_H
#include "Value.h"
#include <string>

using namespace std;

class RationalFraction;
class IrrationalFraction;

class Fraction: public Value
    {
    private:
    RationalFraction* rtF;
    virtual Fraction* simplify() = 0;
    IrrationalFraction* irF;
public:
    Fraction(int n, int d);
    Fraction(string n, int d);
    virtual ~Fraction();
    virtual string getType() = 0;
    int numer;
    int denom;
protected:

};

#endif // FRACTION_H

This informs the compiler that there are classes RationalFraction and IrrationalFraction. You can not do very much with forward-declared types because the compiler does not know anythign about the class, especially the object's size. But what you can do is delcare pointers and references to them, so for Fraction this would be enough.

I am not sure what happens in Fraction::Fraction when you instantiate the subclasses in the base-class constructor. My guess would be that this is an infinite recursion. I guess you want something like a factory, but it does not work this way.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top