Разное

Присваивание в си: Операция присваивания: =. Язык Си

С | Операции присваивания

Операции присваивания

Последнее обновление: 18.05.2017

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

Существует базовая операция присваивания =, которая позволяет присвоить значение правого операнда левому операнду:

int x = 2 + 5

Выражение 2 + 5 представляет правый операнд, и его значение присваивается левому операнду.

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

Также можно выполнять множественное присваивание:


int a, b, c;	
a = b = c = 34 +7;

Здесь сначала вычисляется значение выражения c = 34 +7. Значение правого операнда — 34 +7 присваивается левому операнду с.
То есть переменная c получает значение 41. Далее вычисляется выражение b = c: значение правого операнда c (41) присваивается левому операнду b.
И в конце вычисляется выражение a = b: значение правого операнда b (41) присваивается левому операнду
a.

Все остальные операции присваивания являются сочетанием простой операции присваивания с другими операциями:

  • +=: присваивание после сложения. Присваивает левому операнду сумму левого и правого операндов: A += B эквивалентно
    A = A + B

  • -=: присваивание после вычитания. Присваивает левому операнду разность левого и правого операндов:
    A -= B эквивалентно A = A — B

  • *=: присваивание после умножения. Присваивает левому операнду произведение левого и правого операндов:
    A *= B эквивалентно A = A * B

  • /=: присваивание после деления. Присваивает левому операнду частное левого и правого операндов:
    A /= B эквивалентно A = A / B

  • %=: присваивание после деления по модулю. Присваивает левому операнду остаток от целочисленного деления левого операнда на правый:
    A %= B эквивалентно A = A % B

  • <<=: присваивание после сдвига разрядов влево. Присваивает левому операнду результат сдвига его битового представления влево на определенное количество разрядов, равное значению
    правого операнда: A <<= B эквивалентно A = A << B

  • >>=: присваивание после сдвига разрядов вправо. Присваивает левому операнду результат сдвига его битового представления вправо на определенное количество разрядов, равное значению
    правого операнда: A >>= B эквивалентно A = A >> B

  • &=: присваивание после поразрядной конъюнкции. Присваивает левому операнду результат поразрядной конъюнкции его
    битового представления с битовым представлением правого операнда: A &= B эквивалентно A = A & B

  • |=: присваивание после поразрядной дизъюнкции. Присваивает левому операнду результат поразрядной дизъюнкции его
    битового представления с битовым представлением правого операнда: A |= B эквивалентно A = A | B

  • ^=: присваивание после операции исключающего ИЛИ. Присваивает левому операнду результат операции исключающего ИЛИ его
    битового представления с битовым представлением правого операнда: A ^= B эквивалентно A = A ^ B

Примеры операций:


int a = 5;
a += 10;		// 15
a -= 3;			// 12
a *= 2;			// 24
a /= 6;			// 4
a <<= 4;		// 64
a >>= 2;		// 16

C++ | Операции присваивания

Операции присваивания

Последнее обновление: 13.09.2017

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

Базовая операция присваивания = позволяет присвоить значение правого операнда левому операнду:


int x; 
x = 2

То есть в данном случае переменная x (левый операнд) будет иметь значение 2 (правый операнд).

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

При этом операции присваивания имеют правосторонний порядок, то есть выполняются справа налево. И, таким образом, можно выполнять
множественное присваивание:


int a, b, c;	
a = b = c = 34;

Здесь сначала вычисляется значение выражения c = 34. Значение правого операнда — 34 присваивается левому операнду с.
Далее вычисляется выражение b = c: значение правого операнда c (34) присваивается левому операнду
b. И в конце вычисляется выражение a = b: значение правого операнда b (34) присваивается левому операнду
a.

Кроме того, следует отметить, что операции присваивания имеют наименьший приоритет по сравнению с другими типами операций, поэтому выполняются
в последнюю очередь:


int x;
x = 3 + 5;

В соответствии с приоритетом операций вначале выполняется выражение 3 + 5, и только потом его значение присваивается переменной x.

Все остальные операции присваивания являются сочетанием простой операции присваивания с другими операциями:

  • +=: присваивание после сложения. Присваивает левому операнду сумму левого и правого операндов: A += B эквивалентно
    A = A + B

  • -=: присваивание после вычитания. Присваивает левому операнду разность левого и правого операндов:
    A -= B эквивалентно A = A — B

  • *=: присваивание после умножения. Присваивает левому операнду произведение левого и правого операндов:
    A *= B эквивалентно A = A * B

  • /=: присваивание после деления. Присваивает левому операнду частное левого и правого операндов:
    A /= B эквивалентно A = A / B

  • %=: присваивание после деления по модулю. Присваивает левому операнду остаток от целочисленного деления левого операнда на правый:
    A %= B эквивалентно A = A % B

  • <<=: присваивание после сдвига разрядов влево. Присваивает левому операнду результат сдвига его битового представления влево на определенное количество разрядов, равное значению
    правого операнда: A <<= B эквивалентно A = A << B

  • >>=: присваивание после сдвига разрядов вправо. Присваивает левому операнду результат сдвига его битового представления вправо на определенное количество разрядов, равное значению
    правого операнда: A >>= B эквивалентно A = A >> B

  • &=: присваивание после поразрядной конъюнкции. Присваивает левому операнду результат поразрядной конъюнкции его
    битового представления с битовым представлением правого операнда: A &= B эквивалентно A = A & B

  • |=: присваивание после поразрядной дизъюнкции. Присваивает левому операнду результат поразрядной дизъюнкции его
    битового представления с битовым представлением правого операнда: A |= B эквивалентно A = A | B

  • ^=: присваивание после операции исключающего ИЛИ. Присваивает левому операнду результат операции исключающего ИЛИ его
    битового представления с битовым представлением правого операнда: A ^= B эквивалентно A = A ^ B

Примеры операций:


int a = 5;
a += 10;		// 15
a -= 3;			// 12
a *= 2;			// 24
a /= 6;			// 4
a <<= 4;		// 64
a >>= 2;		// 16

C# и .NET | Операции присваивания

Операции присваивания

Последнее обновление: 19.06.2017

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

Как и во многих других языках программирования, в C# имеется базовая операция присваивания =, которая присвоивает
значение правого операнда левому операнду:


int number = 23;

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

Также можно выполнять множественно присвоение сразу нескольких переменным одновременно:


int a, b, c;
a = b = c = 34;

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


int a, b, c;
a = b = c = 34 * 2 / 4; // 17

Сначала будет вычисляться выражение 34 * 2 / 4, затем полученное значение будет присвоено переменным.

Кроме базовой операции присвоения в C# есть еще ряд операций:

  • +=: присваивание после сложения. Присваивает левому операнду сумму левого и правого операндов: выражение
    A += B равнозначно выражению A = A + B

  • -=: присваивание после вычитания. Присваивает левому операнду разность левого и правого операндов:
    A -= B эквивалентно A = A — B

  • *=: присваивание после умножения. Присваивает левому операнду произведение левого и правого операндов:
    A *= B эквивалентно A = A * B

  • /=: присваивание после деления. Присваивает левому операнду частное левого и правого операндов:
    A /= B эквивалентно A = A / B

  • %=: присваивание после деления по модулю. Присваивает левому операнду остаток от целочисленного деления левого операнда на правый:
    A %= B эквивалентно A = A % B

  • &=: присваивание после поразрядной конъюнкции. Присваивает левому операнду результат поразрядной конъюнкции его
    битового представления с битовым представлением правого операнда: A &= B эквивалентно A = A & B

  • |=: присваивание после поразрядной дизъюнкции. Присваивает левому операнду результат поразрядной дизъюнкции его
    битового представления с битовым представлением правого операнда: A |= B эквивалентно A = A | B

  • ^=: присваивание после операции исключающего ИЛИ. Присваивает левому операнду результат операции исключающего ИЛИ его
    битового представления с битовым представлением правого операнда: A ^= B эквивалентно A = A ^ B

  • <<=: присваивание после сдвига разрядов влево. Присваивает левому операнду результат сдвига его битового представления влево на определенное количество разрядов, равное значению
    правого операнда: A <<= B эквивалентно A = A << B

  • >>=: присваивание после сдвига разрядов вправо. Присваивает левому операнду результат сдвига его битового представления вправо на определенное количество разрядов, равное значению
    правого операнда: A >>= B эквивалентно A = A >> B

Применение операций присвоения:


int a = 10;
a += 10;        // 20
a -= 4;         // 16
a *= 2;         // 32
a /= 8;         // 4
a <<= 4;      // 64
a >>= 2;      // 16

Операции присвоения являются правоассоциативными, то есть выполняются справа налево. Например:


int a = 8;
int b = 6;
int c = a += b -= 5;	// 9

В данном случае выполнение выражения будет идти следующим образом:

  1. b -= 5 (6-5=1)

  2. a += (b-=5) (8+1 = 9)

  3. c = (a += (b-=5)) (c = 9)

Оператор присваивания. Запись значения в переменную на Си.

Пожалуйста, приостановите работу AdBlock на этом сайте.



Как Вы помните, переменные нужны чтобы хранить в них какие-то данные. Разберёмся как использовать переменные по их прямому назначению. Основной вопрос такой: «Как сохранить значение в переменной». Или вспоминая аналогию с коробкой «Как положить что-то в коробку?»

Ответ прост. Для этого существует специальный оператор, который называется оператором присваивания и обозначается символом равно «=». Обычно говорят не сохранить значение в переменную, а присвоить переменной значение.

Как работает оператор присваивания?

Сразу будем разбираться на примерах.

Листинг 1. Присваивание значения переменной

int z;	// объявляем переменную целого типа с именем z
z = 5;	// заносим в переменную число 5

double pi = 3.1415926; // создаём переменную и сразу присваиваем ей значение 

В примере выше показаны два способа присвоить значение переменной. В первом случае мы сначала создаём переменную, а потом записываем в неё значение, а во втором случае мы сразу создаём переменную и присваиваем ей значение.

Второй вариант записи короче, но делает в то же самое, что и первый. Берёт значение, которое записано справа от знака равно, и сохраняет его в переменную, которая записана слева от от него.

Частая ошибка!

Не забывайте ставить после очередной команды точку с запятой.

Есть несколько важных моментов на которых надо отдельно заострить внимание:

  • в некоторых языках программирования, после того как вы создали переменную, в ней сразу же хранится значение нуль. В языке Си это не так. Пока переменной не присвоено какое-нибудь значение, в ней будет храниться «мусор». Как этот мусор выглядит мы посмотрим на следующем уроке.
  • справа от знака равно НЕ обязательно должно быть записано какое-то конкретное число, там может быть записано арифметическое выражение или имя переменной.

Остановимся на втором пункте подробнее. Чтобы лучше разобраться давайте обратимся к примерам.

Если справа от знака равно записано выражение, то сначала вычисляется его значение и уже оно сохраняется в переменную, которая записана слева от =.

Листинг 2.

int n;	
n = 1+2;	// В переменную n записываем значение 3

Когда переменной присваивается значение, то то, что хранилось в ней до этого удаляется.

Листинг 3.

int n=10;	// Создаём переменную и записываем в неё 10
n = 5;	// Теперь в переменной записано значение 5, а значение 10 удалено. 

Обратите внимание, новое значение не добавляется к старому, а полностью заменяет его. Я как-то говорил, что переменные похожи на коробки, но только на первый взгляд. Теперь мы видим, что наша аналогия с коробкой не совсем точная. Если бы в коробке лежало десять шариков, и мы бы туда положили ещё пять, то их стало бы 15.

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

Листинг 4.

int n=10, x=0; 	// можно инициализировать сразу несколько переменных
n = 5; 	// теперь n равно 5
x = n - 3; // x будет равно 2 (5-3)

При вычислении выражения n — 3, n равняется 5. Это потому, что исходное значение 10, заданное при инициализации, мы в программе изменяли. А при вычислениях используется текущее значение переменной.

В выражении справа от знака равенства может быть использована сама переменная.

Листинг 5.

int n=10;
n = n + 5; 	// n будет равно 15 (10+5)

Кажется, что это какой-то порочный круг, но на самом деле проблем здесь не возникнет. Т.к. сначала вычисляется выражение справа, а только потом происходит присваивание. По аналогии с предыдущим примером при вычислении значения справа от знака равно вместо переменной n будет подставлено её текущее значение, т.е. 10. Потом будет произведено сложение и присваивание.

Ещё один поучительный пример.

Листинг 6.

int x=5, y=0, z;
y = x + z; 	// что будет записано в переменной y?

Если Вы подумали что 5, то это неправильно. Никто не знает, что в ней будет записано. Т.к. неизвестно, что было записано в переменной z. Ещё раз напоминаю, что если переменной не присвоено никакое значение, в ней хранится не нуль, а неизвестно что.

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

Листинг 7.

int a = 2, b=9, c=4, D;
D = b*b - 4*a*c; 	// D будет равно 49 (9*9 - 4*2*4)  

Теперь ещё один важный момент, который надо понимать. Когда я рассказывал о типах данных, я говорил, что в переменной можно хранить значения только того же типа, что и тип переменной. Например, в переменную целого типа нельзя положить вещественное число. Этот принцип применяется и с выражениями. Чтобы программа работала корректно, необходимо, чтобы тип выражения или переменной справа от знака присваивания совпадал с типом переменной.

Посмотрим несколько примеров.

Листинг 8.

int n, a = 2;
double x, pi = 3.14;
char s = 'k'; 	// сохраняем в s символ k. Сам символ нужно написать в одинарных кавычках.  

s = pi; // неправильно. В коробку для символов пытаемся положить вещественное число
n = a*pi; // неправильно. В коробку для целых числе пытаемся положить 6.28 (2*3.14)
a = x; // неправильно. В целочисленную переменную пытаемся сохранить вещественное число

x = 2+a;	// ДОПУСТИМО!!! x будет равно 4.0

Внимание на последнюю строчку! Хотя справа выражение целого типа, а слева вещественная переменная, данная команда всё равно будет работать так, как надо. Это из-за того, что все целые числа можно записать как вещественные, у которых дробная часть равна нулю. Подобная штука называется неявное приведение (преобразование) типов. С этой возможностью надо быть очень осторожным, т.к. компилятор не отслеживает такие ошибки. Другими словами программа будет скомпилирована, но работать будет неправильно. Мы ещё вернёмся к этому в следующем уроке, когда научимся выводить значение переменных на экран компьютера.

II. Операции присваивания. Язык Си

II. Операции присваивания

=     Присваивает значение, указанное справа, переменной, стоящей слева

Каждая из приводимых ниже операции изменяет переменную, стоящую слева, на величину, находящуюся справа. Мы используем следующие обозначения: П для правой части и Л для левой. + = прибавляет величину П к переменной Л

-=вычитает величину П из переменной Л
*=умножает переменную Л на величину П
/=делит переменную Л на величину П
%=выдает остаток от деления переменной Л на величину П

Пример:

rabbits *= 1.6; то же самое, что rabbits = rabbits * 1.6;  

Поделитесь на страничке

Следующая глава >

Составное присваивание. Язык программирования Си для персонального компьютера

Составное присваивание

Операция составного присваивания состоит из простой операции присваивания, скомбинированной с какой-либо другой бинарной операцией. При составном присваивании вначале выполняется действие, специфицированное бинарной операцией, а затем результат присваивается левому операнду. Выражение составного присваивания со сложением, например имеет вид:

<выражение1> += <выражение2>

Оно может быть записано и таким образом:

<выражение1> = <выражение1> + <выражение2>

Значение операции вырабатывается по тем же правилам, что и для операции простого присваивания. Однако выражение составного присваивания не эквивалентно обычной записи, поскольку в выражении составного присваивания <выражение1> вычисляется только один раз, в то время как в обычной записи оно вычисляется дважды: в операции сложения и в операции присваивания. Например, оператор

*str1.str2.ptr += 5;

легче для понимания и выполняется быстрее, чем оператор

*str1.str2.ptr = *str1.str2.ptr + 5;

Использование составных операций присваивания может повысить эффективность программ. Каждая операция составного присваивания выполняет преобразования, которые определяются входящей в ее состав бинарной операцией, и соответственно ограничивает типы своих операндов. Результатом операции составного присваивания является значение, присвоенное левому операнду. Тип результата — тип левого операнда.

Пример:

n &= 0xFFFE;

В этом примере операция поразрядное И выполняется над n и шестнадцатеричным значением FFFE, и результат присваивается n.

Поделитесь на страничке

Следующая глава >

Оператор присваивания в C++ — это… Что такое Оператор присваивания в C++?



Оператор присваивания в C++

Операция присваивания в языке программирования C++ обозначается знаком ‘=’. Как и другие операции в C++, она может быть перегружена.

Операция присваивания копированием — особый вид операции присваивания, используемый для присваивания объектов одного класса друг другу. Является одним из особых членов-функций и генерируется автоматически компилятором в случае, если нет явного объявления программистом. Код, сгенерированный компилятором, выполняет поверхностное копирование.

Операция присваивания копированием отличается от конструктора копирования тем, что должен очищать члены-данные цели присваивания (и правильно обрабатывать самоприсваивание), тогда как конструктор копирования присваивает значения неинициализированным членам-данным.[1] Например:

My_Array first;           // инициализация конструктором по умолчанию
My_Array second = first;  // инициализация конструктором копирования
second = first;           // присваивание операцией присваивания копированием

Перегрузка операции присваивания копированием

Когда нужно сделать глубокие копии объектов необходимо также принять во внимание и обработку исключений. Одним из способов избежать ошибки перемещения ресурсов является следующий:

  1. Получаем новые ресурсы
  2. Освобождаем старые ресурсы
  3. Присваиваем объекту значения нового ресурса
class My_Array 
{
 
    int * array;
    int count;
 
public:
 
    My_Array & operator = (const My_Array & other)
    {
        if (this != &other) // защита от неправильного самоприсваивания
        {
            // 1: выделяем "новую" память и копируем элементы
            int * new_array = new int[other.count];
            std::copy(new_array, new_array + other.count, other.array);
 
            // 2: освобождаем "старую" память
            delete [] array;
 
            // 3: присваиваем значения в "новой" памяти объекту
            array = new_array;
            count = other.count;
        }
        // по соглашнию всегда возвращаем *this
        return *this;
    }
 
    ...
 
};

Тем не менее, если успешный метод обмена доступен для всех членов и класс реализует конструктор копирования и деструктор (согласно Правилу трех), самым коротким путем реализации присваивание копированием будет следующий способ[2]:

public:
 
    void swap(My_Array & other) // обмен члена-функции (неудачи быть не должно!)
    {
        // обмен всех членов (и базовых субобъектов, если возможно) с other
        std::swap(array, other.array);
        std::swap(count, other.count);
    }
 
    My_Array & operator = (My_Array other) // Примечание: аргумент передается по значению!
    {
        // обмен this с other
        swap(other);
 
        // по соглашению всегда возвращаем *this
        return *this;
 
        // other уничтожается, освобождая память
    }

Причина, по которой операция = возвращает My_Array& вместо void, проста. Он разрешен для объединения назначений, как например:

array_1 = array_2 = array_3; // значение array_3 присваивается array_2 
                             // затем значение array_2 присваивается array_1

Смотри также

Ссылки

  1. Bjarne Stroustrup The C++ Programming Language. — 3. — Addison-Wesley. — С. 244. — ISBN 978-0201700732
  2. Sutter, H. & Alexandrescu, A. (October 2004), C++ Coding Standards, Addison-Wesley, ISBN 0-321-11358-6 

Wikimedia Foundation.
2010.

  • Оперативный штаб ВМС США
  • Операторные алгебры

Смотреть что такое «Оператор присваивания в C++» в других словарях:

  • оператор присваивания — — [http://www.iks media.ru/glossary/index.html?glossid=2400324] Тематики электросвязь, основные понятия EN assignment statement …   Справочник технического переводчика

  • Оператор (программирование) — У этого термина существуют и другие значения, см. оператор. Из за путаницы с терминологией словом «оператор» в программировании нередко обозначают операцию (англ. operator), см. Операция (программирование). Инструкция или оператор… …   Википедия

  • Присваивание (программирование) — Содержание 1 Определение присваивания 1.1 Алгоритм работы оператора присваивания …   Википедия

  • Присвоение (программирование) — Содержание 1 Определение присваивания 1.1 Алгоритм работы оператора присваивания …   Википедия

  • Присваивание — Присваивание  механизм в программировании, позволяющий динамически изменять связи объектов данных (как правило, переменных) с их значениями. Строго говоря, изменение значений является побочным эффектом операции присвоения, и во многих… …   Википедия

  • С++ — См. также: Си (язык программирования) C++ Семантика: мультипарадигмальный: объектно ориентированное, обобщённое, процедурное, метапрограммирование Тип исполнения: компилируемый Появился в: 1985 г. Автор(ы): Бьёрн Страуструп …   Википедия

  • C++ — У этого термина существуют и другие значения, см. C. См. также: Си (язык программирования) C++ Семантика: мультипарадигмальный: объектно ориентированное, обобщённое, процедурное, метапрограммирование Тип исполнения: компилируемый Появился в …   Википедия

  • Конструктор копирования — Конструктором копирования (в англоязычной литературе используется термин copy constructor) называется специальный конструктор в языке программирования C++, применяемый для создания нового объекта как копии уже существующего. Такой конструктор… …   Википедия

  • Алгебра Кодда — Содержание 1 Реляционные операторы 1.1 Совместимость отношений …   Википедия

  • Идиома copy-and-swap — Идиома copy and swap  это идиома языка программирования C++, позволяющая разрабатывать устойчивые к исключениям операторы присваивания. Идиома базируется на идиоме Resource Acquisition Is Initialization. Идиома предполагает реализацию… …   Википедия

9.14 — Перегрузка оператора присваивания

Оператор присваивания (operator =) используется для копирования значений из одного объекта в другой уже существующий объект .

Конструктор присваивания и копирования

Назначение конструктора копирования и оператора присваивания почти эквивалентны — оба копируют один объект в другой. Однако конструктор копирования инициализирует новые объекты, тогда как оператор присваивания заменяет содержимое существующих объектов.

Разница между конструктором копирования и оператором присваивания вызывает много путаницы у начинающих программистов, но на самом деле это не так уж и сложно. Суммируя:

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

Перегрузка оператора присваивания

Перегрузка оператора присваивания (operator =) довольно проста, с одной конкретной оговоркой, которую мы рассмотрим.Оператор присваивания должен быть перегружен как функция-член.

1

2

3

4

5

6

7

8

9

10

11

12

13

140002

14

18

19

20

21

22

23

24

25

26

27

28

29

30

000

34

35

36

37

38

39

40

41

42

43

44

45

46

48

47

51

52

53

54

55

56

57

58

#include

#include

class Fraction

{

private:

int m_numerator;

int m_denominator;

public:

// Конструктор по умолчанию

Fraction (int numerator = 0, int denominator = 1):

m_numerator (числитель), m_denominator (знаменатель)

{

assert (знаменатель! = 0) ;

}

// Конструктор копирования

Fraction (const Fraction & copy):

m_numerator (copy.m_numerator), m_denominator (copy.m_denominator)

{

// здесь нет необходимости проверять знаменатель 0, поскольку копия уже должна быть допустимой Fraction

std :: cout << "Вызывается конструктор копирования \ n"; // просто чтобы доказать, что это работает

}

// Перегруженное присвоение

Fraction & operator = (const Fraction & Fraction);

друг std :: ostream & operator << (std :: ostream & out, const Fraction & f1);

};

std :: ostream & operator << (std :: ostream & out, const Fraction & f1)

{

out << f1.m_numerator << "/" << f1.m_denominator;

возврат;

}

// Упрощенная реализация operator = (см. Лучшую реализацию ниже)

Fraction & Fraction :: operator = (const Fraction & Fraction)

{

// копирование

m_numerator = дробь. m_numerator;

m_denominator = дробь.m_denominator;

// вернуть существующий объект, чтобы мы могли связать этот оператор

return * this;

}

int main ()

{

Дробь FiveThirds (5, 3);

Дробь f;

f = FiveThirds; // вызывает перегруженное присвоение

std :: cout << f;

возврат 0;

}

Это отпечатки:

5/3
 

Теперь все должно быть довольно просто.Наш перегруженный operator = возвращает * this, чтобы мы могли связать несколько назначений вместе:

int main ()

{

Дробь f1 (5,3);

Дробь f2 (7,2);

Дробь f3 (9,5);

f1 = f2 = f3; // связанное присвоение

return 0;

}

Проблемы из-за самостоятельного назначения

Здесь все становится немного интереснее.C ++ допускает самостоятельное назначение:

int main ()

{

Дробь f1 (5,3);

f1 = f1; // самостоятельное присвоение

return 0;

}

Это вызовет f1.operator = (f1), и в упрощенной реализации, приведенной выше, все члены будут назначены себе. В этом конкретном примере самоназначение приводит к тому, что каждый член назначается самому себе, что не имеет общего воздействия, кроме потери времени.В большинстве случаев самостоятельное задание вообще не нужно делать!

Однако в случаях, когда оператору присваивания необходимо динамически назначать память, самоназначение может быть опасным:

1

2

3

4

5

6

7

8

9

10

11

12

13

140002

14

18

19

20

21

22

23

24

25

26

27

28

29

30

000

34

35

36

37

38

39

40

41

42

43

44

45

46

48

47

51

52

53

54

55

56

57

58

59

60

#include

class MyString

{

private:

char * m_data;

int m_length;

public:

MyString (const char * data = «», int length = 0):

m_length (length)

{

if (! Length)

m_data = nullptr;

else

m_data = новый символ [длина];

для (int i = 0; i

m_data [i] = data [i];

}

// Перегруженное присвоение

MyString & operator = (const MyString & str);

друг std :: ostream & оператор << (std :: ostream & out, const MyString & s);

};

std :: ostream & operator << (std :: ostream & out, const MyString & s)

{

out << s.m_data;

возврат;

}

// Упрощенная реализация operator = (не использовать)

MyString & MyString :: operator = (const MyString & str)

{

// если данные существуют в текущей строке, удалите их

если (m_data) удалить [] m_data;

m_length = str.m_length;

// копируем данные из str в неявный объект

m_data = new char [str.m_length];

для (int i = 0; i

m_data [i] = str.m_data [i];

// вернуть существующий объект, чтобы мы могли связать этот оператор

return * this;

}

int main ()

{

MyString alex («Alex», 5); // Знакомство с Алексом

MyString сотрудником;

сотрудник = Алексей; // Алекс — наш новый сотрудник

std :: cout << employee; // Назовите свое имя, сотрудник

return 0;

}

Сначала запустите программу как есть.Вы увидите, что программа печатает «Alex» как следует.

Теперь запустите следующую программу:

int main ()

{

MyString alex («Alex», 5); // Знакомство с Алексом

alex = alex; // Алекс сам

std :: cout << alex; // Произнесите свое имя, Алекс

return 0;

}

Вы, вероятно, получите мусор.Что случилось?

Рассмотрим, что происходит в перегруженном операторе =, когда неявный объект И переданный параметр (str) являются переменными alex. В этом случае m_data совпадает с str.m_data. Первое, что происходит, это то, что функция проверяет, есть ли у неявного объекта уже строка. Если это так, его необходимо удалить, чтобы не произошло утечки памяти. В этом случае выделяется m_data, поэтому функция удаляет m_data. Но поскольку str совпадает с * this, строка, которую мы хотели скопировать, была удалена, а m_data (и str.m_data) болтаются.

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

Обнаружение и обработка самостоятельного назначения

К счастью, мы можем определить, когда происходит самостоятельное присвоение. Вот обновленная реализация нашего перегруженного оператора = для класса MyString:

1

2

3

4

5

6

7

8

9

10

11

12

13

140002

14

18

19

20

21

// Упрощенная реализация operator = (не использовать)

MyString & MyString :: operator = (const MyString & str)

{

// проверка самоназначения

if (this == & str)

вернуть * это;

// если данные существуют в текущей строке, удалите их

if (m_data) delete [] m_data;

м_длина = ул.m_length;

// копируем данные из str в неявный объект

m_data = new char [str.m_length];

для (int i = 0; i

m_data [i] = str.m_data [i];

// вернуть существующий объект, чтобы мы могли связать этот оператор

return * this;

}

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

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

Когда не выполнять самостоятельное назначение

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

Во-вторых, проверка самоназначения может быть опущена в классах, которые естественным образом могут обрабатывать самоназначение.Рассмотрим этот оператор присваивания класса Fraction, который имеет защиту самоназначения:

// Лучшая реализация operator =

Fraction & Fraction :: operator = (const Fraction & Fraction)

{

// Защита самоназначения

if (this == & Fraction)

return * this;

// делаем копию

m_numerator = дробь.m_numerator; // может обрабатывать самостоятельное присвоение

m_denominator = дробь.m_denominator; // может обрабатывать самостоятельное присвоение

// вернуть существующий объект, чтобы мы могли связать этот оператор

return * this;

}

Если бы защиты самоназначения не существовало, эта функция все равно работала бы правильно во время самоназначения (поскольку все операции, выполняемые функцией, могут правильно обрабатывать самоназначение).

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

Оператор присвоения по умолчанию

В отличие от других операторов, компилятор предоставит общедоступный оператор присваивания по умолчанию для вашего класса, если вы его не предоставите. Этот оператор присваивания выполняет поэлементное присваивание (что по сути то же самое, что и поэлементная инициализация, которую выполняют конструкторы копирования по умолчанию).

Как и другие конструкторы и операторы, вы можете предотвратить выполнение назначений, сделав свой оператор назначения закрытым или используя ключевое слово delete:

1

2

3

4

5

6

7

8

9

10

11

12

13

140002

14

18

19

20

21

22

23

24

25

26

27

28

29

30

000

34

35

36

37

38

39

40

41

42

#include

#include

class Fraction

{

private:

int m_numerator;

int m_denominator;

public:

// Конструктор по умолчанию

Fraction (int numerator = 0, int denominator = 1):

m_numerator (числитель), m_denominator (знаменатель)

{

assert (знаменатель! = 0) ;

}

// Конструктор копирования

Fraction (const Fraction & copy) = delete;

// Перегруженное присвоение

Fraction & operator = (const Fraction & Fraction) = delete; // никаких копий через присваивание!

друг std :: ostream & operator << (std :: ostream & out, const Fraction & f1);

};

std :: ostream & operator << (std :: ostream & out, const Fraction & f1)

{

out << f1.m_numerator << "/" << f1.m_denominator;

возврат;

}

int main ()

{

Дробь FiveThirds (5, 3);

Дробь f;

f = FiveThirds; // ошибка компиляции, оператор = был удален

std :: cout << f;

возврат 0;

}

.

Перегрузка оператора присваивания в C ++

Переполнение стека

  1. Около
  2. Продукты

  3. Для команд
  1. Переполнение стека
    Общественные вопросы и ответы

  2. Переполнение стека для команд
    Где разработчики и технологи делятся частными знаниями с коллегами

  3. Вакансии
    Программирование и связанные с ним технические возможности карьерного роста

  4. Талант
    Нанимайте технических специалистов и создавайте свой бренд работодателя

  5. Реклама
    Обратитесь к разработчикам и технологам со всего мира

  6. О компании

.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *