Модераторы: Daevaorn

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> перегрузка оператора 
:(
    Опции темы
bsa
Дата 14.5.2013, 08:00 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Модератор
Сообщений: 9185
Регистрация: 6.4.2006
Где: Москва, Россия

Репутация: 63
Всего: 196



Я это делал, когда писал с++ обертку для bigdigits.
В принципе, переопределение данного операторов (да и всех математических) имеет смысл только для матемаических классов.
PM   Вверх
baldina
Дата 14.5.2013, 10:06 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 3433
Регистрация: 5.12.2007
Где: Москва

Репутация: 32
Всего: 101



Цитата(borisbn @  14.5.2013,  06:00 Найти цитируемый пост)
когда вы в последний раз переопределяли операторы -=() и/или -() в боевом проекте?

не так часто, конечно)) обычно алгебра, bsa прав
из последних - классы математических выражений для отображения хода вычислений, года три назад. давно как-то приходилось вектора/матрицы писать

Добавлено через 6 минут и 44 секунды
еще (довольно давно) были классы для вычислений с учетом размерностей
иногда возникают небольшие задачи с использованием operator+ специального вида для строк (вроде конкатенации с добавлением разделителя)

Добавлено через 10 минут и 21 секунду
Цитата(volatile @  13.5.2013,  19:56 Найти цитируемый пост)
там создание лишнего объекта, лишний оператор присваивания

пара 
Код

a+=b
c=a+b

по идее должны давать одинаковый код при любой реализации. конечно, это от самих классов и компилятора зависит...
давайте проведем эксперимент: предлагайте (разумную) семантику operator+() в конкретном, гипотетически полезном случае - сравним
PM MAIL   Вверх
bsa
Дата 14.5.2013, 14:30 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Модератор
Сообщений: 9185
Регистрация: 6.4.2006
Где: Москва, Россия

Репутация: 63
Всего: 196



Обычно, оператор + реализуется через +=:
Код
class MyClass
{
...
    MyClass& operator+=(const MyClass &o) {
         ...
         return *this;
    }
...
};

MyClass operator+(const MyClass &o1, const MyClass &o2)
{
   MyClass ret(o1);
   ret += o2;
   return ret;
}

PM   Вверх
volatile
Дата 14.5.2013, 17:45 (ссылка) |    (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 2107
Регистрация: 7.1.2011

Репутация: 37
Всего: 85



Цитата(baldina @  14.5.2013,  10:06 Найти цитируемый пост)
предлагайте (разумную) семантику operator+() в конкретном, гипотетически полезном случае - сравним 

Ну хорошо.
Давайте, чтоб не скучно было, выведем первую тыщу чисел фибоначчи.
Код конечно далёк от оптимального, (например. в чаре храницца только число в диапазоне [0..9], ну да суть не в этом.
Код

#include <iostream>
#include <iomanip>
typedef unsigned ui;
typedef unsigned char u8;

// Для другого варианта закомментировать эту строчку =>
#define BALDINA_VARINT

class big_num
{
public:
   static const ui SIZE = 220;
   big_num (ui v = 0)
   {
      for (ui i = 0; i < SIZE; ++ i)
      {
         data [i] = v % 10;
         v /= 10;
      }
   }

   ~ big_num () {++ object_counter;} // объекты подсчитываем в деструкторе, потому-что конструкторов много, и не все видны.
   static ui object_counter;

   friend std::ostream & operator << ( std::ostream & os, const big_num & v)
   {
      bool leading_zeros = 1;
      for (ui i = 0; i < SIZE; ++ i)
      {
         ui j = SIZE - i - 1;
         if (!leading_zeros || v.data [j] || j == 0)
         {
            os << char('0' + v.data [j]);
            leading_zeros = 0;
         }
         // else
         //    os << ' ';
      }
      return os;
   }

   // -------------------------------------------
   // ----------- Начинаюцца Варианты -----------
#ifdef BALDINA_VARINT
   big_num operator + (const big_num & r) const
   {
      big_num t;
      ui carry = 0;
      for (ui i = 0; i < SIZE; ++ i)
      {
         ui val = data [i] + r.data [i] + carry;
         t.data[i] = val % 10;
         carry = val / 10;
      }
      return t;
   }

   big_num & operator += (const big_num & r)
   {
       return *this = *this + r;
   }

#else

   big_num operator + (const big_num & r) const
   {
      big_num t (*this);
      t += r;
      return t;
   }

   big_num & operator += (const big_num & r)
   {
      ui carry = 0;
      for (ui i = 0; i < SIZE; ++ i)
      {
         ui val = data [i] + r.data [i] + carry;
         data[i] = val % 10;
         carry = val / 10;
      }
      return *this;
   }
#endif
   // ----------- Конец Вариантов ---------------
   // -------------------------------------------

private:
   u8 data [SIZE];
};

ui big_num::object_counter = 0;

void fib ()
{
   big_num a (1);
   big_num b (0);
   ui num = 0;
   for (int i = 0; i < 500; ++ i)
   {
      a += b;
      std::cout << std::setw(5) << ++ num << " : " << a << std::endl;
      b = a + b;
      std::cout << std::setw(5) << ++ num << " : " << b << std::endl;
   }
}

int main ()
{
   fib ();
   
   // здесь все объекты удалены, можно вывести количесво.
   std::cout << "objects: " << big_num::object_counter;
}

Будем подсчитывать кол-во созданных объектов big_num, потребовавшихся для вычисления.
в первом варианте (BALDINA_VARINT)
сорри за имя 'BALDINA_VARINT', что-то ничего больше не пришло больше в голову. переименуйте как вам угодно.
Код
objects: 1002
во втором варианте ()
Код
objects: 502

На gcc и в студии одинаково.
В студии была максимальная оптимизация на скорость + Use Link Time Code Generation

Добавлено через 7 минут и 6 секунд
Попробовал на codepad.org.
Тыщу чисел он не смог переварить, уменьшил в 10 раз

http://codepad.org/02MJuOJS
http://codepad.org/Ol4LplTE
objects: 202 
objects: 102 

но там без оптимизации.
На домашнем компе
objects: 102
objects: 52

В любом случае разница в 2 раза количества объектов сохраняецца. smile 
PM MAIL   Вверх
baldina
Дата 15.5.2013, 14:05 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 3433
Регистрация: 5.12.2007
Где: Москва

Репутация: 32
Всего: 101



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

интересно, что (в данном конкретном случае), компилятор таки умудряется обходиться без фактического "лишнего копирования":
Код

/* b = a + b; */
012F1340  lea         eax,[ebp-1D0h]  
012F1346  push        eax  
012F1347  lea         ecx,[ebp-2B0h]  
012F134D  push        ecx  
012F134E  lea         eax,[ebp-0F0h]  
012F1354  call        big_num::operator+ (12F1060h)  
012F1359  inc         dword ptr [big_num::object_counter (12F4370h)]  

здесь от временного объекта только один след - изменение деструктором object_counter
сравните с явно определенным operator=()
Код

/* b = a + b; */
002B1344  lea         eax,[ebp-1D0h]  
002B134A  push        eax  
002B134B  lea         ecx,[ebp-2B0h]  
002B1351  push        ecx  
002B1352  lea         eax,[ebp-0F0h]  
002B1358  call        big_num::operator+ (2B1060h)  
002B135D  push        0DCh  
002B1362  push        eax  
002B1363  lea         edx,[ebp-1D0h]  
002B1369  push        edx  
002B136A  call        dword ptr [__imp__memmove (2B30D0h)]  
002B1370  inc         dword ptr [big_num::object_counter (2B4370h)]  
002B1376  add         esp,0Ch  


Это сообщение отредактировал(а) baldina - 15.5.2013, 14:05
PM MAIL   Вверх
bsa
Дата 15.5.2013, 15:18 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Модератор
Сообщений: 9185
Регистрация: 6.4.2006
Где: Москва, Россия

Репутация: 63
Всего: 196



Цитата(baldina @  15.5.2013,  15:05 Найти цитируемый пост)
кстати, вариант со свободной функцией operator+(), некоторыми компиляторами оптимизируется хуже, чем с членом класса.
К сожалению, вариант с членом класса менее гибок. Например, если суммируются объекты разных типов, но один из них может быть неявно создан из второго (конструктор соответствующий есть). То в этом случае вариант с внешним оператором будет поддерживать и a+b, и b+a, а вот вариант со встроенным - только a+b.
PM   Вверх
volatile
Дата 15.5.2013, 18:50 (ссылка) |    (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 2107
Регистрация: 7.1.2011

Репутация: 37
Всего: 85



Цитата(baldina @  15.5.2013,  14:05 Найти цитируемый пост)
если деструкторами измерять,

кол-во деструкторов == равно кол-ву констукторов == кол-ву объектов.
деструкторами измерялось только в силу удобства определения в одном месте.

Цитата(baldina @  15.5.2013,  14:05 Найти цитируемый пост)
сравните с явно определенным operator=()

Хорошо, хотя это не совсем корректно по сравнению с кол-вом объектов.
Цитата(volatile @  14.5.2013,  17:45 Найти цитируемый пост)
   ~ big_num () {++ object_counter;} // объекты подсчитываем в деструкторе, потому-что конструкторов много, и не все видны.
   big_num & operator = (const big_num & r) 
   { ++ object_counter;
      memcpy (data, r.data, SIZE);
      return *this;
   }


Тем не менее, операторов присваивания, как и деструкторов в 2 раза больше:
Код

objects: 1000
objects: 500


 smile 

Это сообщение отредактировал(а) volatile - 15.5.2013, 18:52
PM MAIL   Вверх
bsa
Дата 15.5.2013, 20:21 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Модератор
Сообщений: 9185
Регистрация: 6.4.2006
Где: Москва, Россия

Репутация: 63
Всего: 196



Кстати, оператор присваивания рекомендуется делать через операцию swap.  smile 
Код
MyClass& MyClass::operator=(const MyClass &other)
{
  MyClass(other).swap(*this);
  return *this;
}

PM   Вверх
volatile
Дата 15.5.2013, 23:41 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 2107
Регистрация: 7.1.2011

Репутация: 37
Всего: 85



Цитата(bsa @  15.5.2013,  20:21 Найти цитируемый пост)
рекомендуется делать через операцию swap.

bsa, чтоб делать через swap, нужно сначала написать этот swap  smile 
И кстати, даже если решицца писать его,  то для данного конкретного класса, swap априори будет менее эффективен чем memcpy.
Так что слепо следовать рекомендациям, далеко не всегда оправдано.


Цитата(baldina @  15.5.2013,  14:05 Найти цитируемый пост)
компилятор таки умудряется обходиться без фактического "лишнего копирования":

baldina, в общем как я понял, вы хотите сказать, что не смотря на разницу в количестве объектов, 
операторов присваивания, деструкторов и прочих сишних прибамбасов,
компилятор, каким-то неведомым, волшебным способом делает так, что код, всё равно выполняецца за одинаковое время...

Ну чтож, давайте (чтоб уже добить тему) измерим и время...

Время в миллионах тиков процессора.
(по 3 измерения на каждый вариант)

Первый вариант
Код

#define BALDINA_VARINT
20.6379
20.6013
20.5993


Второй вариант 
Код

// #define BALDINA_VARINT
13.5428
13.393
13.3941

Весь проект для 2008-ой студии (без бинарников).

 smile 


Это сообщение отредактировал(а) volatile - 15.5.2013, 23:53

Присоединённый файл ( Кол-во скачиваний: 2 )
Присоединённый файл  operators.7z 3,98 Kb
PM MAIL   Вверх
bsa
Дата 16.5.2013, 18:42 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Модератор
Сообщений: 9185
Регистрация: 6.4.2006
Где: Москва, Россия

Репутация: 63
Всего: 196



Да. Не обратил внимания. Обычно, такое через динамические массивы делают.
PM   Вверх
baldina
Дата 17.5.2013, 12:10 (ссылка) |    (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 3433
Регистрация: 5.12.2007
Где: Москва

Репутация: 32
Всего: 101



Доберусь до компа, погляжу.
Да, я имел в виду что в частных случаях (частых для математических классов) результат может быть одинаков за счет оптимизации. В общем конечно нет, если требуется вызывать сложные конструкторы/деструкторы
Как уже говорил, я сам за каноническую форму, просто хотелось побудить к размышлению и расставить все точки.
PM MAIL   Вверх
Ответ в темуСоздание новой темы Создание опроса
Правила форума "С++:Общие вопросы"
Earnest Daevaorn

Добро пожаловать!

  • Черновик стандарта C++ (за октябрь 2005) можно скачать с этого сайта. Прямая ссылка на файл черновика(4.4мб).
  • Черновик стандарта C (за сентябрь 2005) можно скачать с этого сайта. Прямая ссылка на файл черновика (3.4мб).
  • Прежде чем задать вопрос, прочтите это и/или это!
  • Здесь хранится весь мировой запас ссылок на документы, связанные с C++ :)
  • Не брезгуйте пользоваться тегами [code=cpp][/code].
  • Пожалуйста, не просите написать за вас программы в этом разделе - для этого существует "Центр Помощи".
  • C++ FAQ

Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Earnest Daevaorn

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | C/C++: Общие вопросы | Следующая тема »


 




[ Время генерации скрипта: 0.0641 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.