Можно ли присвоить указатель на подкласс varibale с типом суперкласса?

Предположим, что существует класс A, который имеет два подкласса, Aa и Ab. Я хочу сделать массив, который может хранить указатели на объекты класса Aa и Ab. Если массив объявлен с типом класса A, допустимо ли это? Если нет, то как я могу это сделать? Например:

A *arr;
//space allocated
Ab *obj1;
arr[x] = obj1;

В связи с этим я хочу написать функцию, которая при указании местоположения будет возвращать объект, хранящийся в этом месте в массиве. Если приведенное выше работает, и у меня есть массив объектов либо Aa, либо Ab, функция может вернуть объект либо типа Aa, либо Ab. Если тип возвращаемого значения функции указан как A, суперкласс, допустимо ли это? Если нет, я просмотрел функции шаблона, но не могу найти прямого ответа о том, что тип возвращаемого значения должен быть переменным, а не параметрами. В этом примере параметр функции всегда будет int, но он может возвращать Aa или Ab, в зависимости от того, что находится в этом месте в массиве.


person J. Cal    schedule 23.02.2018    source источник
comment
В C++ мы говорим о производных и базовых классах, а не о подклассах и суперклассах.   -  person    schedule 24.02.2018
comment
Короткий ответ: да, если ваш массив действительно является массивом. Я имею в виду A *arr[100] или что-то в этом роде.   -  person Sergey Kalinichenko    schedule 24.02.2018
comment
Если массив объявлен с типом класса А, то в нем нельзя хранить указатели. Вам нужен массив A*.   -  person eerorika    schedule 24.02.2018
comment
@dasblinkenlight Отлично, спасибо! Итак, пока массив действительно создан, указатели на производный класс рассматриваются как указатели на базовый класс? применим ли этот принцип и ко второму вопросу? Что функция с возвращаемым типом базового класса может возвращать указатели на объекты производных классов?   -  person J. Cal    schedule 24.02.2018
comment
@J.Cal Да и да: указатели предоставляют самый простой способ добиться полиморфного поведения в C++.   -  person Sergey Kalinichenko    schedule 24.02.2018
comment
@J.Cal • чтобы получить полиморфное поведение, вам нужно использовать виртуальные функции-члены. Таким образом, когда вы вызываете a[50]->DoSomething(), вы получаете либо A::DoSomething, либо Aa::DoSomething, либо Ab::DoSomething, в зависимости от того, на какой тип A указывает a[50]. И вам, вероятно, понадобится A, чтобы иметь деструктор virtual ~A().   -  person Eljay    schedule 24.02.2018
comment
Ваш массив также может быть std::vector‹A*›, или std::vector‹std::shared‹A››, или std::vector‹std::unique_ptr‹A››.   -  person Michaël Roy    schedule 24.02.2018


Ответы (2)


Да, именно так достигается polymorohism (с использованием указателя на базовый класс) и virtual methods.

Вот пример:

#include <iostream>
using namespace std;
#include <vector>


class A{
    public:
        virtual void foo()const{
            std::cout << "A::foo()" << std::endl;
        }
};

class Aa : public A {
    public:
        virtual void foo()const{
            std::cout << "Aa::foo()" << std::endl;
        }
};

class Ab : public A {
    public:
        virtual void foo()const{
            std::cout << "Ab::foo()" << std::endl;
        }
};

int main(){

    A* ptrA[3];

    A*  a  = new A;
    Aa* aa = new Aa;
    Ab* ab = new Ab;

    ptrA[0] = aa;
    ptrA[1] = ab;
    ptrA[2] = a;

    for(int i(0); i != 3; ++i)
        ptrA[i]->foo();

    delete a;
    delete aa;
    delete ab;

    return 0;
}
  • Помните, что C++ — это Invariant, а не Contravariant, что означает, что вы не можете присвоить объекту derived объект base:

    A* a = new A;
    Ab* ab = a; // error
    
person Raindrop7    schedule 23.02.2018

Допустимо иметь массив базовых указателей, также вы можете использовать dynamic_cast, чтобы узнать во время выполнения тип возвращаемого вашего массива и использовать API из производного класса. См. и пример ниже.

struct Base { virtual void do_something() {} };

struct Derived1 : Base
{
  void first() const { std::cout << "first" << std::endl; }
  void do_something() override {}
};

struct Derived2 : Base
{
  void second() const { std::cout << "second" << std::endl; }
  void do_something() override {}
};

Base& get(int option)
{
  static std::vector<Base*> b {new Derived1{}, new Derived2{}};
  return !option ? *b[0] : *b[1];
}

int main()
{
  const int option {0};
  // const int option {1};

  if (Derived1* derived {dynamic_cast<Derived1*>(&get(option))})
  {
    derived->first();
  }
  else if (Derived2* derived {dynamic_cast<Derived2*>(&get(option))})
  {
    derived->second();
  }
}
person AdvSphere    schedule 23.02.2018