Массив C++ базового класса, который содержит экземпляры производных классов, хранящихся в элементах массива

Я создаю приложение, которое позволяет пользователю определять размеры для различных фигур и возвращает область пользователю, используя указанные им размеры.

Мой базовый класс — Shape. Производными классами являются Triangle, Circle, Square и Rectangle.

Я создал массив Shape в надежде создать и сохранить экземпляры любого из производных классов в массиве во время выполнения.

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

shape[i] = new Circle(radius);

Мне удалось это, однако я не могу получить доступ к методам экземпляров классов. Извините, если это глупый вопрос, я новичок в C++.


person Mark    schedule 03.01.2014    source источник
comment
Создаваемые методы должны быть виртуальными методами базового класса. В противном случае вам нужно опуститься.   -  person user2672165    schedule 04.01.2014
comment
Не получается как? Объяснять.   -  person Lightness Races in Orbit    schedule 04.01.2014
comment
Пожалуйста, покажите определение класса и только функцию области, либо ваша наследственная, либо виртуальная функция, вероятно, определена не совсем правильно.   -  person Glenn Teitelbaum    schedule 04.01.2014


Ответы (4)


Предположим, что ваши типы имеют следующее определение

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

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

С этим определением вы можете получить доступ к методам Shape, выполнив следующие действия.

shape[i]->Method1();

Однако в этом контексте было бы невозможно получить доступ к Method2, потому что компилятор знает только о Shape, а не о Circle.

shape[i]->Method2(); // Error! 
person JaredPar    schedule 03.01.2014
comment
Разве метод не должен быть виртуальным? - person Glenn Teitelbaum; 04.01.2014
comment
@GlennTeitelbaum - нет, это не обязательно должно быть виртуальным. Вы можете вызывать любые общедоступные методы базового класса. Если бы он был виртуальным, то он мог бы быть переопределен производным классом, и в этом случае вызывался бы метод производного класса. - person acarlon; 04.01.2014
comment
@GlennTeitelbaum это может быть виртуальным, но это не обязательно, чтобы этот образец работал. - person JaredPar; 04.01.2014

У вас есть три варианта:

  1. Сделайте Shape абстрактным базовым классом и вызовите virtual методы, которые являются членами Shape
  2. Используйте static_cast для приведения от Shape* к Circle* и вызовите через него методы.
  3. Используйте dynamic_cast для приведения от Shape* к Circle* и вызывайте методы через это.

Первый вариант, вероятно, лучше во многих случаях. Среди других причин вам почти наверняка понадобится деструктор virtual (который может быть побочным продуктом создания Shape ABC), и вы можете предпочесть не знать, на какой тип объекта указывает указатель, а лучше нравится просто вызывать методы для чего бы то ни было. Если вы можете использовать эту идиому, используйте ее.

Второй вариант опасен. Вы должны абсолютно знать, что объект, на который указывает указатель, является Circle (или чем-то еще), чтобы использовать static_cast, иначе вы получите Undefined Behavior.

Третий вариант возможен, только если ваш класс полиморфен, что означает, что Shape должен иметь хотя бы один virtual метод. У вас обязательно должен быть деструктор virtual, и это послужит этой цели.

person John Dibling    schedule 03.01.2014

Как насчет этого:

shape[i]->aMethod();
person deviantfan    schedule 03.01.2014
comment
Извините, но это не работает, как и оператор точки. - person Mark; 04.01.2014
comment
@Mark: Пожалуйста, возьмите за привычку идти дальше, чем просто говорить, что это не работает. Вам нужно подробно объяснить, что происходит. - person Lightness Races in Orbit; 04.01.2014

Для начала я настоятельно рекомендую использовать интеллектуальную оболочку указателя вместо необработанных указателей (особенно если вы новичок в языке).

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

Это определит вектор с начальным размером TOTAL_SHAPES.

В корне вашей проблемы любой метод, который вы хотите вызвать с помощью Shape*, должен быть допустимым для Shape, или вы должны сделать рискованное приведение к соответствующему типу. Например:

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
};

Затем в коде вашего приложения

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

Обратите внимание, что в зависимости от вашего использования std::unique_ptr может заменить std::shared_ptr.

person Zac Howland    schedule 03.01.2014