Може ли указател към подклас да бъде присвоен на 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, тогава не можете да съхранявате указатели в него. Имате нужда от масив от 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