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

161

В C #, C ++ и Java, когда вы создаете конструктор, принимающий параметры, стандартный параметр без параметров исчезает. Я всегда только принимал этот факт, но теперь я начал задаваться вопросом, почему.

В чем причина такого поведения? Это просто «мера безопасности / догадка», говорящая «Если вы создали собственный конструктор, вы, вероятно , не хотите, чтобы этот неявный зависал»? Или у него есть техническая причина, которая делает невозможным его добавление компилятором, если вы сами создали конструктор?

olagjo
источник
7
Вы можете добавить C ++ в список языков с таким поведением.
Хенк Холтерман
18
А в C ++ теперь можно сказать, Foo() = default;чтобы получить обратно по умолчанию.
MSalters
1
Если ваш конструктор с параметрами может иметь аргументы по умолчанию для всех своих параметров, то он будет конфликтовать со встроенным конструктором без параметров, следовательно, необходимо удалить его при создании собственного.
Morwenn
3
Представьте, что вы присутствовали на этой дискуссии между основателями первого компилятора, чтобы сделать требование конструктора по умолчанию, и горячим обсуждением, которое оно могло бы вдохновить.
кингданго
7
@HenkHolterman C ++ - это не просто еще один с таким поведением, а создатель, и он должен обеспечивать совместимость с C, как обсуждалось Страуструпом в Проекте и Эволюции C ++ и как резюмировано в моем обновленном ответе.
Джон Ханна

Ответы:

219

Нет никаких причин, по которым компилятор не мог бы добавить конструктор, если вы добавили свой собственный - компилятор мог делать практически все, что ему нужно! Тем не менее, вы должны посмотреть на то, что имеет больше смысла:

  • Если я не определил никакого конструктора для нестатического класса, я, скорее всего, хочу иметь возможность создать экземпляр этого класса. Чтобы это допустить, компилятор должен добавить конструктор без параметров, который не будет иметь никакого эффекта, кроме как разрешить создание экземпляров. Это означает, что мне не нужно включать пустой конструктор в мой код только для того, чтобы он работал.
  • Если я определил свой собственный конструктор, особенно конструктор с параметрами, то, скорее всего, у меня есть собственная логика, которая должна выполняться при создании класса. Если бы в этом случае компилятор создал пустой конструктор без параметров, он позволил бы кому-то пропустить написанную мной логику, что могло бы привести к поломке моего кода разными способами. Если в этом случае мне нужен пустой конструктор по умолчанию, я должен сказать об этом явно.

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

Дэн Пьюзи
источник
2
Я думаю, что остальная часть вашего ответа в значительной степени доказывает, что ваше первое предложение неверно.
Конрад Рудольф
76
@KonradRudolph, первое предложение говорит, что компилятор может добавить конструктор в этом сценарии - остальная часть ответа объясняет, почему это не так (для языков, указанных в вопросе)
JohnL
1
Ну нет. Если бы мы разработали ОО-язык с нуля, самым очевидным смыслом отсутствия конструктора является то, что «вы забыли добавить конструктор, который обеспечивает класс« инвариант »», и это вызовет ошибку компиляции.
Джон Ханна
70

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

Я вижу четыре реалистичных варианта:

  1. Нет конструкторов по умолчанию вообще
  2. Текущий сценарий
  3. Всегда предоставляя конструктор по умолчанию, но позволяя его явно подавить
  4. Всегда предоставлять конструктор по умолчанию, не допуская его подавления

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

Вариант 2 У меня все хорошо.

Вариант 3 идет против потока как Java, так и C #, для остальной части языка. Нет ничего, что вы явно «удаляете», если только вы не рассчитываете сделать вещи более закрытыми, чем они были бы по умолчанию в Java.

Вариант 4 ужасен - вы абсолютно хотите иметь возможность форсировать конструкцию с определенными параметрами. Что бы new FileStream()вообще значило?

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

Джон Скит
источник
1
Мне нравится вариант 3, потому что, когда я пишу что-то, мне нужно чаще иметь оба типа конструкторов, чем просто конструктор с параметром. Поэтому я бы предпочел один раз в день сделать приватный конструктор без параметров, а затем писать конструкторы без параметров 10 раз в день. Но это, вероятно, только я, я пишу много сериализуемых классов ...
Петр Менсик
8
@PetrMensik: Если ваш конструктор без параметров действительно ничего не должен делать, это однострочная строка, которая, конечно же, не потребует гораздо больше кода, чем оператор «явного удаления».
Джон Скит
2
@PetrMensik: Извините, моя ошибка, да. Это определенно противоположно моему опыту - и я бы сказал, что автоматическое включение чего-либо также является более опасным вариантом ... если вы случайно не исключите это, вы можете испортить свои инварианты и т. Д.
Джон Скит
2
# 4 в случае с C # struct, к лучшему или к худшему.
Джей Базузи
4
Мне нравится вариант 1, потому что его легче понять. Нет "волшебного" конструктора, который вы не видите в коде. С опцией 1 компилятор должен выдавать предупреждение, когда (или, возможно, даже запретить?) У нестатического класса нет конструкторов экземпляров. Как насчет: «Не найдено конструкторов экземпляров для класса <TYPE>. Вы хотели объявить класс статическим?»
Джеппе Стиг Нильсен
19

Редактировать. На самом деле, хотя то, что я говорю в своем первом ответе, действительно, это реальная причина.

Вначале был C. С не объектно-ориентированный (вы можете использовать ОО-подход, но он вам не помогает и ничего не обеспечивает).

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

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

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

Одним из эффектов этого было дублирование функциональности между structи class. Первые делают вещи C-способом (все общедоступно по умолчанию), а вторые делают вещи хорошим способом OO (все приватно по умолчанию, разработчик активно публикует то, что они хотят публично).

Другая причина состоит в том, что для того, чтобы C struct, у которого не могло быть конструктора, потому что у C нет конструкторов, был бы действителен в C ++, для этого должен был быть смысл в подходе C ++. И поэтому, хотя отсутствие конструктора противоречило бы ОО-практике активного обеспечения инварианта, C ++ принял это, чтобы означать, что был конструктор по умолчанию без параметров, который вел себя так, как если бы он имел пустое тело.

Все C structsтеперь были действительными C ++ structs(что означало, что они были такими же, как C ++ classesсо всем - члены и наследование - общедоступными), которые рассматривались извне, как если бы у них был один конструктор без параметров.

Однако, если вы поместили конструктор в classили struct, то вы делали это способом C ++ / OO, а не способом C, и не было необходимости в конструкторе по умолчанию.

Так как это служило сокращением, люди продолжали использовать его, даже если совместимость не была возможна иначе (он использовал другие функции C ++, не в C).

Следовательно, когда появилась Java (основанная на C ++ во многих отношениях), а затем и C # (основанная на C ++ и Java по-разному), они сохранили этот подход, поскольку кодеры уже могут быть использованы.

Страуструп пишет об этом на своем языке программирования C ++ и тем более, уделяя больше внимания «почему» языка в «Проектировании и эволюции C ++» .

=== Оригинальный ответ ===

Допустим, этого не произошло.

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

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

Что еще больше усложняет язык. Лучше не делать этого.

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

Джон Ханна
источник
1
И опять же, я вижу, что кто-то считает, что это достаточно плохой ответ, чтобы проголосовать за него, но не может быть обеспокоен тем, чтобы просветить меня или кого-то еще. Что может быть полезно.
Джон Ханна
То же самое на мой ответ. Ну, я не вижу ничего плохого здесь, так что +1 от меня.
Botz3000
@ Botz3000 Меня не волнует оценка, но если у них есть критика, я бы лучше прочитал ее. Тем не менее, это заставило меня задуматься над тем, что можно добавить к вышесказанному.
Джон Ханна
1
И снова с понижением голосов без объяснения причин. Пожалуйста, если я упускаю что-то настолько очевидное, что это не требует объяснения, просто предположите, что я глуп, и все равно сделайте мне одолжение, чтобы объяснить это.
Джон Ханна
1
@jogojapan Я тоже не эксперт, но парень, который - будучи парнем, который принял решение - написал об этом, так что я не должен быть. Кстати, это очень интересная книга; немного о низкоуровневых технологиях и много о дизайнерских решениях, с забавными частями вроде речи одного человека, говорящей о том, что вам следует пожертвовать почку, прежде чем предлагать новую функцию (вы очень сильно подумаете, и сделаете это только дважды) прямо перед его речью введение как шаблонов, так и исключений.
Джон Ханна
13

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

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

Андерс Абель
источник
У вас действительно есть такая возможность. Сделать конструктор без параметров закрытым.
mw_21
5
Это не то же самое. Любой метод класса, включая статические, может вызывать его. Я предпочитаю иметь его полностью несуществующим.
Андерс Абель
3

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

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

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

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

mw_21
источник
3

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

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

Если вы уже определили конструктор, ваш класс действителен, так почему компилятор должен считать, что вам нужен конструктор по умолчанию? Что делать, если вы не хотите один? Реализовать атрибут, чтобы сказать компилятору не генерировать конструктор по умолчанию? Я не думаю, что это было бы хорошей идеей.

Botz3000
источник
1

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

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

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

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

пепел
источник
1

посылка

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

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

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

  1. Объявить непубличный конструктор без параметров
  2. Автоматически удалять конструктор без параметров при объявлении конструктора с параметрами
  3. Некоторое ключевое слово / атрибут, указывающее компилятору удалить конструктор без параметров (достаточно неудобно, что его легко исключить)

Выбор лучшего решения

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

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

  1. Мы не хотим, чтобы экземпляр класса создавался вообще, или мы хотим контролировать видимость конструктора: объявлять непубличный конструктор
  2. Мы хотим, чтобы параметры были предоставлены на конструкцию: объявить конструктор с параметрами

Вывод

Там у нас это есть - именно два способа, которыми C #, C ++ и Java позволяют удалить открытый конструктор по умолчанию без параметров.

Зайд Масуд
источник
Четко структурированный и понятный, +1. Но относительно (3.) выше: я не думаю, что специальное ключевое слово для удаления конструктора является такой неловкой идеей, и на самом деле C ++ 11 ввел = deleteдля этой цели.
Джогожапан
1

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

На самом деле, когда вы определяете класс (не статический), компилятор предоставляет эту возможность, думая, что вы будете просто создавать экземпляр. И если вы хотите, чтобы какая-то конкретная операция выполнялась, у вас наверняка будет собственный конструктор.

PQubeTechnologies
источник
0

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

Вивек Прадхан
источник
0

Классу нужен конструктор. Это обязательное требование.

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

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

Jaider
источник