-1

I want users of my class "ShapeHolder" to be able to set the "Shape shape" member variable to be any class with the same interface (int w/h, getArea(), etc). Therefore, I believe I made the base class an abstract class by setting a function to virtual (?), and added derived classes to act as the various implementations of shape.

However, this current code throws an error where the Shape shape member variable is defined, saying that I "cannot declare field 'ShapeHolder::shape' to be of abstract type 'Shape'".

In the perfect world, I would love the user to be able to hand in the enum corresponding to the class they want to be represented by the Shape member variable. Is there a way to do this?

Is there a better method all together than the base:derived class interface I am trying to create here?

I have the following example program to illustrate my question:

#include <iostream>
#include <string>

using namespace std;

typedef int shape_type;
enum {SQUARE, TRIANGLE, REGTANGLE};

// Base class
class Shape {
   public:
      Shape(int h, int w){
        height = h;
        width = w;
      };
      virtual int getArea() = 0;
      //int getArea(){return -999;};
   
   private:
      int width;
      int height;
};
 
// Derived classes
class Rectangle: public Shape {
  public:
    Rectangle(int h, int w) : Shape(h, w){
      height = h;
      width = w;
    };
    int getArea() { 
        return (width * height); 
    }
  private:
      int height;
      int width;
};

class Triangle: public Shape {
  public:
    Triangle(int h, int w) : Shape(h, w){
      height = h;
      width = w;
    };
    int getArea() { 
        return (width * height)/2; 
    }
  private:
      int height;
      int width;
};

// Main Class
class ShapeHolder {
  public:
    ShapeHolder(int ver, string s, shape_type st):
      dummyInt(ver), dummyString(s), shape(ver, ver){};
    void printStuff(){
        cout << shape.getArea() << endl;
    }
  private:           
    int dummyInt;
    string dummyString;
    // this type should be dependent on shape_type
    Shape shape;

};

int main() {
  ShapeHolder sh(10, "test", TRIANGLE);
  sh.printStuff();
  return 0;
}
  • 3
    In C++, polymorphism is done via a *pointer* or a *reference*. (Including *smart pointers*.) – Eljay Jun 15 '22 at 21:18
  • What don't you understand, that creation of abstract classes is not possible? That's just much of their purpose, that you need a derived class, implementing the abstract functions is required to create a final instance. – πάντα ῥεῖ Jun 15 '22 at 21:20
  • 1
    most straight forward way IMO would be to do `Shape* shape;` , then in the `ShapeHolder` ctor, use a lookup table on `st` to see which shape to create: `if (st == TRIANBLE) shape = new Triangle(ver ver); ` .. etc. Of course smart pointers are preferred. – yano Jun 15 '22 at 21:21

2 Answers2

0

Your class Shape is an abstract class and lacks definition's for it's pure virtual function.

Fix this by converting Shape shape inside your ShapeHolder class to a pointer instead, whichs allows any derived Shape class to be used.

Also, marking the width & height variable's inside the base class protected allows derived classes to access them.

EtaBeta
  • 1
  • 2
  • The compiler refuses to instantiate an abstract class because it’s not allowed. That has nothing to do with undefined behavior. “Undefined behavior” means that the language definition doesn’t tell you what a program that has it does. Here, it tells you: the program is ill-formed, and the implementation must issue a diagnostic. – Pete Becker Jun 15 '22 at 22:00
0

In a class once you declare a member function pure virtual, you can't instantiation an object of that class.

So in your ShapeHolder class, declaring the shape variable as Shape shape will cause a compilation error.

error: cannot declare field ‘ShapeHolder::shape’ to be of abstract type ‘Shape’
   65 |     Shape shape;
      |           ^~~~~

You can't directly create a variable of abstract class but you can keep a pointer or reference to abstract class.

Abstract class is like an interface/contract between abstract class and derived class. Any class which is derived from abstract class must implement this interface/contract.

Change your base class as follows:

class Shape {
public:
    Shape(int h, int w) : height{h}, width{w} {}
    virtual int getArea() = 0;

// protected instead of private so that derived classes can access it
protected:
    int height;
    int width;
};
class ShapeHolder {
public:
    ShapeHolder(int h, int w, string s, shape_type st): dummyString(s) {
        // Based on st, create either Triangle or Rectangle
        switch (st)
        {
        case TRIANGLE:
            shape = std::make_unique<Triangle>(h, w);
        break;
        case REGTANGLE:
            shape = std::make_unique<Rectangle>(h, w);
        break;
        }
    };

    void printStuff(){
        cout << shape->getArea() << endl;
    }

private:
    string dummyString;
    std::unique_ptr<Shape> shape;  // pointer to keep track of shape
};

In your Triangle and Rectangle classes no need to have width and height variables, they will have access to base class width and height. So

class Rectangle: public Shape {
public:
    Rectangle(int h, int w) : Shape(h, w) {}
    int getArea() override { return (width * height); }
};

class Triangle: public Shape {
public:
    Triangle(int h, int w) : Shape(h, w) {}
    int getArea() override { return (width * height)/2; }
};

In the main function:

int main() {
  ShapeHolder shape1(10, 20, "tri", TRIANGLE);
  shape1.printStuff();

  ShapeHolder shape2(5, 8, "rect", REGTANGLE);
  shape2.printStuff();
}

Output:

100
40
Aamir
  • 1,974
  • 1
  • 14
  • 18
  • Thank you! Is a destructor when using smart pointers the way you did? –  Jun 16 '22 at 12:55
  • I’m not sure what you are asking. If you use smart pointer, no need to destroy them in destructor. Smart pointer use a concept call RAII. – Aamir Jun 16 '22 at 13:41