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

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

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

10 ответов

Решение

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

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

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

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

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

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

Вариант 1 несколько привлекателен тем, что чем больше я кодирую, тем реже мне нужен конструктор без параметров. Когда-нибудь я должен посчитать, как часто я на самом деле в конечном итоге использую конструктор по умолчанию...

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

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

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

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

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

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

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

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

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

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

Другое дело, что для того, чтобы 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 ключевое слово. По крайней мере, мне нужно создать частный конструктор без параметров, чтобы никакой код вызова не вызывал значение по умолчанию.

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

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

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

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

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

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

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

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

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

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

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

посылка

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

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

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

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

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

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

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

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

Заключение

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

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

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

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

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

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

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

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

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

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

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

Другие вопросы по тегам