C ++: перегрузка математических операторов - PullRequest
3 голосов
/ 20 июня 2010

Я хочу перегрузить, скажем, оператор сложения и добавить в него два объекта одного и того же класса. Когда я объявляю эту функцию-прототип «operator +» в объявлении класса в заголовочном файле, я передаю оба объекта как параметры. Я получаю ошибку компилятора, говорящую, что «бинарный оператор« + »имеет слишком много параметров». Я искал ответ в Интернете и обнаружил, что объявление встроенной функции прямо за пределами объявления класса в заголовочном файле скомпилировано. Мне интересно, что я делаю неправильно или я что-то здесь упускаю. Вот код, который я использую в заголовочном файле.

class Person
{
private:
    int age;
    double weight;
public:
    Person::Person();                           //default constructor       
    Person::~Person();                           //default desctructor
    Person operator+(Person r, Person i);
};

Это компилируется с ошибкой, о которой я упоминал выше. Ниже приведен код, который прекрасно компилируется.

class Person
{
private:
    int age;
    double weight;
public:
    Person::Person();                           //default constructor       
    Person::~Person();                           //default desctructor
};
inline Person operator+(Person r, Person i)
{
return Person(0,0); 
}

Ответы [ 8 ]

6 голосов
/ 20 июня 2010

Каждая функция-член имеет неявный первый аргумент: this.Это также верно для операторов.Поэтому, если вы хотите сделать operator+() членом, он должен принимать только один аргумент, правый операнд, потому что левый операнд уже равен this.

Однако для бинарных операторов, которые могут быть или функцией-членом, или свободной функцией, мое правило: сделать так, чтобы эти свободные функции обрабатывали свои аргументы симметрично (operator+() не меняет ни одного из своихоперанды), и сделайте те функции-члены, которые не (operator+=() изменяет свой левый операнд, но не его правый).
Кроме того, для всех арифметических операторов есть хороший шаблон для их реализации на основе их комбинированного назначенияверсии.То есть operator+() будет основано на operator+=(), - основано на -= и т. Д.
Добавьте к этому, что вы не хотите копировать объекты класса без необходимости, но обычно хотите, чтобы они передавались в соответствии сconst ссылка.

Исходя из этого, мои канонические версии operator+ будут:

class Person
{
public:
    Person& operator+=(const Person& i)
    {
      // whatever
      return *this;
    }
};

inline Person operator+(Person lhs, const Person& rhs)
{
  lhs += rhs; // note: lhs passed per copy
  return lhs;
}

Однако. Что, вероятно, самое важное правилоБольшой палец относительно перегрузки оператора таков: Не делайте этого .Это может показаться парадоксальным, но чаще всего именованные функции намного лучше, чем операторы, потому что последние делают код менее читаемым, чем обычные функции.А при написании кода удобочитаемость должна быть самым важным аспектом .нет если только операторы не имеют четкое и бесспорное значение в прикладной области, не перегружайте его для класса.
В вашем примере конечно есть нет чистых ибесспорный значение оператора + применяется к двум лицам.Что значит в реальном мире добавить к людям?(Первое, о чем я мог подумать, это не один человек в результате, а множество людей. :))
Так что, если перегрузка этого оператора для вашего Person класса не является домашним заданием, янастоятельно советую против этого.(И если это задание, я бы упрекнул учителя в том, что он нашел такой плохой пример.)

6 голосов
/ 20 июня 2010

Если вы объявите oparator+ как функцию экземпляра, то первый аргумент будет передан как this объект, и, таким образом, вам потребуется только еще один аргумент.Прочитайте это для получения дополнительной информации, в частности, попытайтесь понять концепцию const:

http://www.cs.caltech.edu/courses/cs11/material/cpp/donnie/cpp-ops.html

Лучший подход, рекомендованный в упомянутой статье:1011 * если вы решите использовать const версию, помните, что вы сможете вызывать const методы только для ссылок this и i.Это предпочтительный способ.

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

Дополнительно - Дэвид Родригес предлагает реализовать operator+ как свободную функцию независимо от наличия + =.

2 голосов
/ 20 июня 2010

Вот пример для рабочего + = в классе.

Vector3D& operator+=( const Vector3D& other ) {
        x+=other.x;
        y+=other.y;
        z+=other.z;
        return *this;
    }
1 голос
/ 20 июня 2010

У меня только один совет: используйте Boost.Operators.

class Person: boost::addable<Person>      // yes private inheritance
{
public:
  Person& operator+=(Person const& rhs);

private:
};

Насколько я вижу, есть 2 преимущества:

  • Нет необходимости писать код котельной плиты, и, следовательно, нет шансов ошибиться;)
  • Сделать для понятного интерфейса, addable в значительной степени говорит само за себя

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

Очень удобно сохранять интерфейс компактным и вставлять некоторую документацию прямо в код.

1 голос
/ 20 июня 2010

Из того, что я прочитал, лучшая практика - объявлять operator+ как функцию друга, а не как функцию-член.

Целое обсуждение этой проблемы приведено на cplusplus.com:*1004.*

Бинарные операторы как друзья - почему?

Мейерс также обсуждает это в своей книге Effective C ++ IIRC.

Цитата из комментария geustgulkan :

Для любого данного двоичного оператора, это левый объект, который выполняет операцию.

Скажем, у нас есть класс ClassX, который имеет перегрузку для оператора + дляцелые числа.Затем, если мы создадим объект класса thet (назовем его objx), мы можем сказать, что objx + 2. objx выполнит операцию и вернет результирующий объект ClassX.

Но что произойдет, если мы скажем2 + objx?Результат должен быть таким же, как 2 + objx - НО целые числа не являются классами и, конечно, числовые литералы - нет, поэтому 2 + objx напрямую невозможен.

Было бы больно, если бы нам пришлось разрабатывать наш кодчтобы убедиться, что у нас всегда есть объекты ClassX в левой части +, когда мы хотим добавить к нему int.

Здесь мы используем перегрузки друзей для оператора + для ClassX.

class ClassX
{

    friend ClassX operator+(ClassX objx, int i);
    friend ClassX operator+(int i, ClassX objx);
};

Итак, что вы, вероятно, хотите:

class Person
{
private:
    int age;
    double weight;
public:
    Person::Person();                           //default constructor       
    Person::~Person();                           //default desctructor
    friend Person operator+(const Person &rhs, const Person &lhs);
};

Person operator+(const Person &lhs, const Person &rhs) {
     // do whatever constitutes addition here
     Person temp = lhs;
     temp.weight += rhs.weight;
     return temp;  // return copy of temp
}
0 голосов
/ 20 июня 2010

Я согласен с аргументами agsamek и sbi.Я добавлю некоторые дополнительные соображения

  1. , поскольку вы можете видеть, что в вашем классе Person нет конструктора с 2 аргументами, и это ошибка (но еще не передана компилятором)
  2. ofen, когдавы перегружаете один математический оператор, вам также нужно перегрузить «operator =» и конструктор;
  3. , если вы хотите перегрузить оператор плюс двумя аргументами, вы не можете использовать элемент экземпляра, но вы должны использовать функцию,Я избегаю объявлять дружественные функции, потому что вы нарушаете инкапсуляцию этого класса (его будет сложнее поддерживать).Лучше реализовать некоторые «методы доступа» для получения внутренних значений объекта;
  4. в некоторых компиляторах, встроенный метод требует объявления и определения в том же месте (обычно в файле .h), поэтому в менее управляемом.Более того, расширение этого кода осуществляется по усмотрению компилятора.

Далее следуйте моему решению вашего вопроса:

class Person
{
private:
    int age;
    double weight;
public:
    Person(){}                           //default constructor       
    Person(int a, double w) : age(a), weight(w) {}
    ~Person(){}                           //default desctructor

    int getAge() const { return age; }
    double getWeight() const { return weight; }

    const Person & operator =(const Person & rha) {
        age = rha.age; weight = rha.weight; return *this;
    }
};

Person operator +(const Person & lha, const Person & rha)
{
    return Person(lha.getAge() + rha.getAge(), rha.getWeight() + lha.getAge());
}

int _tmain(int argc, _TCHAR* argv[])
{
    Person p1(30,75);
    Person p2(40,80);

    Person sumOfPis = p1 + p2;  // <-- Without "operator =" this statement will use the default copy constructor

    return 0;
}
0 голосов
/ 20 июня 2010
 class Person
  {
    private:
     int age;
     double weight;
    public:
      Person();                           //default constructor       
      ~Person();                           // destructor
      Person operator+(const Person &r); //overloaded operator as member function, so only one argument is required to be passed
 };

Реализация:

  Person Person::operator+(const Person &r)
  {
      Person x;
      x.age=this->age + r.age; // this refers to the object on which the function has been invoked, P1 in this case(see below)
      return x;
  }

Когда вы перегружаете бинарный оператор (в данном случае +) как функцию-член, объект, для которого вызывается оператор, неявно передается функции, и вам нужно явно передать только один аргумент

Таким образом, ваша основная функция должна быть такой:

 int main()
 {
    Person P1,P2;
    P2=P1+P2; //P1 is passed implicitly 

    //The above expression is equivalent to P2=P1.operator+(P2);
}
0 голосов
/ 20 июня 2010

Похоже, вы сделали оператор member + объявление без намерения ссылаться на какие-либо данные экземпляра Person: оператор member, как и любой метод member, принимает экземпляр, для которого он вызывается, в качестве аргумента.

Если дляпо какой-то причине вам нужно сделать его членом (например, доступ к приватному состоянию), вы можете объявить его статическим.

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