Чому конструктор без параметрів за замовчуванням відходить, коли ви створюєте його з параметрами


161

У C #, C ++ та Java, коли ви створюєте конструктор, що приймає параметри, без параметри за замовчуванням відходить. Я завжди просто приймав цей факт, але тепер почав цікавитись, чому.

У чому причина такої поведінки? Це просто "міра безпеки / здогадка", яка говорить: "Якщо ви створили власний конструктор, ви, мабуть , не хочете, щоб цей неявний підвісний"? Або у нього є технічна причина, яка унеможливлює компілятор додати його, коли ви створили конструктор самостійно?


7
Ви можете додати C ++ до списку мов, які мають таку поведінку.
Хенк Холтерман

18
А в C ++ тепер можна сказати, Foo() = default;щоб повернути типовий.
MSalters

1
Якщо ваш конструктор з параметрами може мати аргументи за замовчуванням для всіх його параметрів, він би конфліктував із вбудованим конструктором без параметрів, отже, потрібно видалити його, коли ви створюєте свій власний.
Морвен

3
Уявіть, що ви присутні в цій дискусії між засновниками першого компілятора, щоб викласти вимогу конструктора за замовчуванням, і бурхливою дискусією, яку вона надихнула.
kingdango

7
@HenkHolterman C ++ - це не просто інша поведінка, а розробник, і це дозволити сумісність з C, про яку розповідав Stroustrup в «Дизайні та еволюції C ++» та як узагальнено в моїй оновленій відповіді.
Джон Ханна

Відповіді:


219

Немає причини, що компілятор не міг би додати конструктор, якщо ви додали свій власний - компілятор міг би робити майже все, що завгодно! Однак ви повинні подивитися на те, що має найбільше значення:

  • Якщо я не визначив жодного конструктора для нестатичного класу, я, швидше за все, хочу мати можливість інстанціювати цей клас. Щоб дозволити це, компілятор повинен додати конструктор без параметрів, який не матиме жодного ефекту, окрім дозволу інстанції. Це означає, що мені не потрібно включати порожній конструктор у свій код лише для того, щоб він працював.
  • Якщо я визначив власний конструктор, особливо один з параметрами, я, швидше за все, маю власну логіку, яку необхідно виконати при створенні класу. Якби компілятор створив порожній, безпараметричний конструктор у цьому випадку, це дозволить комусь пропустити написану мною логіку, що може призвести до порушення мого коду всіма способами. Якщо я хочу в такому випадку пустий конструктор за замовчуванням, мені це потрібно сказати прямо.

Отже, в кожному конкретному випадку ви можете бачити, що поведінка поточних компіляторів має найбільш сенс з точки зору збереження ймовірного наміру коду.


2
Я думаю, що решта вашої відповіді в значній мірі доводить ваше перше речення неправильним.
Конрад Рудольф

76
@KonradRudolph, у першому реченні сказано, що компілятор міг би додати конструктор у цьому сценарії - решта відповіді пояснює, чому це не відбувається (для мов, зазначених у питанні)
JohnL

1
Ну ні. Якщо ми розробили мову OO з нуля, найбільш очевидним сенсом відсутності конструктора є ", ви нехтували додаванням конструктора, який забезпечує клас" інваріантним ", і це призведе до помилки компіляції.
Джон Ханна

70

Там, звичайно , не технічна причина , чому мова має бути розроблений таким чином.

Я бачу чотири реалістичні варіанти:

  1. Конструкторів за замовчуванням взагалі немає
  2. Нинішній сценарій
  3. Завжди надавати конструктор за замовчуванням за замовчуванням, але дозволяючи його явно придушувати
  4. Завжди надайте конструктор за замовчуванням, не допускаючи його придушення

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

Варіант 2 Мені добре.

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

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

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


1
Мені подобається варіант 3, тому що коли я щось пишу, мені потрібно частіше мати обидва типи конструкторів, то просто конструктор з параметром. Тому я вважаю за краще один раз на день робити якийсь конструктор без параметрів приватним, а потім писати 10 разів на день конструктори без параметрів. Але це, мабуть, тільки я, я пишу багато серіазних занять ...
Петро Менсик

8
@PetrMensik: Якщо вашому безпараметричному конструктору справді нічого не потрібно робити, це одноклапник, який, безумовно, не забирає набагато більше коду, ніж заява про "явне видалення".
Джон Скіт

2
@PetrMensik: Вибачте, моя помилка, так. Це, безумовно, протилежне моєму досвіду - і я заперечую, що включення чогось автоматично є і більш небезпечним варіантом ... якщо ви випадково виявитесь не виключаючими, ви можете накрутити інваріантів тощо.
Джон Скіт

2
№4 - це справа з C # structs, для кращого або гіршого.
Джей Базузі

4
Мені подобається варіант 1, тому що його легше зрозуміти. Жодного "магічного" конструктора, який ви не бачите в коді. У варіанті 1 компілятор повинен видавати попередження, коли (або, можливо, навіть заборонити?) Нестатичний клас не має конструкторів екземплярів. Як щодо: "Не знайдено конструкторів примірників для класу <TYPE>. Ви мали намір оголосити клас статичним?"
Jeppe Stig Nielsen

19

Редагувати. Насправді, хоча те, що я кажу в першій своїй відповіді, справедливо, це справжня причина:

На початку був C. 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з усіма - членами та спадщиною - громадськими), які розглядаються ззовні так, ніби вони мають єдиний безпараметричний конструктор.

Якщо ви все-таки поставили конструктор в a classабо struct, то ви робили речі C ++ / OO, а не C, і не було потреби в конструкторі за замовчуванням.

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

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

Про це пише Stroustrup у своїй мові програмування на C ++, а ще більше, приділяючи більше уваги "whys" мови в "Дизайні та еволюції C ++" .

=== Оригінальний відповідь ===

Скажімо, цього не сталося.

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

Щоб мій клас міг захистити своїх інваріантів, мені потрібно спеціальне removeDefaultConstructorключове слово. Як мінімум, мені потрібно створити приватний конструктор без параметрів, щоб переконатися, що жоден викликовий код не закликає за замовчуванням.

Що ще більше ускладнює мову. Краще не робити цього.

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


1
І все-таки знову, я бачу, хтось вважає, що це досить погана відповідь, щоб проголосувати за це, але не могло потурбуватися просвітником мене чи когось іншого. Що може бути, знаєте, корисним.
Джон Ханна

Те саме на мою відповідь. Ну я не бачу тут нічого поганого, тому +1 від мене.
Botz3000

@ Botz3000 Мене не хвилює оцінка, але якщо у них є критика, я б краще її прочитав. І все-таки це змусило мене придумати щось додати до сказаного.
Джон Ханна

1
І знову з голосуванням без будь-яких пояснень. Будь ласка, якщо я пропускаю щось настільки очевидне, що воно не потребує пояснень, просто припустіть, що я дурний, і зробіть мені прихильність пояснити це все одно.
Джон Ханна

1
@jogojapan Я також не експерт, але хлопець, який є - тим, що був хлопцем, який прийняв рішення - написав про це, тому мені не треба бути. До речі, це дуже цікава книга; мало на низькому рівні техніки та багато дизайнерських рішень, із кумедними бітами, як-от промова однієї людини, яка говорить про те, що вам доведеться пожертвувати ниркою, перш ніж запропонувати нову функцію (ви б подумали дуже важко, і зробите це лише двічі) прямо перед його виступом представляючи і шаблони, і винятки.
Джон Ханна

13

Конструктор без параметрів за замовчуванням додається, якщо ви нічого не зробите самостійно, щоб взяти під контроль створення об'єктів. Після того, як ви створили єдиний конструктор, щоб взяти під контроль, компілятор "відступає" і дасть вам повний контроль.

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


У вас є такий варіант. Зробити приватний конструктор без параметрів.
mw_21

5
Це не те саме. Будь-який метод класу, включаючи статичні методи, може його назвати. Я вважаю за краще це абсолютно неіснуюче.
Андерс Абель

3

Це зручність функції компілятора. Якщо ви визначаєте Конструктор з параметрами, але не визначаєте конструктор без параметрів, можливість того, що ви не хочете дозволити конструктор без параметрів, набагато вище.

Це стосується багатьох об'єктів, які просто не мають сенсу ініціалізувати з порожнім конструктором.

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

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


3

Я думаю, що питання має бути навпаки: чому б вам не потрібно було оголосити конструктор за замовчуванням, якщо ви не визначили жодних інших конструкторів?

Конструктор є обов'язковим для нестатичних класів.
Отже, я думаю, якщо ви не визначили жодних конструкторів, створений конструктор за замовчуванням - просто зручна функція компілятора C #, а також ваш клас не був би дійсним без конструктора. Тож нічого поганого в тому, що неявно генерується конструктор, який нічого не робить. Це, звичайно, виглядає чистіше, ніж порожні конструктори навколо.

Якщо ви вже визначили конструктор, ваш клас є дійсним, тож чому компілятор повинен вважати, що ви хочете конструктор за замовчуванням? Що робити, якщо ви цього не хочете? Реалізувати атрибут, щоб сказати компілятору не генерувати цей конструктор за замовчуванням? Я не думаю, що це було б гарною ідеєю.


1

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

Якщо у вас параметризований конструктор, ви, можливо, не хочете, щоб об’єкт створювався за допомогою конструктора за замовчуванням. Якби компілятор надав конструктор за замовчуванням, вам довелося б написати конструктор no-arg і зробити його приватним, щоб запобігти створенню об'єктів без аргументів.

Також більше шансів на те, що ви забудете відключити або "приватизуєте" конструктор за замовчуванням, і тим самим викличете потенційну функціональну помилку, яку важко зрозуміти.

А тепер ви повинні чітко визначити конструктор no-arg, якщо ви хочете, щоб об’єкт створювався або за замовчуванням, або шляхом передачі параметрів. Це суворо перевірено, і компілятор скаржиться інакше, тим самим забезпечуючи відсутність лазівки тут.


1

Приміщення

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

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

Звідси випливає, що повинен бути спосіб видалити загальнодоступний конструктор без параметрів за замовчуванням. Видалення може бути здійснено наступними способами:

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

Вибір найкращого рішення

Тепер ми запитуємо себе: якщо немає конструктора без параметрів, на що його слід замінити? та За якими типами сценаріїв ми хочемо видалити загальнодоступний конструктор без параметрів?

Речі починають падати на свої місця. По-перше, він повинен бути замінений на конструктор з параметрами, або на непублічний конструктор. По-друге, сценарії, за якими ви не хочете конструктор без параметрів, є:

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

Висновок

У нас це є - саме два способи, за допомогою яких C #, C ++ і Java дозволяють видалити загальнодоступний конструктор без параметрів за замовчуванням.


Чітко структурований і простий у дотриманні, +1. Але щодо (3.) вище: я не думаю, що спеціальне ключове слово для видалення конструктора є такою незручною ідеєю, і насправді C ++ 11 запровадив = deleteдля цього.
jogojapan

1

Я думаю, що цим займається компілятор. Якщо ви відкриєте .netзбірку, ILDASMви побачите конструктор за замовчуванням, навіть якщо його немає в коді. Якщо ви визначите параметризований конструктор, конструктор за замовчуванням не відображатиметься.

Насправді, коли ви визначаєте клас (нестатичний), компілятор надає цю функцію, думаючи, що ви будете просто створювати екземпляр. І якщо ви хочете виконати якусь конкретну операцію, ви неодмінно матимете власний конструктор.


0

Це тому, що коли ви не визначаєте конструктор, компілятор автоматично генерує для вас конструктор, який не приймає жодних аргументів. Коли ви хочете чогось більшого вийти з конструктора, ви пересунете його. Це НЕ функція перевантаження. Тож єдиний конструктор, який зараз бачить компілятор, - це ваш конструктор, який бере аргумент. Щоб вирішити цю проблему, ви можете передати значення за замовчуванням, якщо конструктор передається без значення.


0

Класу потрібен конструктор. Це обов'язкова вимога.

  • Якщо ви не створите його, конструктор без параметрів буде наданий вам автоматично.
  • Якщо ви не хочете конструктор без параметрів, вам потрібно створити свій власний.
  • Якщо вам потрібні конструктор без параметрів і конструктор на основі параметрів, ви можете додати їх вручну.

Я відповім на ваше запитання з іншим, чому ми завжди хочемо конструктор без параметрів за замовчуванням? є випадки, коли це не бажано, тому розробник має керування додати або видалити його, як потрібно.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.