Зачем нужен конструктор по умолчанию c
Перейти к содержимому

Зачем нужен конструктор по умолчанию c

  • автор:

 

Структуры и конструкторы по умолчанию

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

Вот простой пример, как вы ответите на следующий вопрос: сколько значимых типов из .NET Framework содержит конструкторы по умолчанию? Интуитивным ответом кажется «все«, и будете не правы, поскольку на самом деле, ни один из значимых типов .NET Framework не содержит конструктора по умолчанию.

Но давайте обо всем по порядку и начнем со сравнения таких понятий, как конструктор по умолчанию (default constructor) и значения по умолчанию (default values).

Когда речь заходит о классах, то все просто: если конструктор не объявлен явно, то компилятор языка C# сгенерирует конструктор без параметров, который и называется конструктором по умолчанию; значением же по умолчанию любой переменной (или поля) ссылочного типа является null.

Но когда речь заходит о структурах, то тут все становится немного сложнее. Значением по умолчанию экземпляра значимого типа является «нулевое» представление всех его полей, т.е. все числовые поля равны 0, а все ссылочные поля равны null. С точки зрения языка C# конструктор по умолчанию значимого типа делает тоже самое – он возвращает экземпляр структуры со значением по умолчанию. Это значит, что следующий код является эквивалентным:

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

В спецификации языка C# сказано, что пользователю запрещается создавать конструктор по умолчанию явно, поскольку любая структура содержит его неявно. Однако это не совсем так: если мы получим список конструкторов типа Size, то мы не увидим там конструктора без параметров. То что в языке C# называется конструктором по умолчанию, на самом деле является «обнулением» объекта и не является конструктором в привычном смысле этого слова и не содержит никакого специализированного кода в типе Size.

Аналогичное смешивание понятий существует не только при вызове оператора new, но и при инициализации полей структуры в конструкторе. Так, для конструкторов структур применяются те же правила обязательной инициализации всех полей структуры, аналогичные правилам для локальных переменных (definite assignment rules). Это означает, что до завершения тела конструктора все поля структуры должны быть явно или неявно проинициализированы:

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

Такое смешивание понятий конструктора по умолчанию с получением значения по умолчанию для значимых типов является общепринятым на платформе .NET, но не является обязательным. Некоторые языки, как например, «голый» IL или Managed C++, поддерживают полноценные пользовательские конструкторы по умолчанию для значимых типов, которые позволяют инициализировать состояние структуры произвольным образом, а не только значениями по умолчанию.
Хотя язык C# не позволяет создавать конструкторы по умолчанию для структур он позволяет их использовать. Когда компилятор языка C# встречает инструкцию “new SomeType”, то генерируемый код зависит от того, является ли указанный тип классом или структурой, а также от того, содержит ли структура полноценный конструктор по умолчанию или нет:

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

Оба типа Size и CustomValueType являются значимыми типами, при этом тип CustomValueType содержит конструктор по умолчанию (мы рассмотрим позднее, как этого добиться). В строке 2 происходит инициализация переменной size значениями по умолчанию, с помощью инструкции initobj, а в строке 3 происходит вызов конструктора типа CustomValueType с помощью инструкции call CustomValueType..ctor.

Создание структуры с конструктором по умолчанию

Для генерации структуры воспользуемся модулем System.Reflection.Emit, который поддерживает весь необходимый функционал. Процесс создания нового типа начинается создания объекта AssemblyBuilder, внутри которого создается экземпляр DynamicModuleBuilder, в котором уже создается тип. Такой порядок объясняется тем, что сборка, на самом деле, содержит лишь метаданные сборки (зависимости и т.п.) и модули, которые, в свою очередь уже содержат пользовательские типы.

Метод GenerateValueTypeWithDefaultConstructor принимает имя типа и строку, выводимую на консоль (строка нужна для написания модульного теста, проверяющего работу этого метода). После этого, мы генерируем простой значимый тип с конструктором, вызывающим Console.WriteLine с указанной строкой.
После этого мы можем создать данный тип с помощью Activator.CreateInstance или же сохранить сгенерированную сборку с помощью AssemblyBuilder.Save и использовать ее обычным образом. После этого, можно будет пользоваться сгенерированным типом обычным образом и спокойно вызвать конструктор по умолчанию значимого типа:

Правила вызова конструктора по умолчанию

Существует несколько моментов использования из языка C# структур с настоящим конструктором по умолчанию.

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

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

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

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

Данный конструктор принимает емкость (capacity) списка, а значит внутри него будет создан массив соответствующего размера, что, в свою очередь, приведет к вызову не менее 50 конструкторов по умолчанию, хотя текущий размер списка все равно будет равным 0. Для решения этой проблемы пришлось бы разделить процесс выделения памяти для массива от процесса создания экземпляра значимого типа внутри него. Такая техника применяется в языке С++ с помощью размещающего оператора new, однако это явно добавило бы больше проблем, нежели пользы, поэтому достаточно разумно, что разработчики .NET на это не пошли.

ПРИМЕЧАНИЕ
На платформе .NET проблема со списками, массивами и перечислениями все еще существует. Подробности можно прочитать в статье «Проблемы передачи списка перечислений или Почему абстракции текут».

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

C++. Классы. Часть 2. Конструктор класса. Особенности использования конструкторов в классах. Конструктор по умолчанию. Параметризированные конструкторы. Примеры классов, содержащих конструкторы

В данной теме рассматривается понятие конструктора на примере unmanaged ( native ) классов. Материалы данной темы также касаются и конструкторов managed- классов.

Содержание

  • 1. Что называется конструктором класса? Какое назначение конструктора?
  • 2. В какой момент работы программы осуществляется вызов конструктора класса?
  • 3. Может ли конструктор иметь параметры? Примеры конструкторов с разным количеством параметров
  • 4. Обязательно ли в классе описывать конструктор?
  • 5. Что такое конструктор по умолчанию (default constructor)? Примеры
  • 6. Сколько конструкторов по умолчанию может иметь класс?
  • 7. Может ли конструктор возвращать значение?
  • 8. Пример объявления и использования класса, который содержит несколько конструкторов. Реализация типа string в классе
  • 9. Как работает конструктор класса в случае, когда в классе объявлен объект другого класса (подобъект)? Пример
  • 10. Как работает конструктор класса в случае, когда создается объект класса, который есть производным (унаследованным) от другого класса?
  • 11. Может ли конструктор объявляться в разделе private ?
  • 12. В каких случаях могут создаваться приватные конструкторы?
  • 13. Как будет работать программа, если попробовать создать объект класса, в котором объявлен приватный конструктор по умолчанию?
  • 14. Может ли в классе быть объявлено два конструктора, которые принимают одинаковое количество параметров?
  • 15. Какие конструкторы называются параметризованными?
  • 16. Какие существуют способы инициализации членов объекта с помощью конструктора, который получает один параметр? Пример

Поиск на других ресурсах:

1. Что называется конструктором класса? Какое назначение конструктора?

Класс может содержать специальные функции: конструкторы и деструкторы .

Конструктор класса – это специальный метод (функция) класса. Конструктор вызывается при создании объекта класса. Как правило, конструктор используется для:

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

Конструктор предназначен для формирования экземпляра объекта класса. Имя конструктора класса совпадает с именем класса.

2. В какой момент работы программы осуществляется вызов конструктора класса?

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

3. Может ли конструктор иметь параметры? Примеры конструкторов с разным количеством параметров

Конструктор может иметь любое количество параметров. Также конструктор может быть без параметров (конструктор по умолчанию).

Пример. Объявляется класс CMyDate , определяющий дату (число, месяц, год). В классе объявлены два конструктора. Один конструктор без параметров, другой конструктор, получающий три параметра, которые устанавливают новую дату.

Объявление класса и его методов имеет вид

Демонстрация вызова конструкторов при объявлении объектов класса

4. Обязательно ли в классе описывать конструктор?

Не обязательно. При создании объекта класса, который не содержит ни одного конструктора, будет вызываться неявно заданный конструктор по умолчанию (default constructor), выделяющий память для объекта класса. Однако, в классе можно объявить собственный конструктор по умолчанию. Такой конструктор называется: явно заданный конструктор по умолчанию.

5. Что такое конструктор по умолчанию ( default constructor )? Примеры

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

Пример 1. Пусть задан класс CMyPoint , определяющий точку на координатной плоскости. В классе не реализовано ни одного конструктора.

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

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

компилятор выдаст ошибку.

Пример 2. Модификация класса CMyPoint . В классе присутствует явно заданный конструктор по умолчанию.

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

6. Сколько конструкторов по умолчанию может иметь класс?

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

7. Может ли конструктор возвращать значение?

Конструктор не может возвращать значения (даже значение void ). Если в конструкторе написать возвращение значения с помощью оператора return , то компилятор выдаст ошибку.

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

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

Для представления внутренних членов данных в классе используется тип string . Чтобы использовать тип string в программах на Visual C++, нужно в начале модуля, который описывает класс, подключить библиотеку < string > и пространство имен std

Реализация конструкторов и деструктора класса

 

9. Как работает конструктор класса в случае, когда в классе объявлен объект другого класса (подобъект)? Пример
  • первым вызывается конструктор (конструкторы) класса, который есть подобъектом включающего класса;
  • следующим вызывается конструктор включающего класса.

Пример. Пусть заданы 2 класса: CMyPoint , CMyLine . В классе CMyLine есть два подобъекта класса CMyPoint . При создании объекта класса CMyLine , сначала будут вызваны 2 конструктора класса CMyPoint для двух подобъектов, а потом вызовется конструктор класса CMyLine .

Объявление объекта класса CMyLine

После такого объявления конструкторы вызовутся в следующей последовательности:

  • CMyPoint::CMyPoint() для объекта p1 класса CMyLine() ;
  • CMyPoint::CMyPoint() для объекта p2 класса CMyLine() ;
  • CMyLine::CMyLine() для объекта ML .
10. Как работает конструктор класса в случае, когда создается объект класса, который есть производным (унаследованным) от другого класса?

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

  • сначала вызовется конструктор базового класса;
  • следующим вызовется конструктор унаследованного класса.
11. Может ли конструктор объявляться в разделе private ?

Да, может. Такой конструктор называется приватным конструктором (private constructor).

12. В каких случаях могут создаваться приватные конструкторы?

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

Доступ к созданию объектов с помощью приватных конструкторов имеют:

  • методы, объявленные в классе (методы-члены класса). Сюда относятся также и статические методы, объявленные с ключевым словом static ;
  • методы дружественных классов (дружественные методы).
13. Как будет работать программа, если попробовать создать объект класса, в котором объявлен приватный конструктор по умолчанию?

В этом случае будет ошибка компиляции.

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

Попытка создать объект класса приведет к ошибке компиляции

То же самое будет, если попробовать создать статический объект

14. Может ли в классе быть объявлено два конструктора, которые принимают одинаковое количество параметров?

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

  • в классе не может быть двух методов (функций) с одинаковой (совпадающей) сигнатурой.

Этот вопрос тесно связан с темой перегрузки функций.

15. Какие конструкторы называются параметризованными?

Параметризованный конструктор – это конструктор класса, который имеет параметры.

16. Какие существуют способы инициализации членов объекта с помощью конструктора, который получает один параметр? Пример

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

  • инициализация по образцу вызова функции;
  • инициализация по образцу оператора присваивания.

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

Общий вид объявления класса

Объявить объект класса CMyInt с использованием конструктора с 1 параметром можно двумя способами

Урок №116. Конструкторы

На этом уроке мы рассмотрим конструкторы в языке С++.

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

Когда все члены класса (или структуры) являются открытыми, то мы можем инициализировать класс (или структуру) напрямую, используя список инициализаторов или uniform-инициализацию (в C++11):

Однако, как только мы сделаем какие-либо переменные-члены класса закрытыми, то больше не сможем инициализировать их напрямую. Здесь есть смысл: если вы не можете напрямую обращаться к переменной (потому что она закрыта), то вы и не должны иметь возможность напрямую её инициализировать.

Как тогда инициализировать класс с закрытыми переменными-членами? Использовать конструкторы.

Конструктор — это особый тип метода класса, который автоматически вызывается при создании объекта этого же класса. Конструкторы обычно используются для инициализации переменных-членов класса значениями, которые предоставлены по умолчанию/пользователем, или для выполнения любых шагов настройки, необходимых для используемого класса (например, открыть определенный файл или базу данных).

В отличие от обычных методов, конструкторы имеют определенные правила их именования:

конструкторы всегда должны иметь то же имя, что и класс (учитываются верхний и нижний регистры);

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

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

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

Конструкторы и деструкторы

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

Если конструктор требует аргументы, их следует указать:

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

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

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

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

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

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

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

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

Во всех этих случаях транслятор не вызывает конструктора для вновь создаваемого объекта:

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

Вместо этого в них копируется содержимое объекта-источника:

  • date1 в приведенном примере;
  • фактического параметра;
  • объекта-результата в операторе return .

Конструктор копии

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

Деструкторы

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

. Так, для класса X деструктор будет иметь имя

X() . Многие классы используют динамическую память, которая выделяется конструктором, а освобождается деструктором.

Поля, имеющие тип класса

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

Конструктор нового класса имеет пустое тело и список вызываемых конструкторов класса vect , перечисленных после двоеточия (:) через запятую (,). Они выполняются с целым аргументом i , создавая 3 объекта класса vect: a, b, c .

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

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

 

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

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