1

Why does my code return error? I've defined a parent class called Shape and two derived classes. I'm trying to store objects defined by the classes and store them inside a list.

# include <iostream>

using namespace std;

class Shape{
    public:
        virtual double area(const double &height, const double &weight) const = 0;
};

class Triangle: public Shape{
   public:
       double height, weight;
       Triangle(double height, double weight): height(height), weight(weight){}
   double area(){
       return (height*weight)/2;
   }
};

class Rectangle: public Shape{
    public:
        double height, weight;
        Rectangle(double height, double weight): height(height), weight(weight){}
    double area(){
        return height*weight;
    }
};

Shape *shapes[3];
shapes[0] = new Triangle(2, 1);
shapes[1] = new Rectangle(3, 2);
shapes[2] = new Rectangle(5, 2);

double * show(Shape *shapes){
    double arr[3];
    for (int i=0; i < 3; i++){
        arr[i] = shapes[i].area();
    }
    return arr;
}

int main(){
    double arr[3] = show(shapes);
    cout << arr[0] << endl;
    cout << arr[1] << endl;
    cout << arr[2] << endl;
}

But I receive these two errors:

error: 'shapes' does not name a type Shapes[0] = new Trangle(2,1);

error: cannot convert 'shape**' to 'shape*' double arr[3] = show(shapes);

bitWise
  • 699
  • 5
  • 11
  • `shapes[0] = new Triangle(2, 1);` etc - you can't have code in the global scope. Move those statements inside `main()`. – Sid S Feb 01 '20 at 06:24
  • 1
    Also, `show()` returns a pointer to a local variable. That's UB. – Sid S Feb 01 '20 at 06:26
  • Besides Sid S's reply, Your ``Shape* shapes[3];`` defines an array of size 3 of type ``Shape*``. The name of an array is a pointer to the first element (let's not talk about decay here), so ``shapes`` has a type of ``Shape**``. Your ``shapes[i].area()`` should be ``shapes[i]->area()``. Your pure virtual function ``area()`` isn't properly overrided in your derived class. – sz ppeter Feb 01 '20 at 06:42
  • @Sid S. I've done what you told me, but I receive this error: Invalid new-expression of abstract class type 'Triangle'. – bitWise Feb 01 '20 at 06:47
  • @szppeter - Sorry, that's wrong. The name of an array is not a pointer. The name of an array can be used *as if* it is a pointer in some contexts, and implicitly converted to a pointer in some contexts (what some people call "decay"), but it is not a pointer. Certainly, an array of `Shape *` and a `Shape **` are different things entirely. – Peter Feb 01 '20 at 06:49

1 Answers1

3

Your code has several problems:

  1. Your abstract base class Shape has a pure virtual function area() which accepts parameters, but your derived class does not. So it makes your derived class abstract too, so you cannot initialize an derived class object.
  2. You cannot declared an array of pointer Shapes* shapes[3]; and then assign them seperately in the global scope. That's the 1st error you are getting. More complete explanation see here
  3. When you call your show(shapes) function, shapes is a name of Shapes* array, it decays to a pointer to the first element. The resulting type should be Shapes**, that's the 2nd error you are getting.
  4. Your shapes[i]in the show() function is a pointer pointing to an object, so you should use -> instead of .
  5. Your double arr[3]; is an array on the stack, so after the function is finished and return, the contents are destoryed. Returning the pointer to the destoried element is undefined.
  6. You did not delete all the object created by new, causing memory leak.

The minimum changes to your code to work properly is:

# include <iostream>

using namespace std;

class Shape {
public:
    virtual double area() const = 0;
};

class Triangle : public Shape {
public:
    double height, weight;
    Triangle(double height, double weight) : height(height), weight(weight) {}
    double area() const override {    //add override to let compiler check for correctness
        return (height * weight) / 2;
    }
};

class Rectangle : public Shape {
public:
    double height, weight;
    Rectangle(double height, double weight) : height(height), weight(weight) {}
    double area()const override {
        return height * weight;
    }
};

Shape* shapes[3] = { new Triangle(2, 1),  new Rectangle(3, 2) ,new Rectangle(5, 2) };

double* show(Shape** shapes) {
    double* arr = new double[3];
    for (int i = 0; i < 3; i++) {
        arr[i] = shapes[i]->area();
        delete shapes[i];    //free the object pointed in shapes[]
    }
    return arr;
}

int main() {
    double* arr = show(shapes);
    cout << arr[0] << endl;
    cout << arr[1] << endl;
    cout << arr[2] << endl;
    delete[] arr;
}

But there are some recommendations to improve, like creating object on the stack without using new instead of heap, and using std::array to create arrays, etc.

sz ppeter
  • 1,698
  • 1
  • 9
  • 21
  • What version of C++ should I use? – bitWise Feb 01 '20 at 08:21
  • @bitWise The ``override`` keyword is introduced in C++11, but you can removed it (**!strongly unrecommend!**) to make it compatible with C++98, the oldest standard. This functioning code is very "bad" if using new standard is possible. – sz ppeter Feb 01 '20 at 08:31