Чтобы добавить к предыдущим ответам: если вам действительно нужен оператор *, чтобы быть перегруженным, вы должны понять, что вам нужно найти какой-то способ создания нового Figure
, который вы будете возвращать.
Однако прежде чем сделать это, лучше указать, что бинарный оператор * обычно должен быть перегружен вне класса, например:
T operator*(T const& lhs, T const& rhs);
Это может быть полезно сделатьэто друг, но это также часто не нужно.Если вы хотите, чтобы этот оператор вел себя по-разному, в зависимости от того, был ли передан T или производный от T, пусть он вызовет виртуальную T multiply(T const& rhs) const;
функцию на одной из сторон (в любом случае, это хорошая идея).
Теперь, что касается возврата, есть несколько способов сделать это:
Сделать Figure
не абстрактным и вернуть его по значению
Это довольно простое решение, но, к сожалению, оно мало что дает.Скорее всего, если вы используете полиморфизм, нарезка недопустима.
new
Figure
и возврат по ссылке
ШансЭто выглядит следующим образом:
T& operator*(T const& lhs, int rhs) {
T* t = lhs.Clone(); // Clone returns a new derived-from-T.
t->multiply(rhs);
return *t;
}
Это работает, но затрудняет управление памятью: например, если выбрасывает multiply()
, у вас есть утечка памяти.В общем, хотя это можно сделать, это не кажется разумным.
Используйте PImpl и возвращайте обертку по значению
Если честно, у меня нет практического опытас PImpl, но я думаю, что это (или что-то подобное) может быть применено здесь.Вместо прохождения Figure*
s вы можете переименовать текущий Figure
в FigureImpl
и создать новый класс Figure
, который будет иметь вид:
class Figure {
// We don't want direct instantiation.
Figure();
Figure(FigureImpl* fi) : p_(fi) {}
public:
Figure(Figure const& f)
: p_(f.p_->Clone())
{}
void PrintInfo(); // Hm, why not overload operator<<?
Figure Multiply(int rhs) const {
Figure f(p_.Clone());
f.p_->Multiply(rhs);
return f;
// Or even: return Figure(p_->Multiply(rhs.p_));
// with Multiply returning a new FigureImpl*.
}
private:
unique_ptr<FigureImpl*> p_;
};
Figure operator*(Figure const& lhs, int rhs) {
return lhs.Multiply(rhs);
}
После этого вы можете иметь FigureImpl
class и LineImpl
class:
struct FigureImpl {
virtual void printInfo() = 0;
virtual void Multiply(int) = 0; // or make it const and return a FigureImpl*
};
struct LineImpl {
// implement functions
};
Осталось две проблемы, главной из которых является реализация Figure
.Я бы предложил создать некоторую функцию, например MakeLineFigure
, которая будет возвращать новый Figure
соответствующего типа.Вы могли бы также выставить FigureImpl
конечному пользователю, но это кажется менее полезным.
Другая проблема заключается в том, что вы не можете больше делать обычные LineImpl
с.Является ли это действительно проблемой, зависит от того, как вы собираетесь использовать класс.Вы можете сделать подобную оболочку с именем Line
и дать ей operator Figure
(это может быть неплохо; вам не нужны функции создания экземпляров), но в итоге это станет еще более шаблонным (возможно, вы могли бы сделать это с наследованием)где все новые классы не получают новых переменных-членов, а просто расширяют функции - я не пробовал).
Последняя проблема, которую я вижу, которая не зависит от того, какой маршрутвы выбираете, что вы позволяете линии умножить на плоскость.Возможно, это правильное поведение, но оно выглядит сомнительным.
Я не заметил, что умножение было скалярным, поэтому не обращайте внимания на эту последнюю часть.