полиморфизм — нарезка и перегрузка операторов в переполнении стека

Справочная информация

Я программировал на Java некоторое время, и я переключился на C ++ всего несколько месяцев назад, поэтому я прошу прощения, если ответ — просто глупость, которую я пропустил! Теперь, когда это все сказано, настало время для проблемы! Я занимаюсь разработкой базового текстового игрового движка, и недавно я столкнулся с интересной специфической и маловероятной проблемой. Я попытался протестировать его в меньшем масштабе в программе ниже и решил просто показать это (в отличие от моего реального игрового кода), чтобы не задушить экран и сделать проблему менее запутанной. Проблема, смоделированная ниже, отражает проблему с моим настоящим кодом, просто без пушистых отвлекающих факторов.

Эта проблема

По сути, проблема заключается в полиморфизме. Хочу перегрузить оператор вывода<<«служить функцией отображения, уникальной для каждого объекта в иерархии. Проблема в том, что когда я вызываю этот оператор из списка, в котором хранятся эти элементы иерархии, они теряют свою идентичность и вызывают оператор вывода базового класса. Обычно Можно было бы решить эту проблему, заменив перегрузки оператора простым методом отображения, пометив метод отображения виртуальным и перейдя к их счастливому дню. Я не особо возражаю против внесения такого изменения в код, но теперь я просто Любопытно. Есть ли способ перегрузить операторы в иерархии, что приводит к тому, что я собираюсь здесь?

Код [Пример]

#include <vector>
#include <iostream>
#include <string>

using namespace std;

class Polygon {
friend ostream& operator<<(ostream& os, const Polygon& p);
public:

private:

};class Rectangle : public Polygon {
friend ostream& operator<<(ostream& os, const Rectangle& r);
public:

private:

};class Square : public Rectangle {
friend ostream& operator<<(ostream& os, const Square& s);
public:

private:

};

ostream& operator<<(ostream& os, const Polygon& p) {
os << "Polygon!" << endl;
return os;
}
ostream& operator<<(ostream& os, const Rectangle& r) {
os << "Rectangle!" << endl;
return os;
}
ostream& operator<<(ostream& os, const Square& s) {
os << "Square!" << endl;
return os;
}int main() {
vector<Polygon*> listOfPoly;
listOfPoly.push_back(new Polygon());
listOfPoly.push_back(new Rectangle());
listOfPoly.push_back(new Square());

for(Polygon* p : listOfPoly) {
cout << *p;
}
}

Выход для кода [Пример]

Polygon!
Polygon!
Polygon!

Требуемый вывод для кода [Пример]

Polygon!
Rectangle!
Square!

6

Решение

Есть ли способ перегрузить операторы в иерархии, что приводит к тому, что я собираюсь здесь?

Нет.

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


Обратите внимание, что перегрузка операторов — просто синтаксический сахар. Выражение

os << shape;

можно разрешить до свободной функции (как у вас здесь)

ostream& operator<< (ostream&, Polygon&);

или член левого операнда, такой как

ostream& ostream::operator<<(Polygon&);

(очевидно, здесь второй случай не существует, потому что вам придется изменить std::ostream). Какой синтаксис не может решите, является членом правого операнда.

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


Обычное решение состоит в том, чтобы иметь единственную перегрузку для верхнего уровня иерархии, которая отправляется виртуальному методу. Так:

class Polygon {
public:
virtual ostream& format(ostream&);
};

ostream& operator<<(ostream& os, const Polygon& p) {
return p.format(os);
}

Теперь просто реализовать Polygon::formatи переопределить его в производных классах.


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

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

Если вы замените свой полиморфный список времени выполнения на полиморфный кортеж во время компиляции и выполните итерации тот, Ваша оригинальная схема перегрузки будет отправлена ​​правильно.

5

Другие решения

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

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

Как правило, делать виртуальные операторы — довольно плохая идея. Это потому, что вы можете только динамически отправлять на this но операторы часто используются с типом, реализующим их в RHS, или в качестве нечленов. Перегрузки операторов, не являющихся членами, являются более мощными, чем члены.

3

Вы можете добавить виртуальную функцию Display () в базовый класс Rectangle. Каждый класс в иерархии может переопределять функцию и реализовывать ее по-своему.

Вам нужно только определить один оператор<< это занимает многоугольник& в качестве параметра. Сама функция просто вызывает функцию виртуального дисплея.

class Polygon
{
public:
virtual void Display(ostream& os) const
{
os << "Polygon" << endl;
}
};

class Rectangle : public Polygon
{
public:
virtual void Display(ostream&os) const override
{
os << "Rectangle" << endl;
}
};

ostream& operator<<(ostream& os, const Polygon& p)
{
p.Display(os);
return os;
}
1
По вопросам рекламы [email protected]