Каков правильный метод для форматирования и использования производных классов внутри структуры данных Base *? - PullRequest
0 голосов
/ 13 ноября 2018

Введение

Я немного растерялся от правильного способа хранения структуры данных нескольких производных классов. Я относительно новичок в C ++, поэтому извините, если у меня есть идиотские ошибки / неправильные представления.

В данный момент я использую vector<Base*>*, что имеет смысл для меня. Однако я сталкиваюсь с некоторыми проблемами, когда пытаюсь использовать объекты, содержащиеся в векторе.

Проблемы, с которыми я сталкиваюсь

Я собираюсь использовать этот пример, чтобы продемонстрировать свои проблемы:

Base.h

class Base {

 public:
  Base();
  Base(int a, int b);
  virtual ~Base();

  friend std::ostream& operator<<(std::ostream& os, Base& base);

  int getA();
  int getB();

 protected:
  int a;
  int b;

};

Derived.h

class Derived : public Base {

 public:
  Derived(int a, int b, int c);
  ~Derived();

  friend std::ostream& operator<<(std::ostream& os, Derived& derived);

  getC();

 private:
  int c;

};

# 1: Использование функций-членов производных классов

int main() {
  vector<Base*>* objects = new vector<Base*>();

  Base* newbase = new Base(0, 1);
  Derived newderived = new Derived(2, 3, 4);
  objects.push_back(newbase);
  objects.push_back(newderived);

  cout << objects->front()->getA() << endl; // '0'
  cout << objects->back()->getA() << endl; // '2'

  cout << objects->back()->getC() << endl; // error: 'class Base' has no member named 'getC()'
}

Несмотря на то, что последний объект в objects является экземпляром класса Derived, он распознается только как класс Base. Это, конечно, имеет смысл; objects удерживать Base*.

Хотя я понимаю, почему возникает ошибка, я не совсем понимаю, как ее устранить. В моих предыдущих поисках я обнаружил, что есть два обычно предлагаемых (и также довольно спорных) решения: включая каждую функцию-член, которую мои различные производные классы используют в качестве virtual функции в Base, и использование статического приведения.

Каков надлежащий метод / хорошая практика для использования функций-членов производных классов внутри структур данных, содержащих Base*?

# 2: Использование друзей: перегрузка оператора вставки

int main() {

  vector<Base*>* objects = new vector<Base*>();

  Base* newbase = new Base(0, 1);
  Derived newderived = new Derived(2, 3, 4);
  objects.push_back(newbase);
  objects.push_back(newderived);

  cout << *(objects->front()) << endl; // Executed `Base`'s overloaded insertion operator
  cout << *(objects->back()) << endl; // Also executed `Base`'s overloaded insertion operator
}

В этом случае я нашел очень мало надежных предложений для решения этой проблемы, за исключением «просто использовать метод print ()». Я понимаю, что могу обойти эту проблему, но я бы лучше понял, как правильно реализовать что-то, чем просто избегать его использования.

Итак, возможно ли каким-либо образом выполнить перегруженный оператор вставки производного класса при вызове из vector<Base*>? Любая функция друга?

# 3: Определение, к какому производному классу относится объект

int main() {
  vector<Base*>* objects = new vector<Base*>();

  Derived newderived = new Derived(2, 3, 4);
  Derived2 newderived2 = new Derived2(5, 6, 7, 8);
  objects.push_back(newderived);
  objects.push_back(newderived2);

  if (/* objects->front() is a Derived */) cout << "Type check success << endl;
  if (/* objects->back() is a Derived2 */) cout << "Type check success << endl;
}

Эта проблема, в частности, была рассмотрена несколько раз ранее. Два решения, которые я видел, так или иначе оценивают то, что происходит после статического приведения, и сохраняют некоторую форму списка типов в базовом классе и сохраняют значение типа во всех производных классах.

Однако, как я упоминал ранее, я новичок в C ++, и я не понимаю первый вариант или правильный способ реализации второго. Как правильно решить эту проблему, и есть ли какие-нибудь объясненные примеры видео или подобные мне для расследования?

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

Спасибо за помощь!

Ответы [ 2 ]

0 голосов
/ 13 ноября 2018

IMO все ваши проблемы проистекают из попытки бороться с выбранным вами решением - динамический полиморфизм .

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

В вашем примере:

1) Вы пытаетесь использовать два различных интерфейса . Ваша динамика полиморфизм действует через интерфейс Base, и вы хотите использовать интерфейс Derived. С динамическим полиморфизмом выберите ваш интерфейс. Если вам нужно знать конкретный тип в данной части системы, тогда polymorphism - просто не лучшее решение в этой области кода.

2) Правильный способ сделать это - добавить функцию print(). Идея состоит в том, что каждый производный тип знает, как печатать сам, так что вызывающему коду не нужно знать или заботиться о том, как это делается.

3) В тех частях системы, где динамический полиморфизм терпит неудачу (где вам необходимо знать конкретный тип объекта), тогда рекомендуемый способ обнаружения типа - использование dynamic_cast

if(auto derived = dynamic_cast<Derived*>(objects->front()))
{
     derived->getC(); // Derived specific calls
}

Если указатель неправильного типа, возвращается nullptr и ошибка if().

Что я обнаружил с помощью C++, так это то, что динамический полиморфизм на самом деле не лучшее решение для многих проблем. Может возникнуть соблазн попытаться сделать все динамически полиморфным, но IMO, который хорошо работает только для данного подмножества проблем объектно-ориентированного проектирования.

C++ также превосходит в других областях, таких как статический полиморфизм (с использованием механизма шаблонов) и процедурный полиморфизм (перегрузка функции). Их тоже стоит изучить.

0 голосов
/ 13 ноября 2018

Вы реализуете неабстрактный базовый класс. В полиморфизме попытка найти, существует ли метод в базовом / производном классе, считается неправильной практикой программы. Вместо этого объявите общие методы как виртуальные. Попробуйте вот так.

class Base {

public:
    Base();
    Base(int a, int b);
    virtual ~Base();

    friend std::ostream& operator<<(std::ostream& os, Base& base);

    virtual int getA();
    virtual int getB();
    virtual int getC() { return 0; }

protected:
    int a;
    int b;

};

class Derived : public Base {

public:
    Derived(int a, int b, int c);
    ~Derived();

    friend std::ostream& operator<<(std::ostream& os, Derived& derived);

    virtual int getA();
    virtual int getB();
    virtual int getC();

private:
    int c;

};

int main() {
    vector<Base*> objects;

    Base* newbase = new Base(0, 1);
    Derived newderived =  Derived(2, 3, 4);
    Base* pnewderived = &newderived;
    objects.push_back(newbase);
    objects.push_back(pnewderived);

    cout << objects.front()->getA() << endl; 
    cout << objects.back()->getA() << endl; 

    cout << objects.back()->getC() << endl; 
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...