C конструктор инициализация структуры

Обновлено: 28.02.2024

Тип структуры представляет собой тип значения, который может инкапсулировать данные и связанные функции. Для определения типа структуры используется ключевое слово struct :

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

Поскольку типы структуры имеют семантику значений, рекомендуется определять неизменяемые типы структуры.

Структура readonly

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

Члены экземпляров readonly

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

Как правило, модификатор readonly применяется к следующим типам элементов экземпляров.

Можно также применить модификатор readonly к методам, переопределяющим методы, объявленные в System.Object.

Свойства и индексаторы.

Если необходимо применить модификатор readonly к методам доступа свойства или индексатора, примените его в объявлении свойства или индексатора.

Компилятор объявляет метод доступа get автоматически реализуемого свойства как readonly независимо от наличия модификатора readonly в объявлении свойства.

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

Обратимое изменение

Структура record

Инициализация структуры и значения по умолчанию

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

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

Наиболее распространенная ситуация, когда значения по умолчанию отображаются в массивах или в других коллекциях, где внутреннее хранилище содержит блоки переменных. В следующем примере создается массив из 30 TemperatureRange структур, каждый из которых имеет значение по умолчанию:

Все поля элементов структуры должны быть определенно назначены при его создании, так как struct типы хранят их данные напрямую. default Значение структуры определенно присвоило всем полям значение 0. Все поля должны быть определенно назначены при вызове конструктора. Вы инициализируете поля с помощью следующих механизмов:

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

Каждый struct имеет public конструктор без параметров. Если вы пишете конструктор без параметров, он должен быть открытым. Если вы не напишете открытый конструктор без параметров, компилятор создаст его. Созданный компилятор конструктор без параметров выполняет все инициализации поля и создает значение по умолчанию для всех остальных полей. При объявлении инициализаторов полей необходимо объявить один явный конструктор. Один явный конструктор может быть конструктором без параметров. Он может иметь пустое тело. Дополнительные сведения см. в примечании к предложению новой возможности в разделе Конструкторы структур без параметров.

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

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

Ограничения при проектировании типа структуры

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

Передача переменных типа структуры по ссылке

Структура ref

  • Структура ref не может быть типом элемента массива.
  • Структура ref не может быть объявленным типом поля класса или структурой, отличной от ref .
  • Структура ref не может реализовывать интерфейсы.
  • Структура ref не может быть упакована в System.ValueType или System.Object.
  • Структура ref не может быть аргументом типа.
  • Переменная структуры ref не может быть зафиксирована лямбда-выражением или локальной функцией.
  • Переменную структуры ref нельзя использовать в методе async . Однако переменные структуры можно использовать ref в синхронных методах, например в методах, возвращающих Task или Task .
  • Переменную структуры ref нельзя использовать в итераторах.

Как правило, тип структуры ref определяется, если требуется тип, который также содержит члены данных типов структуры ref :

Чтобы объявить структуру ref как readonly , объедините модификаторы readonly и ref в объявлении типа (модификатор readonly должен предшествовать модификатору ref ):

Ограничение struct

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

Преобразования

Для любого типа структуры (за исключением типов структуры ref ) существует упаковка-преобразование и распаковка-преобразование в типы System.ValueType и System.Object и из них. Существуют упаковка-преобразование и распаковка-преобразование между типом структуры и любым интерфейсом, который он реализует.

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

Для определения структуры применяется ключевое слово struct , а сам формат определения выглядит следующим образом:

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

После имени структуры в фигурных скобках помещаются Компоненты_структуры , которые представляют набор описаний объектов, которые составляют структуру.

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

Например, определим простейшую структуру:

Здесь определена структура person , которая имеет два элемента: age (представляет тип int) и name (представляет указатель на тип char).

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

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

Здесь определена переменная tom, которая представляет структуру person. И при каждом определении переменной типа структуры ей будет выделяться память, необходимая для хранения ее элементов.

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

Инициализация структур аналогична инициализации массивов: в фигурных скобках передаются значения для элементов структуры по порядку. Так как в структуре person первым определено свойство, которое представляет тип int - число, то в фигурных скобках вначале идет число. И так далее для всех элементов структуры по порядку.

Также после создания переменной структуры можно обращаться к ее элементам - получать их значения или, наоборот, присваивать им новые значения. Для обращения к элементам структуры используется операция "точка":

Теперь объединим все вместе в рамках программы:

Консольный вывод программы:

С элементами структуры можно производить все те же операции, что и с переменными тех же типов. Например, добавим ввод с консоли:

Консольный вывод программы:

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

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

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

В этом случае компилятор все равно будет знать, что переменные tom, bob и alice представляют структуры с двумя элементами name и age. И соответственно мы также с этими переменными сможем работать. Другое дело, что мы не сможем задать новые переменные этой структуры в других местах программы.

typedef

Еще один способ определения структуры представляет ключевое слово typedef :

В конце определения структуры после закрывающей фигурной скобки идет ее обозначение - в данном случае person . В дальнейшем мы можем использовать это обозначение для создания переменной структуры. При этом в отличие от примеров выше здесь не надо при определении переменной не надо использовать слово struct .

Директива define

В данном случае директива define определяет константу PERSON, вместо которой при обработке исходного кода препроцессором будет вставляться код структуры struct

В прошлой теме был разработан следующий класс:

И мы можем установить значения для переменных класса Person, можем получить их значения во внешние переменные. Однако если мы попробуем получить значения переменных name и age до их установки, то результаты будут неопределенными:

Чтобы избежать подобной ситуации применяются специальные функции инициализации или конструкторы. Они позволяют инициализировать объект класса. Так, изменим код программы следующим образом:

Теперь в классе Person определен конструктор:

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

Если в классе определены конструкторы, то при создании объекта этого класса необходимо вызвать один из его конструкторов.

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

После этого вызова у объекта person для поля name будет определено значение "Tom", а для поля age - значение 22. Вполедствии мы также сможем обращаться к этим полям и переустанавливать их значения.

Тажке можно использовать сокращенную форму инициализации:

По сути она будет эквивалетна предыдущей.

Консольный вывод определенной выше программы:

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

В классе Person определено три конструктора, и в функции все эти конструкторы используются для создания объектов:

Хотя пример выше прекрасно работает, однако мы можем заметить, что все три конструктора выполняют фактически одни и те же действия - устанавливают значения переменных name и age. И в C++ можем сократить их определения, вызова из одного конструктора другой и тем самым уменьшить объем кода:

Запись Person(string n): Person(n, 18) представляет вызов конструктора, которому передается значение параметра n и число 18. То есть второй конструктор делегирует действия по инициализации переменных первому конструктору. При этом второй конструктор может дополнительно определять какие-то свои действия.

Таким образом, следующее создание объекта

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

Инициализация констант и ссылок

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

Этот класс не будет компилироваться, так как здесь есть две ошибки - отсутствие инициализации константы name и ссылки ageRef. Хотяя их значения устанавливаются в конструкторе, но к моменту, когда код инструкции из тела конструктора начнут выполняться, константы и ссылки уже должны быть инициализированы. И для этого необходимо использовать списки инициализации:

Списки инициализации представляют перечисления инициализаторов для каждой из переменных и констант через двоеточие после списка параметров конструктора:

Таким образом, все переменные, константы и ссылки получат значение, и никакой ошибки не возникнет.

Определение структуры

Для определения структуры применяется ключевое слово struct :

После слова struct идет название структуры и далее в фигурных скобках размещаются элементы структуры - поля, методы и т.д.

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

Как и классы, структуры могут хранить состояние в виде полей (переменных) и определять поведение в виде методов. Например, добавим в структуру Person пару полей и метод:

В данном случае определены две переменные - name и age для хранения соответственно имени и возраста человека и метод Print для вывода информации о человеке на консоль.

И как и в случае с классами, для обращения к функциональности структуры - полям, методам и другим компонентам структуры применяется точечная нотация - после объекта структуры ставится точка, а затем указывается компонент структуры:

Создание объекта структуры

Инициализация с помощью конструктора

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

Например, создадим объект структуры Person с помощью конструктора по умолчанию:

В данном случае создается объект tom. Для его создания вызывается конструктор по умолчанию, который устанавливает значения по умолчанию для его полей. Для числовых данных это значение 0, поэтому поле age будет иметь значение 0. Для строк это значение null , которое указывает на отсутствие значения. Но далее, если поля доступны (а в данном случае поскольку они имеют модификатор public они доступны), мы можем измениь их значения. Так, здесь полю name присваивается строка "Tom". Соответственно при выполнении метода Print() мы получим следующий консольный вывод:

Непосредственная иницилизация полей

Если все поля структуры доступны (как в случае с полями структуры Person, который имеют модификатор public ), то структуру можно инициализировать без вызова конструктора. В этом случае необходимо присвоить значения всем полям структуры перед получением значений полей и обращением к методам структуры. Например:

Инициализация полей по умолчанию

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

Конструкторы структуры

Как и класс, структура может определять конструкторы. Однако, если в структуре определяется конструктор, то в нем обязательно надо инициализировать все поля структуры.

Например, добавим в структуру Person конструктор:

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

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

Опять же при определении конструктора без параметров необходимо инициализировать все поля структуры.

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

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

Инициализатор структуры

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

При использовании инициализатора сначала вызывается конструктор без параметров: если мы явным образом не определили конструктор без параметров, то вызывается конструктор по умолчанию. А затем его полям присваиваются соответствующие значения.

Копирование структуры с помощью with

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

В данном случае объект bob получает все значения объекта tom, а затем после оператора with в фигурных скобках указывается поля со значениями, которые мы хотим изменить.

Инициализатор определяет начальное значение переменной. Можно инициализировать переменные в этих контекстах:

В определении переменной:

В качестве одного из параметров функции:

В качестве возвращаемого типа функции:

Инициализаторы могут принимать эти формы:

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

Знак равенства с последующим выражением:

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

Типы инициализации

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

Нулевая инициализация

Нулевая инициализация — задание для переменной нулевого значения, неявно преобразованного в тип:

Числовые переменные инициализируются значением 0 (или 0,0; 0,0000000000 и т.п.).

Переменные char инициализируются в '\0' .

Указатели инициализируются в nullptr .

Массивы, классы POD , структуры и объединения инициализируют свои члены равным нулю.

Нулевая инициализация выполняется в разное время:

При запуске программы — для всех именованных переменных, имеющих статическую длительность. Далее эти переменные могут быть инициализированы повторно.

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

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

Ниже приведены некоторые примеры нулевой инициализации:

Инициализация по умолчанию

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

Если класс, структура или объединение не имеет конструктор по умолчанию, компилятор выдает ошибку.

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

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

Если члены массива не имеют конструктор по умолчанию, компилятор выдает ошибку.

Инициализация по умолчанию константных переменных

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

Инициализация по умолчанию статических переменных

Статические переменные, объявленные без инициализатора, инициализируются значением 0 (с неявным преобразованием к соответствующему типу).

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

Инициализация значения

Инициализация значения происходит в следующих случаях:

Именованное значение инициализируется с использованием пустых фигурных скобок.

Анонимный временный объект инициализируется с помощью пустых круглых или фигурных скобок.

Объект инициализируется ключевым словом new и пустыми скобками или фигурными скобками

При инициализации значения выполняются следующие действия:

Для классов, имеющих хотя бы один открытый конструктор, вызывается конструктор по умолчанию.

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

В случае массивов каждый элемент инициализируется значением.

Во всех остальных случаях переменная инициализируется нулевым значением.

Инициализация копированием

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

Переменная инициализируется с помощью знака равенства.

Аргумент передается в функцию.

Объект возвращается функцией.

Возникает или перехватывается исключение.

Нестатический элемент данных инициализируется с помощью знака равенства.

Класс, структура и члены объединения инициализируются с применением инициализации путем копирования во время агрегатной инициализации. Примеры см. в разделе "Статистическая инициализация ".

Следующий код демонстрирует несколько примеров инициализации копированием.

Инициализация копированием не может вызывать явные конструкторы.

В некоторых случаях, если конструктор копии класса удален или недоступен, копируемая инициализация вызывает ошибку компилятора.

Прямая инициализация

Прямая инициализация — это инициализация с использованием (непустых) круглых или фигурных скобок. В отличие от копируемой инициализации она может вызывать явные конструкторы. Она выполняется в следующих случаях:

Переменная инициализируется с помощью непустых круглых или фигурных скобок.

Переменная инициализируется ключевым словом new плюс непустые скобки или скобки

Переменная инициализируется с помощью . static_cast

В конструкторе базовые классы и нестатические члены инициализируются с помощью списка инициализации.

В копии захваченной переменной в лямбда-выражении.

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

Инициализация списка

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

Класс инициализируется с помощью ключевого new слова

Объект возвращается функцией.

Аргумент передается функции.

Один из аргументов при прямой инициализации.

В инициализаторе нестатических элементов данных.

В списке инициализации конструктора.

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

Агрегатная инициализация

Агрегатная инициализация — форма инициализации списка для массивов и типов классов (часто структур и объединений), со следующими характеристиками:

Отсутствие закрытых или защищенных членов.

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

Отсутствие базовых классов.

Отсутствие виртуальных функций-членов.

В Visual Studio 2015 и более ранних версиях агрегат не может иметь инициализаторы фигурных скобок или равных значений для нестатических элементов. Это ограничение было удалено в стандарте C++14 и реализовано в Visual Studio 2017 г.

Агрегатные инициализаторы состоят из списка инициализации в фигурных скобках со знаком равенства или без него как в приведенном ниже примере:

Вы должны увидеть следующий результат.

Элементы массива, объявленные, но не инициализированные явно во время статистической инициализации, инициализируются с нуля, как показано myArr3 выше.

Инициализация объединений и структур

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

Инициализация статистических выражений, содержащих статистические выражения

Агрегатные типы могут содержать другие агрегатные типы, например массивы массивов, массивы структур и т. п. Эти типы инициализируются с помощью вложенных наборов фигурных скобок, как показано в следующем примере:

Инициализация ссылок

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

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

Хотя синтаксис может быть одинаковым, инициализация переменных ссылочного типа и присваивание значений переменным ссылочного типа семантически различаются. В предыдущем примере присваивания, которые изменяют значения переменных iVar и lVar , выглядят аналогично инициализации, но имеют другой эффект. Инициализация определяет объект, на который указывает переменная ссылочного типа; при присваивании через ссылку производится присваивание значения объекту, на который указывает ссылка.

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

Переменные ссылочного типа можно объявлять без инициализаторов только в указанных ниже случаях.

Объявления функций (прототипы). Пример:

Объявления типов значений, возвращаемых функцией. Пример:

Объявления члена класса ссылочного типа. Пример:

Объявление переменной, явно указанной как extern . Пример:

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


Граф принятия решений для инициализации ссылочных типов

Ссылки на volatile типы (объявленные как volatile typenameidentifier &) можно инициализировать с volatile объектами того же типа или с объектами, которые не были объявлены как volatile . Однако они не могут быть инициализированы объектами const этого типа. Аналогичным образом ссылки на const типы (объявленные как const typenameidentifier &) можно инициализировать с const объектами того же типа (или с объектами, которые не были объявлены как const ). Однако они не могут быть инициализированы объектами volatile этого типа.

Ссылки, которые не соответствуют ни ключевому слову const volatile , либо могут быть инициализированы только с объектами, объявленными как ни один из const volatile них.

Инициализация внешних переменных

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

Читайте также: