0

Introducing my issue with some comments. Each comment refers to the code below:


comment1: myShapeVector stores Shape*
comment2: Adding up the vector either with with circle or rectangle objects
comment3: The returnPtr() member function is intended to be used for returning either a Circle* or an Rectangle* depending on which id is passed, it is intended to used as a tool to convert the myShapeVector[i] element (which is an object*) to an circle* or rectangle *. I thought using a template class is necessary because only those can return different pointers
comment4: Additionally id is used to refer to a certain element of the vector
comment5: Converting an ShapePtr* to Circle* by static_cast conversion works fine
comment6: This line returns an error. Somehow the returnPtr() does not return the expected Circle*. From my point of view a template class is feasible of returning different pointers*. The T returnPtr(int id) looks fine to me.

#include <iostream>
#include <vector>

class Shape {};
class Circle : public Shape {};
class Rectangle : public Shape {};

template <class T, class d, class c> class ShapeVector {
public:
  T returnPtr(int id) {                                  //comment 3
    if (id == 0) {
      return (static_cast<Circle *>(myShapeVector[0]));  //comment 4
    } else if (id == 1) {
      return (static_cast<Rectangle *>(myShapeVector[1]));
    } else {
      return nullptr;
    }
  }
  std::vector<Shape *> myShapeVector;                  // comment 1
};

int main() {
  ShapeVector<Shape *, Rectangle *, Circle *> myShapes;
  myShapes.myShapeVector.push_back(new Circle);         //comment 2
  myShapes.myShapeVector.push_back(new Rectangle);
  Circle *b = myShapes.returnPtr(0);                    //comment 6 / please see errors below
  Circle *c = static_cast<Circle *>(myShapes.myShapeVector[0]); //comment 5

  return 0;
}
a value of type "Shape *" cannot be used to initialize an entity of type "Circle *" C/C++(144)
invalid conversion from ‘Shape*’ to ‘Circle*’ [-fpermissive] gcc failure message

For sure the code lacks of knowledge using templates.

  1. At first stage my main concern is about how to return child class* from an vector which which contains parent class*?
  2. Is it correct that a template class is needed for this procedure?
KeepRunning85
  • 173
  • 10
  • 2
    `> Converting an ShapePtr* to Circle* by static_cast conversion works fine` - _appears_ to work fine, but you are risking Undefined Behaviour. – alter_igel May 28 '21 at 14:45
  • 2
    This is a lot of unnecessarily complicated and brittle code. In general, it's frowned-upon to be up-casting pointers as part of your design, and you don't need templates to achieve even that. You should instead use `virtual` member functions to achieve what you want in an inheritance hierarchy. – alter_igel May 28 '21 at 14:47
  • Do you mind explaining more concretely what you want to achieve with these `Shape*` pointers? – alter_igel May 28 '21 at 14:48
  • Thank you for commenting. The intention is that the child class objects(e.g. circle, rectangle) have certain member variables. For example only a circle has a diameter, a rectangle has side length. The idea is: 1. having a std::vector myShapes 2. Adding this vector up with elements myShapes.pushback(new Circle) 3. Accessing it by something like mySHapes[0]->diameter; But of course rightnow myShapes only returns Shape-pointers 4. So speaking in other words I tried to build a workaround to convert the unspecified myShape elements in specified pointers. – KeepRunning85 May 28 '21 at 14:53
  • 1
    Related: https://stackoverflow.com/questions/28172306/efficient-run-time-type-checking-in-c – alter_igel May 28 '21 at 14:56
  • I personally would first use the `virtual` base class method in the post I linked. – alter_igel May 28 '21 at 15:00
  • Another question: _what part of your code_ needs to know whether a shape has a `radius` or is a `Circle`? If it's called something like `print`/`draw`/`display`, then you can move all of that into a plain old `virtual` method and let derived classes do the work. If that code is called something like `create`/`make`, then you can consider using a factory pattern. – alter_igel May 28 '21 at 15:03
  • Good hint, and your right the vector objects are just for writing and reading information on call. I will have a closer look at the factory pattern and virtual base class method. – KeepRunning85 May 28 '21 at 15:07
  • Sounds like a simple interface like `class Shape { public: virtual ~Shape() = default; virtual void write(std::ostream&) = 0; virtual void read(std::istream&) = 0; };` should work well for you – alter_igel May 28 '21 at 15:09
  • Okay, I will try out that. Many thanks for helping – KeepRunning85 May 28 '21 at 15:14
  • If you ever need to do something specific to `Circle` while having a pointer to `Shape`, you can use `dynamic_cast`. Each such usage clearly marks a place where your inheritance hierarchy doesn't work well, so you could return later and refactor. But in any case, to use `dynamic_cast`, your classes should have virtual methods. – anatolyg May 28 '21 at 19:14

1 Answers1

1

Finally I found a very simple solution, which differs from my specifiations in the beginning and thats maybe why it was confusing. No need of template class, type casting, virtual inheritance etc. Anyhow I would like to post a way to do it:


comment1: Instead of having one vector and storing derived class objects. I just created separated vectors, one for circle and one for rectangle. Those vectors are members of an Shape object.
comment2: For later accessing a certain rectangle or circle I created an enumeration type
comment3: I use overloaded operators to return a certain datatype from an Shape objects
comments4: By those I easily access a certain object(e.g. circle, rectangle) which is a member of myShapes. So the specification of accessing different objects (circle, rectangle) by one object is accomplished by member variables.


#include <iostream>
#include <vector>

enum RectangleNames { rectangle1, rectangle2 };   //comment 2
enum CircleNames { circle1, circle2 };

class Rectangle;
class Circle;

class Shapes {

public:
                                                    //comment 3
  Rectangle *operator[](RectangleNames rectangleName) { return myRectangles[rectangleName]; }
  Circle *operator[](CircleNames circleName) { return myCircles[circleName]; }

  std::vector<Rectangle *> myRectangles;            //comment 1
  std::vector<Circle *> myCircles;
};

class Circle {
public:
  int diameter;
};

class Rectangle {
public:
  int width;
  int length;
  int height;
};

int main() {
  Shapes myShapes;
  myShapes.myRectangles.push_back(new Rectangle);
  myShapes.myCircles.push_back(new Circle);       

  myShapes[rectangle1]->height = 200;           // comment 4
  myShapes[rectangle1]->length = 100;
  myShapes[rectangle1]->width = 40;

  myShapes[circle1]->diameter = 100;
}
KeepRunning85
  • 173
  • 10