C++ array of base class which has instances of derived classes stored in the elements of the array

StackOverflow https://stackoverflow.com/questions/20912106

Pergunta

I am creating an application that allows a user to define dimensions for different shapes and returns the area to the user using the dimensions they specified.

My base class is Shape. Derived classes are Triangle, Circle, Square and Rectangle.

I have created an array of Shape in the hope of creating and storing instances of any of the derived classes in the array during runtime.

Shape** shape = new Shape*[TOTAL_SHAPES];

shape[i] = new Circle(radius);

I have managed this, however I am unable to access the instantiated classes methods. Sorry if this is a stupid question I am fairly new to C++.

Foi útil?

Solução

Let's assume your types had the following definition

class Shape {
public:
  void Method1() { ... } 
};

class Circle : public Shape { 
  void Method2() { ... }
}

With this definition you could access methods on Shape by doing the following

shape[i]->Method1();

In this context though it wouldn't be possible to access Method2 though because the compiler only knows about Shape, not Circle.

shape[i]->Method2(); // Error! 

Outras dicas

You have three options:

  1. Make Shape an abstract base class and call virtual methods that are members of Shape
  2. Use static_cast to cast from a Shape* to a Circle*, and call methods through that.
  3. Use dynamic_cast to cast from a Shape* to a Circle*, and call methods through that.

The first option is likely best in many cases. Among other reasons, you almost surely need to have a virtual destructor (which can be a by-product of making Shape an ABC), and you may prefer to not have to know what type of object is being pointed to, rather you'd like to simply call methods on whatever it may be. If you can use this idiom, use it.

The second option is dangerous. You must absolutely know that the object being pointed to is a Circle (or whatever) in order to use static_cast, else you will get Undefined Behavior.

The third option is only possible if your class is polymorphic, which means Shape must have at least one virtual method. You surely should have a virtual destructor, and this would serve that purpose.

How about that:

shape[i]->aMethod();

For starters, I highly recommend using a smart pointer wrapper instead of using raw pointers (especially if you are new to the language).

std::vector<std::shared_ptr<Shape>> shapes(TOTAL_SHAPES);

That will define a vector with an initial size of TOTAL_SHAPES.

To the root of your problem, any method you wish to call using a Shape* must be valid for Shape, or you must do a risky downcast to the appropriate type. For example:

class Shape
{
public:
    // constructors and other methods go here
    virtual ~Shape() { } // virtual destructor

    virtual void Draw() { } // virtual function to be used by derived classes
};

class Circle
{
public:
    // ...
    virtual ~Circle() { }
    virtual void Draw() { } // override the virtual function
};

Then, in your application code,

std::vector<std::shared_ptr<Shape>> shapes(TOTAL_SHAPES);
shapes[0] = std::make_shared(new Circle);
shapes[0]->Draw(); // calls Circle::Draw

Note that depending on your usage, std::unique_ptr may replace std::shared_ptr.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top