オブジェクトの作成にコンストラクターが必要なのはなぜですか? コンストラクターを定義しなくても、デフォルトのコンストラクターが生成されます..しかし、なぜコンストラクターが必要なのですか?
5 に答える
コンストラクタなしでオブジェクトを作成できないのはなぜですか?
これは、動作に関する実際の議論というよりも、用語に関する議論です。おっしゃるとおり、構築中に何もすることがない場合もあるので、コンストラクタは必要ないはずですよね?まあ、何もしないコンストラクタである自明なコンストラクタの概念があります。標準ドキュメントのために、現在「コンストラクター」とだけ記載されているすべての場所ですべてのケースと例外を提供する必要があるよりも、すべてを (おそらく些細な) コンストラクターを持つものとして扱う方が簡単です。
「コンストラクター」のすべての使用は、「型に仮想関数またはベースがなく、コンストラクターの生成を必要とするメンバーがない場合、コンストラクターまたは何もない」に置き換える必要があることを考慮してください。
これは、定義上何もオーバーライドしない階層内の最初の仮想関数であっても、すべての仮想関数がオーバーライド機能と呼ばれるのと同じ理由です。この形式の一般化により、言語の解釈が少し簡単になりますが、初期化に関するセクション 8.5 が単純であると主張する人はあまり多くありません...
また、すべてのユーザー定義型には定義上コンストラクターがありますが、コンストラクターはオブジェクトの作成には必要ありません。特に、trivial-constructorを持つオブジェクトの場合、オブジェクトのメモリが割り当てられたときにライフタイムが開始されます (標準では、trivialは何もしないことを意味することを知っているため、その場合にコンストラクターの実行を要求するホップを通過することさえありません。
3.8 [基本寿命]/1
オブジェクトの有効期間は、オブジェクトのランタイム プロパティです。オブジェクトがクラスまたは集約型であり、それまたはそのメンバーの 1 つが自明なデフォルト コンストラクター以外のコンストラクターによって初期化される場合、そのオブジェクトは非自明な初期化を持つと言われます。[ 注: 単純なコピー/移動コンストラクターによる初期化は、非自明な初期化です。— 終わりの注 ] 型 T のオブジェクトの有効期間は、次の時点で始まります。
-- タイプ T に適切な位置合わせとサイズのストレージが取得されます。
-- オブジェクトに重要な初期化がある場合、その初期化は完了しています。
その 2 番目の箇条書きは (C++03) を読んでいた: T が非自明なコンストラクター (12.1) を持つクラス型である場合、コンストラクター呼び出しは完了した。これは、コンストラクターを実行する必要がないことをより明確に述べています。しかし、新しい文言は基本的に同じ方法で意図を表現しています。オブジェクトに非自明な初期化がある場合のみ、初期化を完了する必要があります。自明なコンストラクタ(自明な初期化) を持つオブジェクトの場合、ストレージを割り当てるとオブジェクトが作成されます。どこが重要ですか?
struct T { int x; }; // implicitly defined trivial-constructor
T *p = static_cast<T*>(malloc(sizeof *p));
// *p is alive at this point, no need to do
T *q;
{ void *tmp = malloc(sizeof T);
q = new (tmp) T; // call constructor
}
ある意味で、これは少し哲学的な質問です。コンストラクターは、初期化されていないメモリをオブジェクトに変換するサブルーチンと考えることができます。または、初期化を追跡、記述、および理解を容易にする言語機能と考えることができます。オブジェクトの作成にコンストラクターが必要なのはなぜですか? ある意味で、それがオブジェクトの作成であるからです。コンストラクターがない場合、作成しているものはオブジェクトではありません。
特定のコンストラクターが何もしない可能性がありますが、それはそのクラスの実装の詳細です。すべてのクラスにコンストラクターがあるということは、必要な初期化がクラスによってカプセル化されていることを意味します。クラスを安全に使用するために、コンストラクターが何かを行うかどうかを知る必要はありません。実際、継承、vtables、デバッグ追跡、およびその他のコンパイラ機能が存在する場合、コンストラクターが何かを行うかどうかさえわからない場合があります。(C++ では、一部のクラスを POD 型と呼ぶことでこれが少し複雑になりますが、何かが POD 型であることを知る必要がない限り、カプセル化は保持されます。)
コンストラクターの呼び出しは、オブジェクトが作成されるポイントを定義します。言語のセマンティクスに関しては、コンストラクターが終了すると、構築されたオブジェクトが存在します。それ以前は、オブジェクトは存在せず、存在するかのように使用するとエラーになります。これが、構築順序(つまり、メンバー オブジェクトと基底クラスのサブオブジェクト コンストラクターが呼び出される順序) が C++ および同様の言語で非常に重要である理由です。コンストラクターが例外をスローできる場合は、それらのオブジェクトを正確に破棄する必要があります。構築されました。
最終的に機能するプログラムを作成するには、プログラマ、ソース コードを理解しようとする人、コンパイラとリンカ、ランタイム ライブラリ、およびその他のコンパイル ツールのすべてが、プログラムの意味、つまりセマンティクスについて共有された考えを持つ必要があります。プログラムの。オブジェクトの有効期間(コンパイラがオブジェクトの作成を支援するために追加のコードを実行できる時期と、オブジェクトを安全に使用できる時期) について合意することは、実際にはこの合意の重要な部分です。コンストラクタとデストラクタは、この有効期間の定義の一部です。それらがたまたま空である場合でも、オブジェクトが有効である場合に同意する方法を提供し、言語の指定と理解を容易に (可能に) します。
パラメーター化されたコンストラクターは引数を取ることができます。例えば:
class example
{
int p, q;
public:
example();
example(int a, int b); //parameterized constructor
};
example :: example()
{
}
example :: example(int a, int b)
{
p = a;
q = b;
}
パラメーター化されたコンストラクターでオブジェクトを宣言する場合、初期値を引数としてコンストラクター関数に渡す必要があります。オブジェクト宣言の通常の方法は機能しない場合があります。コンストラクターは、明示的または暗黙的に呼び出すことができます。コンストラクターを暗黙的に呼び出す方法は、短縮方法とも呼ばれます。
example e = example(0, 50); //explicit call
example e(0, 50); //implicit call
これは、オブジェクトに初期値を提供する場合に特に便利です。また、このページには重要なものがあります: http://en.wikipedia.org/wiki/Constructor_%28object-directional_programming%29
単純なクラスがあるとします:
class Foo
{
int bar;
}
の値はbar
正確には何ですか?オブジェクトにメモリがいつ割り当てられるかは気にしないかもしれませんが、プログラムを実行しているマシンはオブジェクトに何らかの値を与える必要があります。それがコンストラクターの目的です。クラスメンバーを何らかの値に初期化します。
コンストラクターは、組み込み型を除いて、クラス メンバーのコンストラクターを呼び出すために必要です。