0

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)) ===|

user3502205
  • 41
  • 2
  • 7
  • 1
    Check your includes. You are missing a few. – juanchopanza Apr 05 '14 at 21:07
  • 1
    Where *IS* the error? – user3344003 Apr 05 '14 at 21:09
  • Note that mathematical sets don't map naturally to object classes. E.g. while every integer is a rational number, it's not very natural to have a class `Int` derived from class `Rational`. Deriving the other way would be compatible with the basic idea of object class derivation as **extension**, but would unnaturally say that every rational is an integer... You can find more info about it by googling circle/ellipse problem. Or, I think you can. – Cheers and hth. - Alf Apr 05 '14 at 21:16
  • If I tried to assign a Fraction object to another Fraction object, your program will more than likely blow up. All of your classes violate the "rule of 3". `int main() { Fraction f(1,1); Fraction f2 = f1; }` Just that 2 line program shows a double deletion error. Run that program, and see if on exit of main(), you get a diagnostic that you're freeing memory that has already been freed. – PaulMcKenzie Apr 05 '14 at 21:28

6 Answers6

1

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?

Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331
  • No, I haven't. This is really only my second semester working with code, and we actually haven't done much (if anything) involving polymorphism. Right now, I'm just trying to get the classes to work, because all of the actual calculations are going to be handled with other (already created) methods. – user3502205 Apr 05 '14 at 21:40
  • Alright, so I'm considering scrapping out the subclass constructor in fraction. My only issue then is, the other methods (being add, subtract, multiplication, and divide) are set to return a Value, as opposed to returning an actual subclass. This was the only way I could think to ensure that the actual subclass simplify method was called. So to prevent that, should I have the other methods return the proper subclass? So if a RationalFraction and RationalFraction are added, it returns a RationalFraction, for example? – user3502205 Apr 05 '14 at 21:56
  • Apparently the purpose is to be able to handle general numerical `Value`s and perform arithmetic operations on them, retaining as much information as practical on the inside. For this I think it's a good idea to think in terms of two different sets of classes: public classes handled by client code, and implementation classes used for the retaining information bit, such as exact fraction. Then each `Value` class instance contains a pointer to polymorphic implementation class instance. A main problem is to define operators for all possible combinations of implementation instance arguments. – Cheers and hth. - Alf Apr 05 '14 at 22:04
  • looks like you gave him much more help than we usually do with homework questions – Walter Apr 05 '14 at 22:54
0

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.

n. m. could be an AI
  • 112,515
  • 14
  • 128
  • 243
0

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.

Walter
  • 44,150
  • 20
  • 113
  • 196
0

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

asaf app
  • 384
  • 2
  • 3
  • 12
0

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:
    ...
    ...
};
Community
  • 1
  • 1
prisco.napoli
  • 606
  • 6
  • 9
  • Okay, so if I format my code like this, I can still have a .cpp file for RationalFraction? Because I'm going to have to pass down simplify for use in the two subclasses. – user3502205 Apr 05 '14 at 21:31
  • Yes, of course you can. You should include the Fraction.h header both in RationalFraction.h and IrrationalFraction.h. And then include RationalFraction.h in RationalFraction.cpp, and IrrationalFraction.h in IrrationalFraction.cpp – prisco.napoli Apr 05 '14 at 21:37
0

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.

Jens
  • 9,058
  • 2
  • 26
  • 43