Перегрузка операторов
От: Red Line  
Дата: 22.05.04 14:33
Оценка:
Объясните пожалуйста что означает перегрузка операторов.
Поясните на простом примере.

Заранее благодарен всем!
... << RSDN@Home 1.1.3 stable >>
Re: Перегрузка операторов
От: adontz Грузия http://adontz.wordpress.com/
Дата: 22.05.04 15:45
Оценка: 23 (6) +1
Здравствуйте, Red Line, Вы писали:

Надо сразу сказать, что перегрузка чего-либо в Си++ это только для удобства программиста, но никак не то, без чего нельзя обойтись в принципе.

Что такое оператор? Любой? Это всего на всего некоторая фукция, кстати возвращающая значение, но у этой функции есть три особенности.
  1. Имя. Имена операторов стандартизированны. Ты можешь как угодно использовать те, что есть, но не можешь создавать свои
  2. Количество параметров. Ты мошешь как угодно обрабатывать параметры, но не можешь изменить их число. Не может быть операции + на тремя аргументами.
  3. Синтаксис вызова. Если для обычной функции параметры перечисляются в скобках, через запятую, после имени функции, то для операторов это не так.

Рассмотрим простой пример
int a = 3;
int b = 5;
int c = a + b;

Формально здесь задействованно 2 оператора. Оператор присваивания = и оператор сложения +.
Это встроенные операторы. Что это значит? Это значит, что ими можно пользоватся без написания своего кода. Они готовы к употребления с самого начала.

Что такое перегрузка операторов? Это определение оператора для какого-то типа(или типов если параметров несколько), для которого такой оператор ещё не опредлён. То есть, например, нельзя определить оператор + для параметров типа int потому что такой оператор (не важно, встроенный или нет) уже определён.

Например у нас есть тип комплексных чисел. Очевидно, что запись
complex a;
complex b;
complex c = a + b;

Понятнее и проще, чем
complex a;
complex b;
complex c = ComplexAdd(a, b);

Вот для того чтобы проще писать программы операторы и перегружают.
Есть однако одно исключение, оператор присваивания =. Его надо перегружать, если копирование объекта выполняется каким-то особым образом.
Например рассмотрим класс строки.
class string
 {
  private:
   char * str;
   int len;
  public:
   string(char * init)
    {
     len = strlen(init);
     str = new char[len + 1];
     strcpy(str, init);
    }
   ~string()
    {
     delete[] str;
    }
 }

Вроде бы класс правильный. Мы выделяем память в конструкторе и освобождаем в деструкторе. Но я не написал оператор присваивания для этого класса. Что же будет? Он сгенерируется автоматически. Такой оператор присваивания будет поэлементно (не побитово!) копировать поля из одного объекта в другой.
Как же это будет работать?
Вот простой пример
{
string a("aaaa");
string b("bbb");

b = a;
}

Итак. (Адреса памяти взяты для примера, они могут быть совершенно другими)
Сперва в конструкторе a выделится 5 байт для строки "aaaa" по адресу 0x00100000 и она будет скопирована в выделенную память.
Затем в конструкторе b выделится 4 байта для строки "bbb" по адресу 0x001000005 и она будет скопирована в выделенную память.
Далее полю str объекта b будет присвоена значение поля str объекта a и оно станет равно 0x00100000, а полю len объекта b будет присвоено значение поля len объякта a и оно станет равно 5.
Затем в деструкторе a мы особождаем ранее выделенную память по адресу 0x00100000.
Далее в деструкторе b мы особождаем ранее выделенную память по адресу 0x00100000.

Что мы имеем? Мы дважда освободили память по адресу 0x00100000 и ни разу не осободили память по адресу 0x00100005.
Первое приведёт к ошибке выполнения, второе к утечке памяти.
Как же исправить такое неправильное поведение? Надо определить оператор присваивания. Свой, не тривиальный оператор, который всё сделает как надо. А вот и он
class string
 {
  private:
   char * str;
   int len;
  public:
   string(char * init)
    {
     len = strlen(init);
     str = new char[len + 1];
     strcpy(str, init);
    }
   ~string()
    {
     delete[] str;
    }

// Возвращаем ссылку на string.
// Если не знаешь, что такео сылки, не надо утруждать себя мыслями зачем,
// достаточно пока знать, что это позволяет избежать лишнего копирования
   string & operator = (const string & s) 
    {
// Удаляем старую строку, она нам больше не понадобится.
// Обрати внимание, что это очень похоже на код деструктора
     delete[] str; 
// Выясняем длину строки и выделяем необходимое количество байт. Далее копируем новую строку в выделенную память.
// Обрати внимание, что это очень похоже на код конструктора
     len = s.len;
     str = new char[len + 1];
     strcpy(str, s.str);
// Возвращаем то, гед сейчас новое значение строки, то есть сам объект.
     return *this;
    }
 }

Вот теперь всё будет работать правильно. Справедливости ради надо сказать, что в некоторых случаях, компилятор может использовать оператор присваивания вместо конструктора копирования. Например как здесь.
string a("kuku")
string b = a; // скорее всего соптимизируется в string b(a);

Поэтому для избежания лишних проблем конструктор копирования лучше тоже написать.
   string(const string & s) 
    {
     len = s.len;
     str = new char[len + 1];
     strcpy(str, s.str);
    }
A journey of a thousand miles must begin with a single step © Lau Tsu
Re[2]: Перегрузка операторов
От: Red Line  
Дата: 22.05.04 16:45
Оценка:
Здравствуйте, adontz, Вы писали:

A> <...>


Большое спасибо за грамотыный и исчерпывающий ответ! Но есть всё же несколько не понятных моментов.
1)
string & operator = (const string & s) — означает что аргументами оператора = могут быть данные только типа string.
Я правильно понял? Т.е это будет работать только если и в левой и в правой части выражения будут переменные типа string:
string a;
string b;
a = d;//здесь произойдёт перегрузка оператора



2) Можно ли переопределить оператор так:
string & operator = (int value)
Будет ли это работать?

class A
{
int Res;
public:
//Можно ли написать оператор, который при вызове A.Res = 5; запишет целое число в поле Res моего класа A/
};

Извините, что не много сумбурно...
... << RSDN@Home 1.1.3 stable >>

Удалено избыточное цитирование. -- ПК
Re[2]: Перегрузка операторов
От: Paranoik  
Дата: 22.05.04 16:45
Оценка:
Здравствуйте, adontz, Вы писали:
A>...
A>Рассмотрим простой пример
A>
A>int a = 3;
A>int b = 5;
A>int c = a + b;
A>

A>Формально здесь задействованно 2 оператора. Оператор присваивания = и оператор сложения +.
A>...
Это конструктор с инициализацией вроде, а не operator=.
Дружба не наследуется и не транзитивна.
©Бьерн Страуструп
Re[3]: Перегрузка операторов
От: adontz Грузия http://adontz.wordpress.com/
Дата: 22.05.04 18:57
Оценка:
Здравствуйте, Paranoik, Вы писали:

P>Это конструктор с инициализацией вроде, а не operator=.


Насколько мне известно, это operator =, который может быть оптимизирован в конструктор копирования.
A journey of a thousand miles must begin with a single step © Lau Tsu
Re[3]: Перегрузка операторов
От: adontz Грузия http://adontz.wordpress.com/
Дата: 22.05.04 19:15
Оценка:
Здравствуйте, Red Line, Вы писали:

RL> string & operator = (const string & s) — означает что аргументами оператора = могут быть данные только типа string.


Да

RL> Я правильно понял? Т.е это будет работать только если и в левой и в правой части выражения будут переменные типа string:


Либо что-то, что можно привести к типу string. Например если ты напишешь класс string2, такого вида
class string2: public string
 {
 }

или такого вида
class string2
 {
  public:
   operator string(); // оператор преобразования типа
 }

То для таких классов этот оператор присваивания тоже будет работать.

RL> a = d;//здесь произойдёт перегрузка оператора


Правильнее сказать, что здесь вызывается перегруженный оператор. Перегрузка явления статическое, вополняется на основе типов аргументов на этапе компиляции.

RL>2) Можно ли переопределить оператор так:

RL> string & operator = (int value)

Да, можно.

RL> Будет ли это работать?


А почему бы и нет?

RL> class A

RL> {
RL> int Res;
RL> public:
RL> //Можно ли написать оператор, который при вызове A.Res = 5; запишет целое число в поле Res моего класа A/
RL> };

Нет, так нельзя. Можно написать оператор который при вызове A = 5, запишет в Res пятёрку.

ЗЫ: Стирай ненужные части сообщения, на которое отвечаешь.
A journey of a thousand miles must begin with a single step © Lau Tsu
Re[4]: Перегрузка операторов
От: Paranoik  
Дата: 22.05.04 19:47
Оценка:
Здравствуйте, adontz, Вы писали:

A>Насколько мне известно, это operator =, который может быть оптимизирован в конструктор копирования.


class TTest
{
public:
  TTest(int){cout << "Constructor";}
  int operator=(int){cout << "Operator";return 1;}
};

int main( int argc, char * argv[] )
{
  TTest test = 7;
  return 0;
}

Constructor

Теперь изменим:
class TTest
{
public:
//  TTest(int){cout << "Constructor";}
  int operator=(int){cout << "Operator";return 1;}
};

int main( int argc, char * argv[] )
{
  TTest test = 7;
  return 0;
}

"untitled1.cpp": E2034 Cannot convert 'int' to 'TTest' in function main(int,char * *) at line ...
Дружба не наследуется и не транзитивна.
©Бьерн Страуструп
Re[5]: Перегрузка операторов
От: adontz Грузия http://adontz.wordpress.com/
Дата: 22.05.04 20:42
Оценка:
Здравствуйте, Paranoik, Вы писали:

P>"untitled1.cpp": E2034 Cannot convert 'int' to 'TTest' in function main(int,char * *) at line ...


Тест некорректный. Для классов (non-POD types) действуют другие правила.
А дял не лкссов мы ничего не сможем проверить Поэтоve остаётся hfpdt xnj отослать к 8.5 стандарта зотя, если честно, мне самому это как аргумент не нравится
Почему-то мне достался PDF со стандартом из которого нельзя делать Copy/Paste. Побежал искать лекарства
A journey of a thousand miles must begin with a single step © Lau Tsu
Re[4]: Перегрузка операторов
От: Red Line  
Дата: 23.05.04 07:33
Оценка:
Здравствуйте, adontz, Вы писали:


class string2
 {
  public:
   operator string(); // оператор преобразования типа---------------->> Поясните пожалуйста как он работает.
 }




RL>> class A

RL>> {
RL>> int Res;
RL>> public:
RL>> //Можно ли написать оператор, который при вызове A.Res = 5; запишет целое число в поле Res моего класа A/
RL>> };

A>Нет, так нельзя. Можно написать оператор который при вызове A = 5, запишет в Res пятёрку.


Можно ли решить такую задачу:
Необхохдимо полностью скрыть "паразитный" код в котором происходит выбрасывание исключения так,
чтобы внешне казалось, что ошибки в программе вобще не обрабатываются.
Я начну — если напутаю с исключениями поправьте плиз...


clas error
{
   int errorCode;
 public:
   error(int value) {errorCode = value;}
   
   void ShowError()
   {
     if (errorCode == 1) MessageBox(...)....
     if (errorCode == 2) MessageBox(...)....
   }
};

class A
{
  public:
  (A) operator = (int RetCode)
  {
    if !RetCode return 0;
    throw error(RetCode);------------------------------------>> Вот это и есть тот "паразитный" код который я пытаюсь убрать из основного текста
                                                                программы и скрыть его внутри оператора =
  } 
};

A a;   
.....
.....
Гдето в коде...
a = CreateSemaphore(...); ---------------->> вот здесь по замыслу, должен вызываться перегруженый оператор и создавать объект исключения,
                                             который потом будет пойман в catch error& {} 

catch(error& e)
{
  e.ShowError();
}


У меня ничего не вышло... В чём моя ошибка?
Re[5]: Перегрузка операторов
От: adontz Грузия http://adontz.wordpress.com/
Дата: 23.05.04 08:53
Оценка:
Здравствуйте, Red Line, Вы писали:

RL>
RL>class string2
RL> {
RL>  public:
RL>   operator string(); // оператор преобразования типа---------------->> Поясните пожалуйста как он работает.
RL> }
RL>


Каждый раз, когда нужно будет преобразование к типу string будет вызван оператор string().

Вот простой пример.

class A
    {
        public:
            operator int()
                {
                    return 15;
                }
    };
//
A a;
int x = a; // x будет иметь значение 15.


RL>Можно ли решить такую задачу:

RL>Необхохдимо полностью скрыть "паразитный" код в котором происходит выбрасывание исключения так,
RL>чтобы внешне казалось, что ошибки в программе вобще не обрабатываются.
RL>Я начну — если напутаю с исключениями поправьте плиз...

Так? (см. класс A)

//
#include <iostream>
//
using namespace std;
//
class MyException
 {
        private:
            int value;
        public:
            MyException(const int & initValue) : value(initValue)
             {
             }
            int GetValue()
                {
                    return value;
                }
 };
//
class A
    {
        public:
            A & operator = (const int & returnValue)
                {
                    if (returnValue == 0) return *this;
                    throw MyException(returnValue);
                }
    };
//
class B
    {
        public:
            B(const int & returnValue)
                {
                    if (returnValue != 0)
                        {
                            throw MyException(returnValue);
                        }
                }
    };
//
#define C(returnValue) if (returnValue != 0) throw MyException(returnValue);
//
int main( int argc, char * argv[], char * envp[] )
    {
        A a;
        try
         {
                cout << "1" << endl;
                a = 1;
         }
        catch(MyException & e)
            {
                cout << e.GetValue() << " catched" << endl;
            }
        try
         {
                cout << "2" << endl;
                B(2);
         }
        catch(MyException & e)
            {
                cout << e.GetValue() << " catched" << endl;
            }
        try
         {
                cout << "3" << endl;
                C(3);
         }
        catch(MyException & e)
            {
                cout << e.GetValue() << " catched" << endl;
            }
        return 0;
    }
//
// End of file
//

А вообще несколько странный способ обработки ошибок Я бы запихал ту же функциональность не в оператор присваивания, а в конструктор (класс B) или макрос (макрос С)
A journey of a thousand miles must begin with a single step © Lau Tsu
Re[2]: Перегрузка операторов
От: Павел Кузнецов  
Дата: 23.05.04 09:18
Оценка: 50 (6)
> Надо сразу сказать, что перегрузка чего-либо в Си++ это только для удобства программиста, но никак не то, без чего нельзя обойтись в принципе.

Учитывая правила выбора лучшего кандидата, можно сказать, что наличие перегрузки позволяет использовать некоторые техники, которые без этого недоступны. Например, выбор лучшего алгоритма в зависимости от типа итератора, как это делается в STLport. Альтернативой этому теоретически могла бы быть частичная специализация, но выбор наиболее подходящей специализации тоже определяется через правила выбора из перегруженных функций.

> Что такое оператор? Любой? Это всего на всего некоторая фукция


Не совсем. Предопределенные операторы для встроенных типов функциями не являются. В частности, нельзя взять их адрес, они не окружены точками следования и т.п.

> Рассмотрим простой пример

>
> int a = 3;
> int b = 5;
> int c = a + b;
>

> Формально здесь задействованно 2 оператора. Оператор присваивания = и оператор сложения +.

Оператора присваивания здесь нет. Здесь есть инициализация. Это суть разные вещи.

> сказать, что в некоторых случаях, компилятор может использовать оператор присваивания вместо конструктора копирования. Например как здесь.

>
> string a("kuku")
> string b = a; // скорее всего соптимизируется в string b(a);
>


Нет, оптимизация здесь ни при чем. Здесь в любом случае должен быть использован конструктор копирования (8.5/14). В частности, для string конструктор по умолчанию в данном случае не требуется, что было бы нужно, если бы стандарт предписывал использовать operator=.
Posted via RSDN NNTP Server 1.9 alpha
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Re[6]: Перегрузка операторов
От: Аноним  
Дата: 23.05.04 10:03
Оценка:
Здравствуйте, adontz, Вы писали:

A>Здравствуйте, Red Line, Вы писали:


RL>>
RL>>class string2
RL>> {
RL>>  public:
RL>>   operator string(); // оператор преобразования типа---------------->> Поясните пожалуйста как он работает.
RL>> }
RL>>


A>Каждый раз, когда нужно будет преобразование к типу string будет вызван оператор string().


A>Вот простой пример.


A>
A>class A
A>    {
A>        public:
A>            operator int()
A>                {
A>                    return 15;
A>                }
A>    };
A>//
A>A a;
A>int x = a; // x будет иметь значение 15.
A>


RL>>Можно ли решить такую задачу:

RL>>Необхохдимо полностью скрыть "паразитный" код в котором происходит выбрасывание исключения так,
RL>>чтобы внешне казалось, что ошибки в программе вобще не обрабатываются.
RL>>Я начну — если напутаю с исключениями поправьте плиз...

A>Так? (см. класс A)


A>
A>//
A>#include <iostream>
A>//
A>using namespace std;
A>//
A>class MyException
A> {
A>        private:
A>            int value;
A>        public:
A>            MyException(const int & initValue) : value(initValue)------------------------>> Здесь не понял. Я думал, что value(initValue)
A>             {                                                                              это константный инициализатор, который надо 
A>             }                                                                              использовать при инициализации члена класса  
A>            int GetValue()                                                                  являющегося константой. Поясните пожалуйста 
A>                {                                                                       этот момент. 
A>                    return value;
A>                }
A> };
A>//
A>class A
A>    {
A>        public:
A>            A & operator = (const int & returnValue)------------------------------------->> я не проверял как это работает, но мне не
A>                {                                                                       понятно почему Вы указываете в качестве воз 
A>                    if (returnValue == 0) return *this;                             вращаемого оператором значения A&??? 
A>                    throw MyException(returnValue);                                 Так же не понятно почему const int&                }                                                                               а не просто const int. Для чего ссылка? 
A>    };                                                                                              Думаю, что я не правильно понимаю, что
A>//                                                                                                    такое ссылка. В моём понимании ссылка есть
                                                                                                        авто разименовывающийся указатель. Т.е
                                                                                                        *returnValue писать не нужно - это произойдёт
                                                                                                        само. Т.е для удобства правильно понимаю? 
A>class B                                                                                               
A>    {
A>        public:
A>            B(const int & returnValue)
A>                {
A>                    if (returnValue != 0)
A>                        {
A>                            throw MyException(returnValue);
A>                        }
A>                }
A>    };
A>//
A>#define C(returnValue) if (returnValue != 0) throw MyException(returnValue);
A>//
A>int main( int argc, char * argv[], char * envp[] )
A>    {
A>        A a;
A>        try
A>         {
A>                cout << "1" << endl;
A>                a = 1;-------------------->> в левой части operator = стоит переменная с типом A. Тогда почему тип значения
A>         }                                           возвращаемого оператором = A&. Для меня ЭТО важно понять!
A>        catch(MyException & e)
A>            {
A>                cout << e.GetValue() << " catched" << endl;
A>            }
A>        try
A>         {
A>                cout << "2" << endl;-------------------------->> А что это даёт в смысле применения? Мне нужно, чтобы при вызове
A>                B(2);                                            системной ф-ции была проверка на возвращаемой ей значения. Если 
A>         }                                                               оно не ноль, то бросать исключение . при этом весь код получающий
A>        catch(MyException & e)                                           описание ошибки ( GetLastError() и далее FormatMessage() ) было
A>                                                                     был скрыть внутри класса. Сокрытие здесь есть, но писать
A>                cout << e.GetValue() << " catched" << endl;      if ( (ret = CreateSemaphre(...)) != 0 ) B(ret) не так красиво как это
A>            }                                                        Вы показали в первом случае (при перегурзке operator =). Тогда всё полу
A>        try                                                              чается прозрачно! Иля я не прав?
A>         {
A>                cout << "3" << endl;
A>                C(3);------------------------------------------>> Лучше чем в предыдущем случае,
A>         }                                                                
A>        catch(MyException & e)                                             С( CreateSemaphre(...) );---->  или я не так чего то понимаю?             {
A>                cout << e.GetValue() << " catched" << endl;
A>            }
A>        return 0;
A>    }
A>//
A>// End of file
A>//
A>

A>А вообще несколько странный способ обработки ошибок Я бы запихал ту же функциональность не в оператор присваивания, а в конструктор (класс B) или макрос (макрос С)
Re[3]: Перегрузка операторов
От: Кодт Россия  
Дата: 23.05.04 10:13
Оценка: 22 (1) +1
Здравствуйте, Павел Кузнецов, Вы писали:

>> Надо сразу сказать, что перегрузка чего-либо в Си++ это только для удобства программиста, но никак не то, без чего нельзя обойтись в принципе.


ПК>Учитывая правила выбора лучшего кандидата, можно сказать, что наличие перегрузки позволяет использовать некоторые техники, которые без этого недоступны. Например, выбор лучшего алгоритма в зависимости от типа итератора, как это делается в STLport. Альтернативой этому теоретически могла бы быть частичная специализация, но выбор наиболее подходящей специализации тоже определяется через правила выбора из перегруженных функций.


Не пугай человека
Если коротко, то перегрузка функций (в том числе операторов) позволяет компилятору автоматически "дописывать" программы на произвольной глубине; без перегрузки — ВЕСЬ объём работы по выбору подходящих функций и типов перекладывается на программиста.
Пример: функция сортировки std::sort использует перегруженные операторы <, = и адресную арифметику. Без перегрузки — либо программист должен переписать этот алгоритм для каждого своего типа данных, либо передавать адреса соответствующих функций как callback-параметры. (сишная функция ::qsort принимает только сравнение, что существенно ограничивает её применение).

>> Что такое оператор? Любой? Это всего на всего некоторая фукция


ПК>Не совсем. Предопределенные операторы для встроенных типов функциями не являются. В частности, нельзя взять их адрес, они не окружены точками следования и т.п.


А конструкторы/деструкторы/присваивания POD-типов — тоже?

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

>> Рассмотрим простой пример

>>
>> int a = 3;
>> int b = 5;
>> int c = a + b;
>>

>> Формально здесь задействованно 2 оператора. Оператор присваивания = и оператор сложения +.

ПК>Оператора присваивания здесь нет. Здесь есть инициализация. Это суть разные вещи.


Более корректный пример был бы
int c;
c = a + b;


>> сказать, что в некоторых случаях, компилятор может использовать оператор присваивания вместо конструктора копирования. Например как здесь.

>>
>> string a("kuku")
>> string b = a; // скорее всего соптимизируется в string b(a);
>>


ПК>Нет, оптимизация здесь ни при чем. Здесь в любом случае должен быть использован конструктор копирования (8.5/14). В частности, для string конструктор по умолчанию в данном случае не требуется, что было бы нужно, если бы стандарт предписывал использовать operator=.


Соответственно,
string b; // конструктор без параметров
b = a; // оператор присваивания
... << RSDN@Home 1.1.2 stable >>
Перекуём баги на фичи!
Re[7]: Перегрузка операторов
От: Red Line  
Дата: 23.05.04 10:23
Оценка:
А>Здравствуйте, adontz, Вы писали:


  >class A
A>    {
A>        public:
A>            operator int()------------------ >> А где здесь оператор? operator - ключевое слово
A>                {                                                     int - встроенный в комплятор тип данных
A>                    return 15;                  Выходит что () есть обозначение оператора приведения типа? 
A>                }
A>    };
A>//
A>A a;
A>int x = a; // x будет иметь значение 15.
Почему x x будет иметь значение 15? Здесь же участвует operator =, как это работает?
Re[3]: Перегрузка операторов
От: adontz Грузия http://adontz.wordpress.com/
Дата: 23.05.04 11:38
Оценка:
Здравствуйте, Павел Кузнецов, Вы писали:

ПК>Учитывая правила выбора лучшего кандидата, можно сказать, что наличие перегрузки позволяет использовать некоторые техники, которые без этого недоступны. Например, выбор лучшего алгоритма в зависимости от типа итератора, как это делается в STLport. Альтернативой этому теоретически могла бы быть частичная специализация, но выбор наиболее подходящей специализации тоже определяется через правила выбора из перегруженных функций.


ДА, пожалуй.

ПК>Не совсем. Предопределенные операторы для встроенных типов функциями не являются. В частности, нельзя взять их адрес, они не окружены точками следования и т.п.


Ну это скорее исключение, а не правило. Для понимания лучше рассматривать оператор как функцию. Хотя ты конечно прав.

ПК>Оператора присваивания здесь нет. Здесь есть инициализация. Это суть разные вещи.


Для классов да, но разве для POD типов это не присваивание? Я долго читал раздел 8.5 стандарта (ИМХО на редкость непонятный) и понял именно так.

ПК>Нет, оптимизация здесь ни при чем. Здесь в любом случае должен быть использован конструктор копирования (8.5/14). В частности, для string конструктор по умолчанию в данном случае не требуется, что было бы нужно, если бы стандарт предписывал использовать operator=.


Ааа да. вот здесь ты абсолютно прав. Это я поторопился
A journey of a thousand miles must begin with a single step © Lau Tsu
Re[7]: Перегрузка операторов
От: adontz Грузия http://adontz.wordpress.com/
Дата: 23.05.04 11:50
Оценка:
Здравствуйте, Аноним, Вы писали:

A>> MyException(const int & initValue) : value(initValue)

А>Здесь не понял. Я думал, что value(initValue)
А>это константный инициализатор, который надо
А>использовать при инициализации члена класса
А>являющегося константой. Поясните пожалуйста
А>этот момент.

Такой синтаксиси можно использовать не только для константных членов класса, а для любых.

A>> A & operator = (const int & returnValue)

А>я не проверял как это работает, но мне не
А>понятно почему Вы указываете в качестве воз
А>вращаемого оператором значения A&???

Я не зря говорил что оператор надо рассматривать как функцию.
A = 3;
равносильно (не то же самое, равносильно чисто логически)
A = A.operator = (3);
Или даже так
A = operator = (A, 3);

А>Так же не понятно почему const int&

А>а не просто const int. Для чего ссылка?

Просто хорошая привычка.

А>Думаю, что я не правильно понимаю, что

А>такое ссылка. В моём понимании ссылка есть
А>авто разименовывающийся указатель. Т.е
А>*returnValue писать не нужно — это произойдёт
А>само. Т.е для удобства правильно понимаю?

Если речь идёт о ссылках при передачи параметров, то в принципе понимание более или менее правильное.
Но объяснять что такое ссылка это обширнейшая тема.

A>>a = 1;-------------------->>

А> в левой части operator = стоит переменная с типом A.
А> Тогда почему тип значения
А> возвращаемого оператором = A&.
А> Для меня ЭТО важно понять!

Потому что если мы возвратим A, а не A &, то должно будет до кучи произойти присваивание A = A.
В принципе оператор
A operator = (A);
Вообще не будет работать.
Кажется я плохо объяснил, но с тем что выше должно быть понятнее.

А>А что это даёт в смысле применения? Мне нужно, чтобы при вызове

А>системной ф-ции была проверка на возвращаемой ей значения. Если
А>оно не ноль, то бросать исключение . при этом весь код получающий
А>описание ошибки ( GetLastError() и далее FormatMessage() ) было
А>был скрыть внутри класса. Сокрытие здесь есть, но писать
А>if ( (ret = CreateSemaphre(...)) != 0 ) B(ret) не так красиво как это
А>Вы показали в первом случае (при перегурзке operator =). Тогда всё получается прозрачно! Или я не прав?

А почему бы не написать B(CreateSemaphore(...));

А>С( CreateSemaphre(...) ); или я не так чего то понимаю?


Правильно понимаешь. С B точно так же.
A journey of a thousand miles must begin with a single step © Lau Tsu
Re[8]: Перегрузка операторов
От: adontz Грузия http://adontz.wordpress.com/
Дата: 23.05.04 12:11
Оценка:
Здравствуйте, Red Line, Вы писали:

RL>
  >>class A
A>>    {
A>>        public:
A>>            operator int()------------------ >> А где здесь оператор? operator - ключевое слово
A>>                {                                                     int - встроенный в комплятор тип данных
A>>                    return 15;                  Выходит что () есть обозначение оператора приведения типа? 
A>>                }
A>>    };
A>>//
A>>A a;
A>>int x = a; // x будет иметь значение 15.
A>>

RL>Почему x x будет иметь значение 15? Здесь же участвует operator =, как это работает?

Какой оператор =? У класса A нет оператора = Участвует int::operator=.
int x = a;
в данном случае равноценно
int x = x.operator=( A.operator(int) );

ИМХО тебе лучше написать класса с кучей перегруженных операторо каждый из которых будет при своём выхове выдавать в консоль своё имя, а потом составляя из них конструкции смотреть что выйдет.
A journey of a thousand miles must begin with a single step © Lau Tsu
Re[2]: Перегрузка операторов
От: Шахтер Интернет  
Дата: 23.05.04 17:07
Оценка:
Здравствуйте, adontz, Вы писали:

A>Здравствуйте, Red Line, Вы писали:


A>Надо сразу сказать, что перегрузка чего-либо в Си++ это только для удобства программиста, но никак не то, без чего нельзя обойтись в принципе.


С трудом себе представляю реализацию класса комплексных чисел без перегрузки операторов. Вообще, если так рассуждать, то весь C++ -- для удобства программиста, а не то, без чего нельзя обойтись в принципе. Пишут же на C.
... << RSDN@Home 1.1.0 stable >>
В XXI век с CCore.
Копай Нео, копай -- летать научишься. © Matrix. Парадоксы
Re[4]: Перегрузка операторов
От: Павел Кузнецов  
Дата: 23.05.04 17:48
Оценка:
> ПК>Оператора присваивания здесь нет. Здесь есть инициализация. Это суть разные вещи.
>
> Для классов да, но разве для POD типов это не присваивание? Я долго читал раздел 8.5 стандарта (ИМХО на редкость непонятный) и понял именно так.

И для POD-типов здесь тоже инициализация: нигде в 8.5 нет речи о замене инициализации операциями присваивания. Вообще-то в плане инициализации разделение происходит не на POD-типы и не POD-типы, а на инициализацию ссылок, инициализацию массивов символов, инициализацию прочих массивов, инициализацию объектов классов, инициализацию объектами классов и прочую инициализацию. POD-класс типы попадают в пункты инициализации объектов классов и инициализацию объектами классов. Фундамендтальные типы, тоже являющиеся POD-типами, попадают в "прочую инициализацию". Вот пункт, применимый к фундаментальным типам:

  • Otherwise, the initial value of the object being initialized is the (possibly converted) value of the initializer expression. Standard conversions (clause 4) will be used, if necessary, to convert the initializer expression to the cv-unqualified version of the destination type; no user-defined conversions are considered. If the conversion cannot be done, the initialization is ill-formed. [Note: an expression of type “cv1 T” can initialize an object of type “cv2 T” independently of the cv-qualifiers cv1 and cv2.
    int a;
    const int b = a;
    int c = b;

    —end note]

  • Posted via RSDN NNTP Server 1.9 alpha
    Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
    Re[3]: Перегрузка операторов
    От: adontz Грузия http://adontz.wordpress.com/
    Дата: 23.05.04 19:12
    Оценка:
    Здравствуйте, Шахтер, Вы писали:

    Ш>С трудом себе представляю реализацию класса комплексных чисел без перегрузки операторов. Вообще, если так рассуждать, то весь C++ -- для удобства программиста, а не то, без чего нельзя обойтись в принципе. Пишут же на C.


    И на ассемблере
    A journey of a thousand miles must begin with a single step © Lau Tsu
    Подождите ...
    Wait...
    Пока на собственное сообщение не было ответов, его можно удалить.