-2

So I saw this thread: What does it mean to "program to an interface"?. Which talks about declaring a Parent, but initializing it as a Child. Is it possible to do the same but with c++? For example: I have an interface Shape which can be implemented as Triangle or Square.

I tried to do the following but my program didn't compile:

Shape * myShape = new Square();
myShape->uniquetoSquare();
"typeinfo for Shape", referenced from:
      typeinfo for Triangle in Triangle.o
class Shape {
  public:
    Shape(){};

    int height;
    int width;
    string color;

    void sayHello(){ 
       cout << "Hello!";
    }
    int getArea(){
      return 0;
    }
}
class Triangle: public Shape {
  public:
    bool obtuse;
    
    Triangle(){
      obtuse = false;
    };
    void sayHello(){ 
       cout << "Hello I'm a triangle!";
    }
    int getArea(){
      return height*width / 2;
    }
}
class Square: public Shape {
  public:
    bool rectangular
    Square(){
      rectangle = true;
    };
    void sayHello(){ 
       cout << "Hello I'm a square!";
    }
    int getArea(){
      return height*width;
    }
    void uniqueToSquare(){
      cout << "This func is only in square!";
    }
}

Cris
  • 209
  • 2
  • 10
  • 1
    With pointer, you use `->` instead of `.`. Thus, `myShape.uniqueToSquare()` would be `myShape->uniqueToSquare()`. If you want more help, you should probably post the error message from the compiler. – ChrisMM Mar 25 '21 at 22:39
  • @ChrisMM Thanks, just updated with the error message. – Cris Mar 25 '21 at 22:43
  • 1
    For a "clean" interface make shape have all pure virtual methods and a virtual destructor only. – Richard Critten Mar 25 '21 at 22:47
  • 1
    `sayHello()` and `getArea()` need to be declared `virtual` in `Shape` in order for `Triangle` and `Square` to override them – Remy Lebeau Mar 25 '21 at 22:52
  • A lot of time in C++ you can get away with compile-time polymorphism using templates and type deduction. As an example, look at all the sequential containers (`std::vector`, `std::list`, `std::deque`, etc.). They have common interfaces but do not share a base class, yet they can be used interchangeably in many cases. – François Andrieux Mar 25 '21 at 23:23

3 Answers3

4

Shape does not have a function named uniqueToSquare. Remember that if you are using a Shape, then you can only use shape-like methods.

If you want to use it as a Square, then do something like:

Square* mySquare = dynamic_cast<Square*>(myShape);
if (mySquare != nullptr) mySquare->uniqueToSquare();
paddy
  • 60,864
  • 6
  • 61
  • 103
2

This is a simple polymorphism example. Yes it is possible in C++ and is actually the heart of the benefits of Object-Oriented design

With C++ it isn't as simple as you might hope. You need virtual functions to do what you want. Modifying your example to be "good" C++.

class Shape {
  public:
    Shape(){}

    int height;
    int width;
    string color;

    virtual void sayHello() const { 
       cout << "Hello!";
    }
    virtual int getArea() const {
      return 0;
    }
    //virtual destructor, no need to declare this on the derived types
    virtual ~Shape() {}
}
class Triangle : public Shape {
  public:
    bool obtuse;
    
    Triangle() {
      obtuse = false;
    }
    void sayHello() const { 
       cout << "Hello I'm a triangle!";
    }
    int getArea() const {
      return height*width / 2;
    }
}
class Rectangle : public Shape {
  public:
    bool square;
    Rectangle() {
      square = false;
    }
    void sayHello() const { 
       cout << "Hello I'm a rectangle!";
    }
    int getArea() const {
      return height*width;
    }
    void uniqueToRectangle() const {
      cout << "This func is only in rectangle!";
    }
}

The error your getting seems to be because of missing runtime type information RTTI. You may want to enable this but it really is unnecessary for what you want to achieve. Use dynamic cast as others have suggested (which also uses RTTI).

Shape* myShape = new Rectangle();
((Rectangle*)myShape)->uniquetoRectangle();

This is OK but remember to delete myShape; before it goes out of scope. See RAII for the use of destructors for that.

ceorron
  • 1,230
  • 1
  • 17
  • 28
  • 3
    You say to use a dynamic cast to access `uniquetoSquare()`, but you don't actually show a dynamic cast (ie `dynamic_cast(myShape)`). What you showed is a C-style cast, which would be better handled using `static_cast(myShape)` instead. Note that `dynamic_cast` is a run-time cast and returns `NULL`/`nullptr` if the cast fails, but `static_cast` is a compile-time cast and does not fail (but will lead to Undefined Behavior if `myShape` is not pointing at a `Square` object). Use `static_cast` when you *know* the type, otherwise use `dynamic_cast` to *discover* the type. – Remy Lebeau Mar 25 '21 at 22:56
  • 1
    I'd rephrase the last sentence as “use `static_cast` when you *know* the type, use `dynamic_cast` when your design is broken”. – spectras Mar 25 '21 at 23:21
-1

Your variable myShape is a pointer to a Shape. Your class Shape does not have a method called uniqueToSquare(). Therefore, you have an error.

The solution is to initialise myShape as a Square simply by Square myShape = Square();

Remember that it is always better to preserve the real type of an object, this is better for performance, and basically gives more information to the compiler. You only resort to dynamic polymorphism when you really have to (ie, storing polymorphic objects in an array).

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
SomeProgrammer
  • 1,134
  • 1
  • 6
  • 12
  • 1
    I think the point of this task IS to use polymorphism. – user4581301 Mar 25 '21 at 23:09
  • It is unclear whether op actually wants to use polymorphism here, since he clearly accesses a method specific to Square... – SomeProgrammer Mar 25 '21 at 23:11
  • There’s a difference between implementing a base class interface and dictating what methods a derived class should have, and actually use a polymorphic object which can change type. – SomeProgrammer Mar 25 '21 at 23:15