какие виды конструкторов создаются по умолчанию
Конструкторы и деструкторы
Конструкторы
Конструктор — функция, предназначенная для инициализации объектов класса. Рассмотрим класс date :
Если конструктор требует аргументы, их следует указать:
Если необходимо обеспечить несколько способов инициализации объектов класса, задается несколько конструкторов:
Конструкторы подчиняются тем же правилам относительно типов параметров, что и перегруженные функции. Если конструкторы существенно различаются по типам своих параметров, то компилятор при каждом использовании может выбрать правильный:
Одним из способов сократить количество перегруженных функций (в том числе и конструкторов) является использование значений по умолчанию.
Конструктор по умолчанию
При создании объекта вызывается конструктор, за исключением случая, когда объект создается как копия другого объекта этого же класса, например:
Однако имеются случаи, в которых создание объекта без вызова конструктора осуществляется неявно:
Во всех этих случаях транслятор не вызывает конструктора для вновь создаваемого объекта:
Вместо этого в них копируется содержимое объекта-источника:
Конструктор копии
Как правило, при создании нового объекта на базе уже существующего происходит поверхностное копирование, то есть копируются те данные, которые содержит объект-источник. При этом если в объекте-источнике имеются указатели на динамические переменные и массивы, или ссылки, то создание копии объекта требует обязательного дублирования этих объектов во вновь создаваемом объекте. С этой целью вводится конструктор копии, который автоматически вызывается во всех перечисленных случаях. Он имеет единственный параметр — ссылку на объект-источник:
Деструкторы
Определяемый пользователем класс имеет конструктор, который обеспечивает надлежащую инициализацию. Для многих типов также требуется обратное действие. Деструктор обеспечивает соответствующую очистку объектов указанного типа. Имя деструктора представляет собой имя класса с предшествующим ему знаком «тильда»
. Так, для класса X деструктор будет иметь имя
Поля, имеющие тип класса
Конструкторы членов класса всегда выполняются до конструктора класса, в котором эти члены описаны. Порядок выполнения конструкторов для членов класса определяется порядком объявления членов класса. Если конструктору члена класса требуются аргументы, этот член с нужными аргументами указывается в списке инициализации. Деструкторы вызываются в обратном порядке.
BestProg
C++. Классы. Часть 2. Конструктор класса. Особенности использования конструкторов в классах. Конструктор по умолчанию. Параметризированные конструкторы. Примеры классов, содержащих конструкторы
В данной теме рассматривается понятие конструктора на примере unmanaged ( native ) классов. Материалы данной темы также касаются и конструкторов managed- классов.
Содержание
Поиск на других ресурсах:
1. Что называется конструктором класса? Какое назначение конструктора?
Конструктор класса – это специальный метод (функция) класса. Конструктор вызывается при создании объекта класса. Как правило, конструктор используется для:
Конструктор предназначен для формирования экземпляра объекта класса. Имя конструктора класса совпадает с именем класса.
2. В какой момент работы программы осуществляется вызов конструктора класса?
Вызов конструктора осуществляется при создании объекта класса. Конструктор класса вызывается компилятором.
3. Может ли конструктор иметь параметры? Примеры конструкторов с разным количеством параметров
Конструктор может иметь любое количество параметров. Также конструктор может быть без параметров (конструктор по умолчанию).
Объявление класса и его методов имеет вид
Демонстрация вызова конструкторов при объявлении объектов класса
4. Обязательно ли в классе описывать конструктор?
Не обязательно. При создании объекта класса, который не содержит ни одного конструктора, будет вызываться неявно заданный конструктор по умолчанию (default constructor), выделяющий память для объекта класса. Однако, в классе можно объявить собственный конструктор по умолчанию. Такой конструктор называется: явно заданный конструктор по умолчанию.
5. Что такое конструктор по умолчанию ( default constructor )? Примеры
Конструктор по умолчанию – это конструктор класса, который объявляется без параметров. Если класс не содержит явным образом определенный конструктор, тогда при создании объекта автоматически вызывается конструктор по умолчанию. Конструктор по умолчанию просто выделяет память для объекта класса, если он объявляется.
Однако, при создании объекта класса компилятор автоматически вызовет конструктор по умолчанию.
Конструктор по умолчанию автоматически вызовется только тогда, если в классе не объявлено ни одного конструктора. Как только в классе объявить любой другой конструктор с параметрами, то при объявлении
компилятор выдаст ошибку.
Демонстрация вызова явным образом заданного конструктора по умолчанию
6. Сколько конструкторов по умолчанию может иметь класс?
Каждый класс может иметь только один конструктор по умолчанию. Это связано с тем, что в классе не может быть двух методов (функций) с одинаковой сигнатурой.
7. Может ли конструктор возвращать значение?
8. Пример объявления и использования класса, который содержит несколько конструкторов. Реализация типа string в классе
Например, нужно объявить класс, который содержит информацию об имени работника и его возраст.
Реализация конструкторов и деструктора класса
9. Как работает конструктор класса в случае, когда в классе объявлен объект другого класса (подобъект)? Пример
Объявление объекта класса CMyLine
После такого объявления конструкторы вызовутся в следующей последовательности:
10. Как работает конструктор класса в случае, когда создается объект класса, который есть производным (унаследованным) от другого класса?
Если есть два класса, один из которых базовый а другой — унаследованный от базового, то в этом случае последовательность вызовов следующая:
Да, может. Такой конструктор называется приватным конструктором (private constructor).
12. В каких случаях могут создаваться приватные конструкторы?
При объявлении обычного объекта класса, конструкторы, которые размещены в разделе private (приватные конструкторы), есть недоступными.
Чтобы использовать приватные конструкторы, нужно выполнение одного из трех условий:
13. Как будет работать программа, если попробовать создать объект класса, в котором объявлен приватный конструктор по умолчанию?
В этом случае будет ошибка компиляции.
Попытка создать объект класса приведет к ошибке компиляции
То же самое будет, если попробовать создать статический объект
14. Может ли в классе быть объявлено два конструктора, которые принимают одинаковое количество параметров?
Да, может. Однако с условием, что типы параметров будут отличаться. Для класса должно выполняться правило:
Этот вопрос тесно связан с темой перегрузки функций.
15. Какие конструкторы называются параметризованными?
Параметризованный конструктор – это конструктор класса, который имеет параметры.
16. Какие существуют способы инициализации членов объекта с помощью конструктора, который получает один параметр? Пример
Для конструктора, получающего один параметр существует два способа инициализации:
Общий вид объявления класса
Объявить объект класса CMyInt с использованием конструктора с 1 параметром можно двумя способами
Виды конструкторов классов С++
В данном посте я попытаюсь разобраться какие виды конструкторов бывают.
Пока мне известно два конструктора конструктор по умолчанию и конструктор копий. Да названия, то мне известны, а что это такое я даже не догадываюсь.
Да можно догадаться, что конструктор по умолчанию это умолчательная инициализация класса. Но синтаксис этого конструктора,
когда он вызывается мне не известно, так же как и конструктор копий, как его вызвать, когда вызовется конструктор по умолчанию ничего мне это не ясно. От как раз щас в этом посте я попытаюсь разобраться с наболевшей темой
Конструктор по умолчанию.
Это конструктор без аргументов. Попробуем привести пример. Создадим класс String, а в нем создадим конструктор по умолчанию и посмотрим когда он вызовется.
Как мы видим при вызове String a; вызывается конструктор по умолчанию, а при вызове String b(); со скобками уже конструктор по умолчанию не вызывается, так разобрались, но вызываться должен в любом случае конструктор при создании объекта токо какой? хз. (не вызывается потому, что Strhing b() — это декларация функции b() ). Да такую запись String b(); не стоить использовать потому, что она ничего не делает. Это просто декларация функции. Инициализацию с пустыми скобками не используйте, только передавайте какое нить значение обязательно потому, что ничего не вызывается если не передаешь.
Конструктор копирования.
Это конструктор который принимает ссылку на тот же объект и создает еще один объект копию переданного по ссылке. Например:
Конструкторы / FAQ C++
Что там с конструкторами?
Конструкторы строят объекты из пыли.
Конструкторы похожи на «функции инициализации». Они превращают груду произвольных битов в живой объект. Как минимум, они инициализируют внутренние поля. Они также могут выделять ресурсы (память, файлы, семафоры, сокеты и т.д.).
Обычное сокращение для конструктора – «ctor».
Предположим, что List – это имя некоего класса. Затем функция f() объявляет локальный объект List с именем x :
Ответ ниже относится к классическому (до 11) C++. Этот вопрос касается особенности конструкторов C++11, которые вызывают конструкторы того же типа. (ссылка скоро появится)
Иногда вы можете объединить два конструктора с помощью параметра по умолчанию:
Если это не сработает, например, если нет подходящего параметра по умолчанию, который объединяет два конструктора, иногда вы можете выделить их общий код в закрытую функцию-член init() :
«Конструктор по умолчанию» – это конструктор, который можно вызывать без аргументов. Одним из примеров этого является конструктор, который не принимает параметров:
Другой пример «конструктора по умолчанию» – тот, который может принимать аргументы, если им заданы значения по умолчанию:
Конструктор по умолчанию Fred ‘a (кроме случаев, описанных ниже).
Если в вашем классе нет конструктора по умолчанию, вы получите ошибку времени компиляции, когда попытаетесь создать массив, используя простой синтаксис, приведенный выше:
Конечно, вам не нужно вводить Fred(5,7) для каждой записи – вы можете вводить любые числа, которые хотите, даже параметры или другие переменные.
Мои конструкторы должны использовать «списки инициализации» или «присвоение»?
Списки инициализации. На самом деле конструкторы, как правило, должны инициализировать все объекты-члены в списке инициализации. Одно исключение обсуждается ниже.
Просмотрите обсуждение инициализации нестатических членов данных в C++11
Если этого было недостаточно, есть еще один источник неэффективности при использовании присваивания в конструкторе: объект-член будет полностью построен его конструктором по умолчанию, и это может, например, выделить некоторый объем памяти по умолчанию или открыть какой-то файл по умолчанию. Вся эта работа может быть напрасной, если выражение «что-то» и/или оператор присваивания заставят объект закрыть этот файл и/или освободить эту память (например, если конструктор по умолчанию выделил не достаточно большой пул памяти, или если он открыл неправильный файл).
Вывод: при прочих равных, ваш код будет работать быстрее, если вы будете использовать списки инициализации, а не присваивание.
Как следует упорядочить инициализаторы в списке инициализации конструктора?
Непосредственные базовые классы (слева направо), затем объекты-члены (сверху вниз).
Другими словами, порядок в списке инициализации должен имитировать порядок, в котором будут происходить инициализации. Это руководство препятствует возникновению особенно трудноуловимого типа ошибок зависимости от порядка, давая очевидную визуальную подсказку. Например, следующий код содержит ужасную ошибку.
Результат этой программы будет следующий.
Для таких случаев не все компиляторы выдают диагностические сообщения. Но вы были предупреждены.
Этично ли инициализировать один объект-член с использованием другого объекта-члена в выражении инициализатора?
Да, но будьте осторожны и делайте это только тогда, когда это приносит пользу.
Что делать, если один объект-член должен быть инициализирован с помощью другого объекта-члена?
Обратите внимание, что комментарий // ЗАВИСИМОСТЬ ОТ ПОРЯДКА указан у задействованных членов данных в теле класса, а не в списке инициализации конструктора, в котором фактически была создана зависимость от порядка. Это потому, что порядок объектов-членов в теле класса имеет решающее значение; порядок инициализаторов в списке инициализации конструктора не имеет значения.
Следует ли использовать указатель this в конструкторе?
Некоторые считают, что вам не следует использовать указатель this в конструкторе, потому что объект еще не полностью сформирован. Однако вы можете использовать this в конструкторе (в <теле>и даже в списке инициализации), если будете осторожны.
Вот то, что всегда работает: <тело>конструктора (или функции, вызываемой из конструктора) может надежно обращаться к членам данных, объявленным в базовом классе, и/или членам данных, объявленным в собственном классе конструктора. Это связано с тем, что все эти члены данных гарантированно будут полностью созданы к моменту начала выполнения <тела>конструктора.
Вот кое-что, что иногда работает: если вы передаете какой-либо из членов данных в объекте this инициализатору другого члена данных, вы должны убедиться, что этот член данных уже был инициализирован. Хорошая новость заключается в том, что вы можете определить, был ли инициализирован (или нет) член данных, с помощью некоторых простых языковых правил, которые не зависят от конкретного компилятора, который вы используете. Плохая новость заключается в том, что вы должны знать эти языковые правила (например, сначала инициализируются подобъекты базового класса (посмотрите порядок, если у вас есть множественное и/или виртуальное наследование!), затем члены данных, определенные в классе, инициализируются в порядке, в котором они появляются в объявлении класса). Если вы не знаете этих правил, не передавайте ни один член данных из объекта this (независимо от того, используете ли вы это ключевое слово явно или нет) в инициализатор любого другого члена данных! И если вы знаете правила, будьте осторожны.
Что такое «идиома именованного конструктора»?
Это метод, который обеспечивает для пользователей вашего класса более интуитивно понятные и/или более безопасные операции создания.
Проблема в том, что у конструкторов всегда то же имя, что и у класса. Следовательно, единственный способ различать различные конструкторы класса – это список параметров. Но если конструкторов много, различия между ними становятся несколько менее заметными и более подверженными ошибкам.
С помощью идиомы именованных конструкторов вы объявляете все конструкторы класса в разделах private или protected и предоставляете общедоступные статические ( public static ) методы, возвращающие объект. Эти статические методы представляют собой так называемые «именованные конструкторы». Как правило, для каждого способа создания объекта существует один такой статический метод.
Один из способов решить эту двусмысленность – использовать идиому именованного конструктора:
Теперь у пользователей Point есть четкий и недвусмысленный синтаксис для создания точек Point в любой системе координат:
Обратите внимание, что идиома именованного конструктора, по крайней мере, как реализовано выше, работает так же быстро, как и прямой вызов конструктора – современные компиляторы не будут делать никаких дополнительных копий вашего объекта.
Означает ли возврат по значению дополнительные копии и дополнительные накладные расходы?
Все(?) компиляторы коммерческого уровня оптимизируют лишнюю копию, по крайней мере, в случаях, как показано в предыдущем ответе данного FAQ.
Чтобы пример оставался чистым, давайте сократим всё до самого необходимого. Предположим, что функция caller() вызывает rbv() («rbv» означает «return by value», «возврат по значению»), которая возвращает объект Foo по значению:
Суть этого вопроса заключается в том, что ответ – нет, компиляторы C++ коммерческого уровня реализуют возврат по значению таким образом, который позволяет им устранить накладные расходы, по крайней мере, в простых случаях, подобных тем, которые показаны в предыдущем вопросе. В частности, все(?) компиляторы C++ коммерческого уровня оптимизируют следующий случай:
В начале 90-х я проводил семинар по группе компиляторов IBM в Торонто, и один из их инженеров сказал мне, что они обнаружили, что эта оптимизация возврата по значению выполняется настолько быстро, что вы получаете ее, даже если не компилируете с включенной оптимизацией. Поскольку оптимизация возврата по значению заставляет компилятор генерировать меньше кода, в дополнение к тому, что ваш сгенерированный код меньше и быстрее, она фактически улучшает время компиляции. Дело в том, что оптимизация возврата по значению реализована почти повсеместно, по крайней мере, в случаях кода, подобных показанным выше.
А как насчет возврата локальной переменной по значению? Существует ли локальный объект как отдельный объект или его нужно оптимизировать?
Когда ваш код возвращает локальную переменную по значению, ваш компилятор может полностью оптимизировать локальную переменную (нулевые затраты на пространство и нулевые временные затраты). Локальная переменная никогда не существует как отдельный объект от целевой переменной вызывающей стороны (подробности о том, что именно это означает, смотрите ниже). Но некоторые компиляторы не оптимизируют это.
Вот некоторые(!) компиляторы, которые полностью оптимизируют локальную переменную:
Вот некоторые(!) компиляторы, которые не оптимизируют локальную переменную:
Вот пример, показывающий, что мы имеем в виду в ответе на этот вопрос:
Они делают это так же, как описывалось выше: возврат по значению в функции rbv() реализуется как передача по указателю, где указатель указывает на место, где должен быть инициализирован возвращаемый объект.
Вот получившийся (псевдо) код:
Почему я не могу инициализировать статический член данных в списке инициализации конструктора?
Потому что вы должны явно определить статические члены данных вашего класса.
Fred.cpp (или Fred.C, или что-то еще):
Почему классы со статическими членами данных получают ошибки линкера?
Потому что статические члены данных должны быть явно определены точно в одном блоке компиляции. Если вы сделали не так, то вы, вероятно, получите ошибку компоновщика « undefined external ». Например:
Линкер будет кричать вам (« Fred::j_ не определен»), если вы не определите (а не просто объявите) Fred::j_ в (точно) одном из ваших исходных файлов:
Обычное место для определения статических членов данных класса Fred – это файл Fred.cpp (или Fred.C, или любое другое расширение исходного файла, которое вы используете).
Могу я добавить = инициализатор; к объявлению члена данных static const в области класса?
Да, но с некоторыми важными оговорками.
Перед тем как прочесть предостережения, вот простой допустимый пример:
И, как и другие статические члены данных, он должен быть определен ровно в одной единице компиляции, но на этот раз без части = инициализатор :
Что такое «фиаско (проблема) с порядком инициализации static » / «проблема порядка статической инициализации»?
Незаметный способ вывести вашу программу из строя.
Это оно. Это так просто.
Чтобы узнать, как решить эту проблему, смотрите следующий раздел часто задаваемых вопросов.
Примечание. Проблема порядка инициализации static также в некоторых случаях может относиться к встроенным/внутренним типам.
Как мне предотвратить «проблему порядка инициализации static »?
Файл y.cpp определяет объект y :
Для полноты картины конструктор Barney может выглядеть примерно так:
Есть много решений этой проблемы, но очень простым и полностью переносимым решением является идиома «создания при первом использовании»: объект x класса Fred в области видимости пространства имен или в глобальной области видимости заменяется на функцию x() в области видимости пространства имен или в глобальной области видимости, которая возвращает объект Fred по ссылке.
Это называется «Construct On First Use Idiom» (идиома «создания при первом использовании»), потому что она делает именно это: объект Fred (в области видимости пространства имен или в глобальной области видимости) создается при первом использовании.
Обратной стороной этого подхода является то, что объект Fred никогда не разрушается. Если у объекта Fred есть деструктор с важными побочными эффектами, есть другой метод, который решает эту проблему; но его нужно использовать с осторожностью, поскольку он создает возможность другой (столь же неприятной) проблемы.
Примечание. Проблема порядка статической инициализации в некоторых случаях также может относиться к встроенным/внутренним типам.
Почему идиома «создания при первом использовании» не использует статический объект вместо статического указателя?
Краткий ответ: можно использовать статический объект, а не статический указатель, но при этом возникает другая (столь же трудноуловимая и столь же неприятная) проблема.
Длинный ответ: иногда люди беспокоятся о том, что предыдущее решение дает «утечку». Во многих случаях это не проблема, но в некоторых случаях это всё-таки проблема. Примечание: даже несмотря на то, что объект, на который указывает ans в предыдущем ответе, никогда не удаляется, на самом деле нет «утечки» памяти при выходе из программы, поскольку операционная система автоматически освобождает всю память в куче программы при выходе из этой программы. Другими словами, вам нужно беспокоиться об этом только тогда, когда деструктор объекта Fred выполняет какое-то важное действие (например, записывает что-то в файл), которое должно произойти когда-то во время выхода из программы.
В тех случаях, когда объект, «созданный при первом использовании» (в данном случае Fred ), должен в конечном итоге быть разрушен, вы можете рассмотреть возможность изменения функции x() следующим образом:
Однако с этим изменением есть (или, скорее, может быть) довольно незаметная проблема. Чтобы понять эту потенциальную проблему, давайте, в первую очередь, вспомним, зачем мы всё это делаем: нам нужно на 100% убедиться, что наш статический объект (a) создается до его первого использования и (b) не разрушается до последнего использования. Очевидно, было бы катастрофой, если бы какой-либо статический объект использовался либо до создания, либо после разрушения. Суть здесь в том, что вам нужно беспокоиться о двух ситуациях (статическая инициализация и статическая деинициализация), а не только об одной.
Какой метод гарантирует статическую инициализацию и статическую деинициализацию?
Краткий ответ: используйте «идиому изящного счетчика» («Nifty Counter Idiom») (но убедитесь, что вы понимаете сложность компромиссов!).
TODO: описать решение.
TODO: описать компромиссы.
Как мне предотвратить «проблему порядка статической инициализации» для моих статических членов данных?
Используйте идиому «создания членов при первом использовании» (Construct Members On First Use Idiom), которая в основном совпадает с обычной идиомой «создания при первом использовании» или, возможно, одним из ее вариантов, но использует статическую функцию-член вместо глобальной функции или функции пространства имен.
Предположим, у вас есть класс X со статическим объектом Fred :
Естественно, этот статический член инициализируется отдельно:
Естественно, объект Fred также будет использоваться в одном или нескольких методах X :
В любом случае всегда переносимо и безопасно изменить статический член данных X::x_ на статическую функцию-член:
Естественно, этот статический член инициализируется отдельно:
Затем вы просто меняете любое использование x_ на x() :
Примечание. Проблема порядка статической инициализации в некоторых случаях также может относиться к встроенным/внутренним типам.
Нужно ли мне беспокоиться о «проблеме порядка статической инициализации» для переменных встроенных/внутренних типов?
Вывод этой маленькой программы покажет, что она использует y до ее инициализации. Решением, как и раньше, является идиома «создания при первом использовании»:
Конечно, вы можете упростить это решение, переместив код инициализации для x и y в соответствующие функции:
И, если избавиться от операторов печати, вы можете еще больше упростить код:
Более того, поскольку y инициализируется с использованием константного выражения, ей больше не нужна функция-оболочка – она снова может быть простой переменной.
Как я могу обработать сбой в конструкторе?
Выбросить исключение. Подробности смотрите здесь (ссылка скоро появится).
Что такое «идиома именованных параметров»?
Это довольно полезный способ использования цепочки методов.
Идея, называемая идиомой именованных параметров (Named Parameter Idiom), состоит в том, чтобы изменить параметры функции на методы созданного класса, где все эти методы возвращают *this по ссылке. Затем вы просто переименовываете основную функцию в метод без параметров для этого класса.
Чтобы облегчить понимание предыдущего абзаца, мы рассмотрим пример.
Примером будет концепция «открыть файл». Скажем, эта концепция логически требует параметр для имени файла и, при необходимости, допускает параметры, указывающие, должен ли файл быть открыт только для чтения, для чтения-записи или только для записи, должен ли файл создаваться, если он еще не существует, должно ли место записи быть в конце («добавление») или в начале («перезапись»), размер блока, если файл должен быть создан, должен ли ввод-вывод быть буферизован или нет, размер буфера, должен ли доступ к нему быть общим или эксклюзивным, и, возможно, какие-либо другие параметры. Если бы мы реализовали эту концепцию, используя обычную функцию с позиционными параметрами, вызывающий код было бы очень трудно читать: было бы до 8 позиционных параметров, и вызывающий, вероятно, допустил бы много ошибок. Поэтому вместо этого мы используем идиому именованных параметров.
Прежде чем мы перейдем к реализации, вот как может выглядеть вызывающий код, если вы готовы принимать все параметры функции по умолчанию:
Это простой случай. Вот как может выглядеть код, если вы захотите изменить несколько параметров.
Обратите внимание, как «параметры», если их так можно назвать, расположены в случайном порядке (они не позиционны), и все они имеют имена. Таким образом, программисту не нужно запоминать порядок параметров, а имена (надеюсь) очевидны.
Остается только сделать так, чтобы конструктор класса File принимал объект OpenFile :
Поскольку каждая функция-член в цепочке возвращает ссылку, копирования объектов не происходит, и объединение в цепочку очень эффективно. Более того, если различные функции-члены являются встраиваемыми, сгенерированный код объекта, вероятно, будет соответствовать коду в стиле C, который устанавливает различные члены структуры. Конечно, если функции-члены не являются встраиваемыми, может произойти небольшое увеличение размера кода и небольшое снижение производительности (но это важно, только если создание происходит в критическом фрагменте программы, привязанном к CPU).
Это будет действительно больно; вам лучше присесть.
Это долгая история, но одно из решений (надеюсь, вы уже сидите!) – добавить дополнительную пару скобок () вокруг Bar() :
Другое решение – использовать = в вашем объявлении (смотрите написанное мелким шрифтом ниже):
Вот еще одно решение (более мелким шрифтом ниже):
А теперь самое печальное. На самом деле это жалко. Какой-нибудь бездумный бездельник пропустит этот последний абзац, а затем навяжет причудливый, неправильный, нерелевантный и просто глупый стандарт кодирования, который гласит что-то вроде: «Никогда не создавайте временные объекты с использованием конструктора по умолчанию» или «Всегда используйте = во всех инициализациях», или что-то еще столь же бессмысленное. Если это вы, пожалуйста, застрелитесь, прежде чем нанесете еще больший урон. Те, кто не понимает проблемы, не должны указывать другим, как ее решить.
(В основном это была шутка. Но в этом есть доля правды. Настоящая проблема в том, что люди склонны преклоняться перед последовательностью, и они склонны экстраполировать от неясного к общему. Это не мудро.)
Ключевое слово explicit является необязательным украшением для конструкторов и операторов преобразования, чтобы сообщить компилятору, что определенный конструктор или оператор преобразования не может использоваться для неявного преобразования выражения в тип его класса.
Например, следующий код без ключевого слова explicit корректен:
Приведенный выше код напечатает следующее:
Почему мой конструктор работает неправильно?
Это вопрос, который возникает во многих формах. Например:
По умолчанию классу предоставляется конструктор копирования и присваивание копирования, которые копируют все элементы, а также конструктор перемещения и присваивание перемещения, которые перемещают все элементы. Например:
Как этого избежать? Самое простое решение – пометить операции, которые копируются, как удаленные:
Если нам нужно копирование или перемещение, мы, конечно, можем определить правильные инициализаторы и присваивания, чтобы обеспечить желаемую семантику.