パラメーター付きのコンストラクターを作成すると、デフォルトのパラメーターレスコンストラクターがなくなる理由


161

C#、C ++、およびJavaでは、パラメーターを受け取るコンストラクターを作成すると、パラメーターのないデフォルトのコンストラクターがなくなります。私は常にこの事実を受け入れてきましたが、今はなぜなのかと思い始めています。

この動作の理由は何ですか?「独自のコンストラクターを作成した場合、おそらくこの暗黙的なコンストラクターをぶら下げたくない」と言っているだけの「安全対策/推測」ですか?それとも、自分でコンストラクタを作成した後にコンパイラが追加できないようにする技術的な理由がありますか?


7
この動作を持つ言語のリストにC ++を追加できます。
Henk Holterman、2012

18
そしてC ++ではFoo() = default;、デフォルトの1つを取り戻すように言うことができます。
MSalters 2012

1
パラメータを持つコンストラクタがすべてのパラメータのデフォルト引数を持つことができる場合、組み込みのパラメータなしコンストラクタと競合するため、独自のコンストラクタを作成するときにコンストラクタを削除する必要があります。
Morwenn、2012

3
デフォルトのコンストラクター要件を作成する最初のコンパイラーの創設者の間のこの議論と、それが刺激したであろう白熱した議論に参加していると想像してください。
kingdango 2012

7
@HenkHolterman C ++は、この振る舞いの単なる1つではなく、その発信者であり、C の設計と進化で Stroustrupによって説明され、私の最新の回答で要約されているように、Cの互換性を可能にします。
Jon Hanna

回答:


219

独自にコンストラクタを追加した場合、コンパイラがコンストラクタを追加できなかった理由はありません-コンパイラは必要に応じてほとんど何でもできます!ただし、最も意味のあるものを確認する必要があります。

  • 私が定義されていない場合は任意の非静的クラスのコンストラクタを、私が最も可能性が高い、そのクラスをインスタンス化することができるようにしたいです。これを可能にするために、コンパイラーパラメーターなしのコンストラクターを追加する必要があります。これは、インスタンス化を許可する以外には効果がありません。つまり、機能させるためだけにコードに空のコンストラクターを含める必要はありません。
  • 独自のコンストラクター、特にパラメーター付きのコンストラクターを定義した場合、クラスの作成時に実行する必要がある独自のロジックを持っている可能性があります。この場合、コンパイラーが空のパラメーターのないコンストラクターを作成すると、誰かが私が書いたロジックをスキップできるようになり、コードがさまざまな方法で壊れる可能性があります。この場合にデフォルトの空のコンストラクターが必要な場合は、明示的に指定する必要があります。

したがって、どちらの場合でも、現在のコンパイラーの動作が、コードの意図を維持するという点で最も理にかなっていることがわかります。


2
あなたの答えの残りの部分はあなたの最初の文が間違っていることをかなり証明していると思います。
コンラートルドルフ

76
@KonradRudolph、最初の文は、コンパイラこのシナリオでコンストラクタを追加できると述べています-残りの回答は、なぜそうでないのかを説明します(質問で指定された言語の場合)
JohnL

1
うーん、ダメ。オブジェクト指向言語を最初から設計した場合、コンストラクターがないことの最も明白な意味は、「クラスの不変を保証するコンストラクターの追加を怠った」ことであり、コンパイルエラーが発生します。
Jon Hanna

70

言語このように設計されなければならない技術的な理由は確かにありません。

私が見ることができる4つのやや現実的なオプションがあります。

  1. デフォルトのコンストラクタはありません
  2. 現在のシナリオ
  3. デフォルトでは常にデフォルトのコンストラクターを提供しますが、明示的に抑制することを許可します
  4. 抑制されることを許可せずに常にデフォルトのコンストラクタ提供する

オプション1はいくぶん魅力的です。コーディングするほど、パラメーターのないコンストラクターが本当に必要になる頻度が減ります。いつの日か、実際にデフォルトのコンストラクタを使用する頻度を数える必要があります...

オプション2私は元気です。

オプション3は、残りの言語について、JavaとC#の両方のフローに反します。Javaのデフォルトよりもプライベートなものを明示的に数えない限り、明示的に「削除」するものはありません。

オプション4は恐ろしいです-あなたは絶対に特定のパラメータで建設を強制することができるようにしたいです。どういうnew FileStream()意味ですか?

したがって、基本的に、デフォルトのコンストラクターを提供することはまったく理にかなっているという前提を受け入れた場合、独自のコンストラクターを提供したらすぐにそれを抑制することは非常に理にかなっていると思います。


1
オプション3が好きです。何かを書いているときは、両方のタイプのコンストラクターが必要で、パラメーターを持つコンストラクターが必要になることが多いからです。したがって、1日1回はパラメーターなしのコンストラクターをプライベートにしてから、1日10回パラメーターなしのコンストラクターを作成します。しかし、それはおそらく私だけです。一連のシリアライズ可能なクラスを書いています...
Petr Mensik

8
@PetrMensik:パラメーターなしのコンストラクターが本当にまったく何もする必要がない場合は、「明示的な削除」ステートメントよりもはるかに多くのコードを必要としないワンライナーです。
Jon Skeet、2012

2
@PetrMensik:すみません、私の間違い、はい。それは間違いなく私の経験とは正反対です-そして私は何かを自動的に含めることもより危険なオプションであると主張します...誤ってそれを除外しないことになった場合は、不変条件を台無しにすることができますなど
ジョンスキート

2
#4はC#にstruct当てはまります。
ジェイ・バズジ

4
理解しやすいので、オプション1が好きです。コードで見ることができない「魔法の」コンストラクタはありません。オプション1を使用すると、非静的クラスにインスタンスコンストラクターがない場合(または許可しない場合でも)、コンパイラーは警告を発行します。方法:「クラス<TYPE>のインスタンスコンストラクターが見つかりません。クラスを静的に宣言するつもりでしたか?」
Jeppe Stig Nielsen 2012

19

編集。実際、最初の答えで私が言ったことは有効ですが、これが本当の理由です。

最初はCがありました。Cはオブジェクト指向ではありません(オブジェクト指向のアプローチをとることはできますが、それはあなたを助けたり、何かを強制したりしません)。

その後、C With Classesがあり、後にC ++に名前が変更されました。C ++はオブジェクト指向であるため、カプセル化を促進し、オブジェクトの不変を保証します。構築時、およびメソッドの最初と最後では、オブジェクトは有効な状態です。

これを行うための自然なことは、クラスが有効な状態で開始することを保証するために、常にコンストラクターを持たなければならないことを強制することです-コンストラクターがこれを保証するために何もする必要がない場合、空のコンストラクターがこの事実を文書化します。

しかし、C ++の目標は、可能な限りすべての有効なCプログラムが有効なC ++プログラムでもあるという点でCと互換性を持つことでした(もはやアクティブな目標ではなく、CからC ++への進化により、もはや保持されなくなりました。 )。

この1つの影響は、 structとのclass。前者はCの方法(デフォルトではすべて公開)で行い、後者は適切なOOの方法で行います(デフォルトではすべて非公開です。開発者は公開したいものを積極的に公開します)。

もう1つは、C structにはコンストラクターがないためにコンストラクターを持つことができなかったCがC ++で有効であるためには、C ++の見方ではこれに意味がなければなりませんでした。したがって、コンストラクターがないと、不変条件を積極的に保証するというOOの慣行に反することになりますが、C ++はこれを、本体が空のように動作するデフォルトのパラメーターなしのコンストラクターがあることを意味すると解釈しました。

すべてのC structsが有効なC ++ structsになりました(つまり、C ++と同じでした)classesと、すべて-メンバーと継承-パブリック)。外部から、単一のパラメーターなしのコンストラクターがあるかのように扱われました。

ただし、コンストラクタをclassまたはに配置した場合はstruct、Cの方法ではなくC ++ / OOの方法で処理を行っていたため、デフォルトのコンストラクタは必要ありませんでした。

それは省略形として機能したため、互換性が他の方法では不可能だった場合でも人々はそれを使い続けました(Cにない他のC ++機能を使用していました)。

したがって、Javaが登場したとき(さまざまな方法でC ++に基づいて)、後でC#(さまざまな方法でC ++とJavaに基づいて)が生まれたとき、彼らはこのアプローチを、プログラマーがすでに慣れている可能性があるため維持しました。

Stroustrupはこれについて彼のC ++プログラミング言語で書いています。C++ のデザインと進化における言語の「なぜ」にもっと焦点を当てています

===元の回答===

これが起こらなかったとしましょう。

パラメーターなしのコンストラクターが必要でないとしましょう。コンストラクターがないとクラスを意味のある状態にすることができないためです。実際、これはstructC#で発生する可能性のあるものです(ただし、C#ですべてゼロとnullを意味のある方法で使用できないstruct場合は、非公開の最適化を使用することをお勧めします。の使用に関する設計上の欠陥struct)。

クラスがその不変条件を保護できるようにするには、特別なremoveDefaultConstructorキーワードが必要です。少なくとも、呼び出しコードがデフォルトを呼び出さないようにするために、パラメーターなしのプライベートコンストラクターを作成する必要があります。

これは言語をさらに複雑にします。それをしない方がいい。

全体として、コンストラクタを追加することでデフォルトを削除するのではなく、何もしないパラメータのないコンストラクタを追加するための構文上の砂糖として、コンストラクタがないことを考えるのが最善です。


1
それでも、誰かがこれは反対票を投じるには不十分な答えだと感じているのに、私や他の誰かを啓蒙することに煩わされることはありませんでした。どちらが役に立つでしょう。
ジョンハンナ

私の答えも同じです。ええと、ここには何も問題はないので、+ 1してください。
Botz3000 2012

@ Botz3000スコアは気にしませんが、批評があれば読んでみます。それでも、上記に追加することを考えさせられました。
Jon Hanna

1
そして、何も説明せずに反対投票で。説明が必要ないほど明白なものがない場合は、私が愚かであると仮定して、とにかくそれを説明するようにしてください。
ジョンハンナ

1
@jogojapan私もエキスパートではありませんが、決定を下した人であるということについて書いたので、私はそうする必要はありません。ちなみに、とても面白い本です。低レベルの技術についてはほとんどなく、設計上の決定についてはたくさんあります。新しい機能を提案する前に腎臓を寄付する必要があると言っている1人のスピーチのような面白い言葉(あなたは非常に難しいと考え、2回だけ行う)が彼のスピーチの直前に両方のテンプレートと例外を紹介します。
Jon Hanna

13

オブジェクトの作成を制御するために自分で何もしない場合は、デフォルトのパラメーターなしのコンストラクターが追加されます。制御を取得するための単一のコンストラクターを作成すると、コンパイラーは「バックオフ」し、完全に制御できるようになります。

これがそうでない場合、オブジェクトをパラメーター付きのコンストラクターでのみ構築可能にする場合は、デフォルトのコンストラクターを無効にする明示的な方法が必要になります。


あなたは実際にそのオプションを持っています。パラメーターなしのコンストラクターをプライベートにします。
mw_21

5
それは同じではありません。静的メソッドを含む、クラスの任意のメソッドがそれを呼び出すことができます。私はそれが完全に存在しないことを好みます。
アンデルスアベル

3

コンパイラの便利な機能です。パラメーター付きのコンストラクターを定義するが、パラメーターなしのコンストラクターは定義しない場合、パラメーターなしのコンストラクターを許可したくない可能性がはるかに高くなります。

これは、空のコンストラクターで初期化しても意味がない多くのオブジェクトの場合です。

それ以外の場合は、制限するクラスごとに、パラメーターのないプライベートコンストラクターを宣言する必要があります。

私の意見では、機能するためにパラメーターを必要とするクラスにパラメーターなしのコンストラクターを許可するのは良いスタイルではありません。


3

他のコンストラクタを定義していないのであれば、なぜデフォルトコンストラクタを宣言する必要がないのでしょうか。

非静的クラスにはコンストラクターが必須です。
したがって、コンストラクタを定義していない場合、生成されたデフォルトコンストラクタはC#コンパイラの便利な機能にすぎません。また、コンストラクタがないとクラスは有効になりません。したがって、何もしないコンストラクタを暗黙的に生成しても問題はありません。周りに空のコンストラクタを置くよりも、明らかにきれいに見えます。

コンストラクタをすでに定義している場合、クラスは有効です。なぜコンパイラがデフォルトのコンストラクタが必要だと想定する必要があるのでしょうか。必要ない場合はどうしますか?属性を実装して、コンパイラーにデフォルトのコンストラクターを生成しないように指示しますか?それは良い考えではないと思います。


1

デフォルトのコンストラクタは、クラスにコンストラクタがない場合にのみ作成できます。コンパイラは、これをバックアップメカニズムとしてのみ提供するように記述されています。

パラメータ化されたコンストラクタがある場合、デフォルトのコンストラクタを使用してオブジェクトを作成したくない場合があります。コンパイラーがデフォルトのコンストラクターを提供した場合、引数なしでオブジェクトが作成されないようにするには、引数なしのコンストラクターを作成してそれをプライベートにする必要がありました。

また、デフォルトのコンストラクターを無効にすること、または「プライベート」にすることを忘れ、それによって潜在的な機能エラーをキャッチすることが困難になる可能性が高くなります。

また、オブジェクトをデフォルトの方法で作成するか、パラメータを渡して作成する場合は、引数なしのコンストラクタを明示的に定義する必要があります。これは厳密にチェックされており、コンパイラはそれ以外の場合は文句を言うため、ここで抜け穴はありません。


1

前提

この振る舞いは、クラスがデフォルトのパラメーターなしのパブリックコンストラクターを持つという決定の自然な拡張と見なすことができます。尋ねられた質問に基づいて、私たちはこの決定を前提とし、この場合は質問しないと仮定します。

デフォルトのコンストラクターを削除する方法

したがって、デフォルトのパラメーターなしのパブリックコンストラクターを削除する方法が必要です。この削除は、次の方法で実行できます。

  1. 非公開のパラメーターなしコンストラクターを宣言する
  2. パラメータを持つコンストラクタが宣言されたときに、パラメータのないコンストラクタを自動的に削除します
  3. パラメーターなしのコンストラクターを削除するようコンパイラーに指示するいくつかのキーワード/属性(簡単に除外できるほど厄介です)

最適なソリューションの選択

次に、自分自身に問いかけます。パラメータのないコンストラクタがない場合、何に置き換える必要がありますか?そして、どのような種類のシナリオで、デフォルトのパラメーターなしのパブリックコンストラクターを削除しますか?

物事は所定の位置に収まり始めます。まず、パラメータ付きのコンストラクタ、または非パブリックコンストラクタに置き換える必要があります。次に、パラメーターなしのコンストラクターが不要なシナリオは次のとおりです。

  1. クラスをインスタンス化したくない、またはコンストラクターの可視性を制御したい:非パブリックコンストラクターを宣言する
  2. 構築時にパラメーターを強制的に提供したい:パラメーターを使用してコンストラクターを宣言する

結論

C#、C ++、およびJavaがデフォルトのパラメータなしのパブリックコンストラクタを削除できるようにする2つの方法があります。


明確に構造化され、わかりやすい+1。しかし、上記(3.)に関して:コンストラクターの削除のための特別なキーワードはそれほど厄介なアイデアだとは思わず、実際にC ++ 11が= deleteこの目的のために導入しました。
jogojapan 2012

1

これはコンパイラによって処理されると思います。で.netアセンブリを開くILDASMと、コード内にない場合でも、デフォルトのコンストラクタが表示されます。パラメータ化されたコンストラクタを定義する場合、デフォルトのコンストラクタは表示されません。

実際、クラス(静的でない)を定義すると、コンパイラーはこの機能を提供し、インスタンスを作成するだけだと考えます。また、特定の操作を実行したい場合は、必ず独自のコンストラクターを使用します。


0

これは、コンストラクタを定義しない場合、コンパイラは引数を取らないコンストラクタを自動的に生成するためです。コンストラクターにもっと何かが必要な場合は、オーバーライドします。これは関数のオーバーロードではありません。そのため、コンパイラが現在見る唯一のコンストラクタは、引数を取るコンストラクタです。この問題に対処するために、コンストラクターが値なしで渡された場合にデフォルト値を渡すことができます。


0

クラスにはコンストラクタが必要です。これは必須の要件です。

  • 作成しない場合は、パラメータなしのコンストラクタが自動的に提供されます。
  • パラメータなしのコンストラクタが必要ない場合は、独自のコンストラクタを作成する必要があります。
  • パラメーターなしのコンストラクターとパラメーターベースのコンストラクターの両方が必要な場合は、手動で追加できます。

私は別の質問であなたに答えますが、なぜデフォルトのパラメータレスコンストラクタが常に必要なのですか?これが望ましくない場合があるため、開発者は必要に応じて追加または削除するコントロールを持っています。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.