чем отличается присваивание от инициализации?
Инициализация происходит один раз при вызове конструктора, присвоение — каждый раз при вызове оператора = .
На строке sample ob = input(); происходит инициализация (с использованием нежелательного синтаксиса copy initialization). Вместо этого во всех случаях следует использовать синтаксис direct list initialization sample ob; .
На строке ob = input(); происходит присваивание. Оно заканчивается плохо, так как оператор = для класса не переопределен надлежащим образом для корректного обращения с динамически выделенной памятью. Для любых не POD классов оператор = следует либо определять, либо запрещать. см правило 0/3/5
Функция присваивания подразумевает аккуратно избавляться от текущего состояния объекта и присваивания нового.
Так как вы сами написали конструктор копирования, то оператор присваивания по-умолчанию не будет реалезован и вы должны сами его написать.
А-ля :
плохую функцию set можете удалить.
![]()
Дизайн сайта / логотип © 2023 Stack Exchange Inc; пользовательские материалы лицензированы в соответствии с CC BY-SA . rev 2023.3.13.43310
Нажимая «Принять все файлы cookie» вы соглашаетесь, что Stack Exchange может хранить файлы cookie на вашем устройстве и раскрывать информацию в соответствии с нашей Политикой в отношении файлов cookie.
1.4 – Присваивание и инициализация переменных
В предыдущем уроке «1.3 – Знакомство с переменными в C++» мы рассмотрели, как определить переменную, которую мы сможем использовать для хранения значений. В этом уроке мы узнаем, как помещать значения в переменные, и как использовать эти значения.
Напоминаем, что следующий короткий фрагмент сначала выделяет память для одной целочисленной переменной с именем x , а затем выделяет память еще для двух целочисленных переменных с именами y и z :
Присваивание значения переменной
После того, как переменная была определена, вы можете присвоить ей значение (в отдельной инструкции) с помощью оператора = . Этот процесс для краткости называется копирующим присваиванием (или просто присваиванием).
Копирующее присваивание названо так, потому что оно копирует значение с правой стороны оператора = в переменную с левой стороны оператора. Оператор = называется оператором присваивания.
Вот пример, где мы используем присваивание дважды:
Когда мы присваиваем значение 7 переменной width , значение 5, которое было там ранее, перезаписывается. Обычные переменные могут содержать только одно значение за раз.
Предупреждение
Одна из наиболее распространенных ошибок, которые делают новички, – это путать оператор присваивания ( = ) с оператором равенства ( == ). Присваивание ( = ) используется для присвоения значения переменной. Равенство ( == ) используется для проверки того, равны ли два операнда по значению.
Копирующая и прямая инициализации
Одним из недостатков присваивания является то, что для него требуются как минимум две инструкции: одна для определения переменной, а другая для присвоения значения.
Эти два шага можно совместить. При определении переменной вы также можете одновременно указать для нее начальное значение. Это называется инициализацией.
C++ поддерживает три основных способа инициализации переменных. Во-первых, мы можем выполнить копирующую инициализацию, используя знак равенства:
Подобно копирующему присваиванию, этот код копирует значение с правой стороны знака равно в переменную, создаваемую с левой стороны.
Во-вторых, мы можем выполнить прямую инициализацию с помощью скобок.
Для простых типов данных (например, целых чисел) копирующая и прямая инициализации, по сути, одинаковы. Различия между копирующей инициализацией и прямой инициализацией мы увидим в этой серии статей позже.
В-третьих, инициализация списком, которую мы рассмотрим в следующем разделе.
Инициализации списком
К сожалению, прямая инициализация с круглыми скобками не может использоваться для всех типов инициализации (таких как инициализация объекта списком данных). Чтобы обеспечить более согласованный механизм инициализации, существует инициализация списком (также иногда называемая унифицированной инициализацией или инициализацией с фигурными скобками), в которой используются фигурные скобки.
Инициализация списком бывает двух форм:
Эти две формы функционируют почти одинаково, но обычно предпочтительнее прямая форма.
Инициализация переменной с пустыми фигурными скобками указывает на инициализацию значения. В большинстве случаев инициализация значения инициализирует переменную нулевым значением (или пустым, если это более подходит для данного типа). В таких случаях, когда происходит обнуление, инициализация значения также называется нулевой инициализацией.
Инициализация списком имеет дополнительное преимущество, так как запрещает «сужающие» преобразования. Это означает, что если вы попытаетесь использовать инициализацию списком для инициализации переменной значением, которое она не может безопасно удерживать, компилятор выдаст предупреждение или ошибку. Например:
В приведенном выше фрагменте мы пытаемся присвоить число ( 4.5 ), имеющее дробную часть (часть .5 ), целочисленной переменной (которая может содержать только числа без дробных частей). При копирующей и прямой инициализациях дробная часть будет отброшена, что приведет к инициализации переменной width значением 4. Однако при инициализации списком это приведет к тому, что компилятор выдаст ошибку (что, как правило, хорошо потому, что потеря данных редко бывает желательной). Преобразования, которые могут быть выполнены без потенциальной потери данных, в этом случае разрешены.
Лучшая практика
По возможности отдавайте предпочтение прямой инициализации списком.
Вопрос: C++ обеспечивает копирующую, прямую инициализации и инициализацию списком, а также копирующее присваивание. Существует ли прямое присваивание или присваивание списком?
Нет, C++ не поддерживает синтаксис прямого присваивания или присваивания списком.
Вопрос: Когда следует инициализировать с помощью <0>, а когда с помощью <> ?
Используйте явную инициализацию значением, если вы действительно используете это значение.
Используйте инициализацию значения, если значение временное и будет заменено.
Инициализируйте свои переменные
Инициализируйте свои переменные при создании. В конечном итоге вам могут встретиться случаи, когда вы захотите проигнорировать этот совет по определенной причине (например, критический для производительности фрагмент кода, который использует множество переменных), и это нормально, если выбор сделан сознательно.
Для более подробного обсуждения этой темы Бьёрн Страуструп (создатель C++) и Герб Саттер (эксперт по C ++) сами дают рекомендацию здесь.
Мы исследуем, что произойдет, если вы попытаетесь использовать переменную, для которой нет четко определенного значения, в уроке «1.6 – Неинициализированные переменные и неопределенное поведение».
Лучшая практика
Инициализируйте свои переменные при создании.
Инициализация нескольких переменных
В последнем разделе мы отметили, что можно определить несколько переменных одного типа в одной инструкции, разделив имена запятыми:
Мы также отметили, что лучше всего вообще избегать этого синтаксиса. Однако, поскольку вы можете столкнуться с чужим кодом, использующим этот стиль, всё же полезно поговорить об этом немного подробнее хотя бы, чтобы акцентировать внимание на некоторых причинам, по которым вам следует избегать его.
Вы можете инициализировать несколько переменных, определенных в одной строке:
К сожалению, здесь есть распространенная ошибка, которая может возникнуть, когда программист по ошибке пытается инициализировать обе переменные с помощью одной инструкции инициализации:
В верхней инструкции переменная a будет оставлена неинициализированной, и компилятор может пожаловаться, а может и не пожаловаться. Если он не пожалуется, это отличный способ для вашей программы периодически давать сбои и выдавать случайные результаты. В ближайшее время мы подробнее поговорим о том, что произойдет, если вы будете использовать неинициализированные переменные.
Лучший способ запомнить, что это неправильно, – рассмотреть случай прямой инициализации или инициализации списком:
Этот код делает более ясным, что значение 5 используется только для инициализации переменной b или d , а не a или c .
Небольшой тест
Вопрос 1
В чем разница между инициализацией и присваиванием?
Инициализация дает переменной начальное значение в момент ее создания. Присваивание дает переменной значение в какой-то момент после создания.
C++/Operations
Операции в C++ — всевозможные действия, что допустимы в языке программирования.
Содержание
Объявление переменных
Объявить целочисленную переменную:
где int — тип, а nVar — имя переменной.
Принцип объявления переменных разных типов аналогичен:
где type — тип (напр., int ), а varNm — имя переменной.
Объявление пяти переменных разных типов:
Переменной типа void быть не может:
не будет работать, так как void не может использоваться в качестве типа переменной.
Инициализация переменных
При объявлении переменной можно сразу присвоить ей значение. Это называется инициализацией переменной. Важно понимать разницу, это не присваивание переменной значения, а именно инициализация переменной значением.
Язык C++ поддерживает 2 основных способа инициализации переменных.
- Копирующая инициализация (или «инициализация копированием») с помощью знака равенства = :
- Прямая инициализация с помощью круглых скобок () :
Поскольку оператор «круглые скобки» ( () ) используется ещё и для вызова функции, поэтому с помощью них так можно задавать переменные только при их одновременном объявлении/декларировании и инициализации/определении (переменной), т.е. инициализировать их, иначе будет вызываться функция.
Прямая инициализация лучше работает с одними типами данных, копирующая инициализация — с другими.
uniform-инициализация переменных
Прямая или копирующая инициализация работают не со всеми типами данных (напр., нельзя использовать эти способы для инициализации списка значений).
В попытке обеспечить единый механизм инициализации, который будет работать со всеми типами данных, в C++11 добавили новый способ инициализации, который называется uniform-инициализация:
Инициализация переменной с пустыми фигурными скобками указывает на инициализацию по-умолчанию (переменной присваивается 0):
В uniform-инициализации есть еще одно дополнительное преимущество: нельзя присвоить переменной значение, которое не поддерживает её тип данных — компилятор выдаст предупреждение или сообщение об ошибке. Например:
Присваивание значений переменным
Когда переменной задаётся значение после её объявления (не в момент объявления), то это не инициализация, а присваивание (оно же «копирующее присваивание»). Присваивание переменной значения:
Инициализация и присваивание это разные операции, несмотря на то, что обозначаются одним знаком ( = ). Присваивание уничтожает предыдущее значение, а инициализация ничего не уничтожает.
В языке C++ нет встроенной поддержки способов прямого/uniform-присваивания, есть только копирующее присваивание.
Объявление и инициализация нескольких переменных
Синтаксис C++ допускает указание переменных через запятую. Т.е. в одном стейтменте можно по очереди объявить и инициализировать сразу несколько переменных одного и того же типа данных, разделяя их имена запятыми. В заголовке функции так делать нельзя.
Объявление нескольких переменных
Можно один раз указать тип, а имена переменных перечислить через запятую. Например, следующие 2 фрагмента кода выполняют одно и то же:
Инициализация нескольких переменных
Можно инициализировать несколько переменных в одном стейтменте:
В C++11 добавили uniform-инициализацию и для расширенного списка инициализаторов (extended initializer list):
Определять каждую переменную в очереди не обязательно, можно и вполне достаточно просто объявить её и всё:
Сразу тут же можно инициализировать одну переменную значением другой, которая уже была инициализирована:
Можно чередовать и комбинировать способы инициализации, и инициализировать одну переменную значением другой, которая была инициализирована другим способом:
Ошибки
При объявлении нескольких переменных по очереди в одном стейтменте часто совершают 3 ошибки:
- Указание каждой переменной одного и того же типа данных при инициализации нескольких переменных в одном стейтменте. Это не критичная ошибка, так как компилятор легко её обнаружит и сообщит об этом:
- Использование разных типов данных в одном стейтменте. Переменные разных типов должны быть объявлены в разных стейтментах. Эту ошибку компилятор также легко обнаружит:
- Инициализация двух переменных с помощью одной операции:
Хороший способ запомнить эту ошибку и не допускать в будущем — использовать прямую или uniform-инициализацию:
Этот вариант наглядно показывает, что значение 5 присваивается только переменным b и d.
Так как инициализация нескольких переменных в одной строке является отличным поводом для совершения ошибок, рекомендуется определять несколько переменных в одной строке только в том случае, если будете каждую из них инициализировать.
Избегайте объявления нескольких переменных в одной строке, если не собираетесь инициализировать каждую из них.
Модель ячейки в Excel
Ячейка Excel на экране может содержать в себе следующие информацию:
Формат ячейки (формат данных + графическое оформление ячейки)
Правила проверки данных (data validation).
Некоторые из вышеперечисленных элементов могут быть пустыми.
Имя ячейки
Как уже говорилось выше, имя ячейки состоит из буквы (или 2 букв) и числа, например, A5, BZ112 и т.д.
При необходимости задать имя ячейки на другом листе это делается следующим образом: Лист1!A1, где Лист1 – имя листа, A1 – имя ячейки на этом листе.
В некоторых формулах иногда бывает необходимо задать диапазон ячеек. Это можно сделать простым перечислением ячеек через точку с запятой (A1;A3;A4), либо указав 2 угла прямоугольного диапазона, разделенного двоеточием (например, в диапазон A1:B3 входят ячейки A1,A2,A3,B1,B2,B3). Эти 2 метода можно комбинировать.
Относительные и абсолютные имена(адреса) ячеек
При копировании или переносе формул из ячейки в ячейку автоматически изменяются адреса влияющих ячеек в формулах. В большинстве случаев это очень удобно, но иногда этого не требуется. Например, требуется разделить содержимое ячеек А1 и А2 на содержимое ячейки А3. Запишем формулу в ячейку В1.
1 10 =A1/A3 1 10 =A1/$A$3
2 20 =A2 /A4 2 20 =A2/$A$3
Если ячейку В1 скопировать в ячейку В2 , то получим формулу А2/А4, т.к. происходит автоматическая замена адресов. Но это не то, что нужно.
Записав абсолютный адрес ячейки А3, мы имеем возможность копировать ячейку В1 сколько угодно раз, адрес ячейки А3 не изменится. Абсолютный адрес записывается с применением символа “$”, который ставится перед каждой «фиксируемой» частью имени. Например: $A1, A$1, $A$1.
Инициализация переменных. Отличие инициализации от присваивания
Инициализация — это присваивание переменной (объекту) начального значения.
В яз. СИ большинство объектов может быть явно или неявно инициализировано в момент их объявления с помощью указания после их имени знака равенства и константного выражения.
Глобальные (внешние) и статические объекты (extern и static) инициализируются однократно в момент компиляции значением константного выражения. Автоматические (auto) и регистровые (register) объекты инициализируются значением любого выражения в процессе исполнения программы при каждом входе в блок, в котором они определены.
Другими словами, объекты инициализируются сразу же после выделения под них памяти.
Явная инициализация производится оператором присвоения, который записывает значение выражения во вновь созданную переменную.
static int counter=0;
char bell=’\007′; // Записываем ОДИН символ с кодом 7
static float pi=n/den; // Недопустимо – это не константное
float pi2=n/den; // Допустимо, т.к. класс памяти – auto
Следующие правила описывают процесс инициализации:
Глобальные и статические переменные всегда инициализируются, и если это не сделано явно, то они инициализируются нулевым значением. Инициализация для них выполняется один раз перед началом программы. Если переменная целая, то ее начальное значение=0, если символьная, то «\0», если это число с плавающей точкой, то 0.0. Эти объекты могут быть явно инициированы только значением константного выражения.
В объявлениях с классом памяти extern не допускается явная инициализация, так как эти объявления ссылаются на уже существующие и определенные ранее переменные.
Переменная, на которую делается ссылка с помощью спецификатора extern, может быть определена только один раз в одном из исходных файлов программы. В этом же месте происходит её инициализация
Инициализация переменных с классом памяти auto или register выполняется всякий раз при входе в блок, в котором они объявлены. Переменные с этими классами памяти автоматически не инициализируется. Они могут быть проинициализированы явно значением любого выражения при объявлении.
Если явная инициализация переменных в объявлении отсутствует, то их начальное значение не определено. Оно будет получено из информации, которая прежде содержалась и продолжает содержаться в ячейках памяти, отведённых под переменную.
Указатели инициализируются аналогично остальным переменным. При этом возможно использование оператора взятия адреса & для присвоения указателю адреса какой-либо переменной.
При этом следует помнить, что адрес статической или глобальной переменной является константным значением, и может быть записан при инициализации в любой указатель.
static *ps=&i; // верно
Адрес же переменной с классом памяти auto таковым не является, и может быть записан при инициализации только в указатель с классом памяти auto.
static *ps=&i; //неверно
К переменным с классом памяти register операция взятия адреса & неприменима.