Вектор базового класса заполнен подклассами не работает - PullRequest
0 голосов
/ 19 декабря 2018

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

Я посмотрел на множество вопросов, и люди говорят, что делать это так, как я это делаю, но по какой-то причине он не работает.

Код находится в публичном репозитории .it:

https://repl.it/@cubingminer8/inheritance-with-vectors-testing

Любая помощь будет принята с благодарностью!

edit: хорошо, так что я собираюсь использовать это для системы групп спрайтов в игровом движке c ++ sdl2.Будет базовый класс спрайтов, который имеет некоторые базовые вещи, такие как рендеринг и перемещение, в то время как любые спрайты, которые мне нужны, будут их собственными классами, наследуемыми от Sprite, у них будет свое собственное уникальное поведение, поэтому виртуальные функции будут непрактичными.Будет объект группы спрайтов, в котором могут храниться объекты, наследуемые от Sprite. Таким образом, все они могут быть визуализированы одновременно и т. Д.

Если вы когда-либо использовали pygame , тогда онпрактически идентична используемой там системе спрайтов и спрайтгрупп.https://www.pygame.org/docs/tut/SpriteIntro.html

#include <iostream>
#include <vector>

class Base {
public:
    char A = 'A';
};

class Sub : public Base {
public:
    char B = 'B';
};

class Sub2 : public Base {
public:
    char C = 'C';
};

int main() {
    std::vector<Base*> List;
    List.push_back(new Sub());
    List.push_back(new Sub2());

    std::cout << List[0]->B << std::endl; // it should be able to print B
    std::cout << List[1]->C << std::endl; // but it is being set as a base class and
                                          // not getting the functionality of the subclass it is.
}

Ответы [ 2 ]

0 голосов
/ 19 декабря 2018

Итак, вы хотите, чтобы это работало:

// your code version 1
std::cout<< List[0]->B << std::endl; //it should be able to print B            
std::cout<< List[1]->C << std::endl; //but it is being set as a base class 

Но что произойдет, если вы напишите это вместо этого?

// your code version 2
std::cout<< List[0]->C << std::endl;
std::cout<< List[1]->B << std::endl;

List[0] не имеет C иList[1] не имеет B.Как вы предлагаете обрабатывать этот код?

Есть несколько способов подойти к этому.

  1. Компилятор должен знать во время время компиляции что версия 1 правильная, а версия 2 неправильная.К сожалению, это обычно невозможно, потому что компилятор не может отслеживать, какой указатель объекта идет на какой слот в массиве.Таким образом, это должно быть отклонено.
  2. система времени выполнения должна обнаружить ошибку при времени выполнения .Это возможный подход, но не тот, который принят в C ++.C ++ является статически типизированным языком.Динамически типизированные языки могут справиться с этим случаем.Если вам нужен динамически типизированный язык, попробуйте, например, Python.
  3. Компилятор не должен пытаться что-либо обнаруживать, и система времени выполнения также не должна пытаться что-либо обнаруживать, но все равно продолжайте выполнять операцию и позволяйтеэто приводит к неправильным результатам или сбою.Это также возможный подход, но не тот, который используется любым современным языком программирования высокого уровня.C ++ и другие современные языки напечатаны .Можно обойти систему типов C ++, используя reinterpret_cast и т. П., Но это очень опасно и не рекомендуется.
  4. Компилятор должен рассматривать обе версии как неправильные.Это то, что делает C ++.

Как уже упоминалось, (единственный) правильный способ расширить функциональность класса - это использовать виртуальные функции.Это требует предварительного планирования.База должна по крайней мере объявить , какие операции необходимы, хотя не нужно знать , как производные классы будут реализовывать их.

0 голосов
/ 19 декабря 2018

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

class Base {
    char A = 'A';
public:
    virtual char getChar()const /*noexcept*/ { return A; }
    virtual Base () = default;
};

class Sub : public Base {
    char B = 'B';
public:
    char getChar()const /*noexcept*/ override { return B; }
};

class Sub2 : public Base {
    char C = 'C';
public:
    char getChar()const /*noexcept*/ override { return C; }
};

теперь в основной

std::cout << List[0]->getChar() << std::endl;

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

Хорошим началом будет:

#include <memory>

std::vector<std::unique_ptr<Base>> List;
List.emplace_back(std::make_unique<Sub>());
...