0

I'm working on a university assignment about finding the array of different shapes using OOP. I have created all of my shape classes so they derive from my main Shape Class. Shape class is being used like an interface, so that all the shape classes derived from the shape class have to have a calculate area function, etc. I want to create an array of different shapes. I have declared an array with a type of shape, which is the parent class and i want to add new instances of each different shape, circle, square, rectangle and triangle to the array list so that all the information about each shape is stored in one array. I have the follow code but i have an error on the line aShapes[i] = square; saying that shape is an inaccessible base of square.

If anyone could help out that would be great.

Thanks in advanced.

Here is my code

#include <cstdlib>
#include <iostream>
#define M_PI 3.14159265358979323846

using namespace std;

class Shape{    
public:
    string sName;
    float nArea;
    void fnAddData();
    float fnCalculateArea();     
};

class Square : private Shape {
private:
   float nSide;   
    void fnAddData()
    {
        cout << "Please enter the length of a side: ";
        cin >> nSide;
    }
    float fnCalculateArea(float side)
    {
        return (side * side);
    }  
public:
    Square()
    {
        sName = "Square";
        fnAddData();
        nArea = fnCalculateArea(nSide);
    }        
};

Shape aShapes[5];


/*
 * 
 */
int main(int argc, char** argv) 
{
    int decision;    

Square square;
for (int i = 0; i < 5; i++)
{
    cout << "Shape number";
    cin >> decision;

    switch (decision)
    {
        case 1:
            aShapes[i] = square;
    }                        
}    
return 0;
}
CraigWake
  • 307
  • 1
  • 3
  • 7

4 Answers4

3

Arrays aren't polymorphic: Everything stored in the array must be the same type. Your array of Shape won't work.1

So how do you get polymorphism when using an array, then? Change your array to store pointers to Shape:

 Shape *aShapes[5];

That solves the problem nicely: A Shape* can point to a Shape or any of its descendants,2 and all the Shape* themselves are the same type.

And then in your main code, store the addresses of the objects in the array:

 aShapes[i] = &square;

You will need to make a couple other changes to your existing code to make this work:


1 If you do manage to copy a descendent of Shape into the array, you end up slicing the object.

2 Technicality: If you give Shape pure virtual methods, you won't be able to create an instance of a pure Shape. In that event, Shape* can't point at an instance of Shape because you can't create one! A Shape* would only point to one of its descendents that implements all of the virtual methods.

Community
  • 1
  • 1
Joe Z
  • 17,413
  • 3
  • 28
  • 39
1

The reason you're getting that particular error is because you're using private inheritance when it looks like you really want to be using public inheritance.

class Square : public Shape {
               ^^^^^^

A further problem is that polymorphism works only with pointers or references. You cannot put a Square into an array of Shapes. It will simply slice out the Shape part of the Square and put that in the array. Shape aShapes[5]; is nothing more than an array of Shapes - there are no Squares, no Triangles, just Shapes. What you need is something like:

Shape* aShapes[5];

You can then set the pointers in this array to point at objects of types that derive from Shape.

You'll also need to make the functions that you want to override in the derived classes virtual. When the compiler sees something like aShapes[0]->fnCalculateArea(), and then sees that fnCalculateArea is a virtual function, it will only then look up the dynamic type of the object that aShapes[0] points to - it will then see that it's a Square and call Square::fnCalculateArea.

Joseph Mansfield
  • 108,238
  • 20
  • 242
  • 324
  • how do you point to the constructor. Ive never used pointers before, they confuse me a bit. In my constructor i call fnCalculateArea, etc, so I just want the constructor to happen – CraigWake Dec 20 '13 at 14:45
  • If your shapes aren't already statically allocated, you can dynamically allocate them: `aShapes[i] = new Square;` In that case, you must remember to deallocate it too when you're done with it: `delete aShapes[i];` – derpface Dec 20 '13 at 14:51
1

1) You're privately inheriting from Shape, which is more typically an undesirable form of composition. You should be publicly inheriting from Shape for your purposes.

    class Square : public Shape

2) None of Shape's member functions are declared virtual, so none of them will be overridden by derived classes. You also don't have a virtual destructor. If you want the CalculateArea function of Square to be used when you call it on a Shape pointer that points to a Square, you have to declare as virtual in Shape, and override it in Square:

In Shape:

    virtual float fnCalculateArea();

In Square:

    void float fnCalculateArea() override;

3) If Shape should act only as an abstract interface, not to be instantiated itself, then you should make it so by making one of its functions pure virtual (the destructor at least, if nothing else).

    virtual ~Shape() = 0 {}

4) If you want to store the different derived types in a single container, then you have to store them by reference, IE through a pointer to their base class.

    Shape* aShapes[5];

5) I also noticed that you're prefixing many variable names with n even though they are floats. Hungarian notation typically uses n to refer to ints and f to refer to floats.

derpface
  • 1,611
  • 1
  • 10
  • 20
  • if i make the fnCalculateArea in shape virtual and override it in the square it gives me the error member function declared with override does not override does not override a base class member. Any ideas? Thanks – CraigWake Dec 20 '13 at 15:09
  • If you get that error, it means something in Square does not match the declaration in Shape. In your original code, Square's version takes in a float, while Shape's does not. Your Square class has a `nSide` data member, and your Square's function takes in a `side` argument. Which one did you intend to use? I would assume you want to use the data member `nSide`, but decide on that, then make both versions of the function match. – derpface Dec 20 '13 at 15:24
  • I do that because I have other shapes, including circle and rectangle and triangle so they each use different parameters to work out the area therefore I dont want to take in a variable in the shape class. I want it to have overloads. Im not sure if i can do this or not? – CraigWake Dec 20 '13 at 16:17
  • nSide and side are the same, its just the naming convention for the parameter being passed in is different to that or the actual variable. – CraigWake Dec 20 '13 at 16:18
  • You could "percolate up" all possible varieties of the function needed into the base class, but that's not the most elegant solution. Instead, you should probably store the needed information as data members through the constructor or mutators, and let fnCalculateArea take nothing in but instead use those data members. – derpface Dec 20 '13 at 16:22
  • I didnt think of that! Thanks. Also if i wanted to access a function of a class ive created an instance of how would i do that? Im doing this at the moment aShapes[nShapeID].fnAddData(); but it doesnt seem to like it. Sorry im very new with OOP – CraigWake Dec 20 '13 at 16:29
  • If you're trying to access a function of the derived type that does not exist in the base type, then you must be making an assumption about what the Shape* is actually pointing to, in which case you don't need a Shape*. If you have a Shape* because that's the only type you can retrieve from a given container, but you can somehow safely assume that it points to a Square, then you can simply `static_cast(aShapes[nShapeID])` and catch the result in a Square*, from which you then access Square-specific functions. – derpface Dec 20 '13 at 16:32
  • If you're doing this at creation, you can simply instantiate a Square* to a new Square, call all the Square-specific functions you need to, and then assign a Shape* to the Square* (upcasting): `Square* pSquare = new Square; pSquare->SomeFunc(); aShapes[i] = pSquare;` – derpface Dec 20 '13 at 16:36
0

Arrays of values do NOT behave polymorphically. You will have to use an array of pointers to Shape, as access and assignment to pointers does behave polymorphically.

What you have here is object slicing, you're cutting off the Derived class chunk so that your object fits into a Base. This is bad.

With a pointer, however, you get no slicing.

Your particular error stems from use of private inheritance, which forbids conversion to the inherited class. Use public inheritance. This is just masking the issue above.