Простой подход состоит в том, чтобы использовать немного желаемого за действительное: просто подумайте, что у вас есть два объекта, прежде чем писать операцию сложения.
Действительно, когда вы определяете операцию, вы делаетенаписание алгоритма, который позже будет применен.На месте вызова, когда некоторые типы пользователей a+b
, a
и b
должны быть действительными экземплярами типа, который вы хотите добавить.При определении операции аргументы функции или метода представляют аргументы действительных экземпляров, которые будут существовать позже.
Поскольку это домашняя работа, я последую другому примеру.Попробуйте написать класс vector2d
, который содержит два типа double, представляющих координаты x
и y
.
class vector2d {
public:
//...
private:
double x,y;
};
И вы хотите предложить некоторые операции, которые можно применять к vector2d
объектам.Вы можете определить operator+=
как способ сохранения в векторе результата добавления того же вектора ко второму:
class vector2d {
public:
//...
vector2d& operator+=( const vector2d& rhs )
{
x += rhs.x;
y += rhs.y;
return *this;
}
};
Для соглашения мы возвращаем ссылку на vector2d
из operator+=
.Мы работаем как с объектом, который вызываем операцией (это функция-член, объект *this
), так и со вторым объектом, который мы берем в качестве параметра.Обратите внимание, что на данный момент в программе не создано ни одного объекта, мы просто определяем операцию, а не операнды.Правый аргумент rhs
обрабатывается через постоянную ссылку (мы получаем ссылку на объект, который мы обещаем не изменять).Фактическая операция проста, добавьте каждую координату отдельно, и поскольку соглашение возвращает ссылку на измененный объект, мы возвращаем *this
.
Мы определили, как операция выполняется в терминах двух объектов,и теперь мы можем использовать его:
int main() {
vector2d a( 5, 10 ); // assume that there is a constructor that
// takes 2 doubles in the ellipsis above...
vector2d b( 2.5, 7.5 );
a += b;
}
Теперь, чтобы сделать звонок, пользователю необходимо , чтобы иметь два объекта.Чтобы определить операцию, нам на самом деле не нужны экземпляры объектов, мы абстрагируем это с помощью параметров.Но для фактического использования операции нам нужны объекты, с которыми можно работать.На данный момент они фактически созданы и названы a
и b
.Затем пользователь вызывает операцию: a += b
.Так как это метод-член, вызов переводится компилятором в несколько менее красивое: a.operator+=(b)
, то есть он будет вызывать метод-член operator+=
для объекта a
, передавая b
в качестве аргумента.
Чтобы обеспечить правильную операцию сложения, мы можем написать бесплатную функцию.Добавление не относится ни к одному из двух аргументов, а скорее создает третий аргумент, поэтому имеет смысл, что он не является методом-членом.
vector2d operator+( vector2d lhs, const vector2d & rhs )
{
lhs += rhs;
return lhs;
}
Эта реализация является идиоматической (общий шаблон),После реализации operatorX=
мы можем реализовать operatorX
в терминах предыдущего.Теперь хитрость: мы берем первый аргумент по значению.Таким образом, компилятор делает для нас копию lhs
внутри этой функции - не первый аргумент добавления, а копия этого.Затем мы используем существующую операцию для изменения этой локальной копии и возвращаем результат пользователю.Возвращаемый объект также является побочным.
Использование аналогично:
int main() {
vector2d a( 5, 10 );
vector2d b( 2.5, 7.5 );
vector2d c = a + b;
}
В месте вызова a+b
переводится в operator+( a, b )
, так как это бесплатныйфункция.Затем, поскольку первый аргумент передается по значению, создается копия a
, которая используется в функции как lhs
.Второй аргумент передается по ссылке, и мы обещаем не изменять его (таким образом, const).Результат операции сохраняется в c
.