つまり、関数とコンストラクタの呼び出しに違いがある以上に、単語を選択するということです。 「オブジェクトのコンストラクタ」という名前のオブジェクトは、「名前がobject
の関数の型object
を返す」という名前でもかまいません。
C++では同じ関数と型名を使用できないと主張する人もいるでしょう。ただし、そのための回避策があります。 C++には特別な構文糖(コンストラクタと呼ばれます)があり、これを使用してobject
という名前の関数を作成し、型object
を返すことができます。ですから、コンストラクタは自立関数として見られ、使用されると思います。
ここに欠けている重要な意味上の違いはありますか?
コンストラクターは基本的にメソッドですが、特殊なメソッドです。
たとえば、C++では、コンストラクターは、単にその型の新しいインスタンスを返す関数ではありません。そうであれば、継承は機能しません。基本コンストラクターを呼び出すことはできませんでした。それらも新しいインスタンスを返すからです。最終的にAの新しいインスタンスが生成され、Bのコンストラクタに返されて、Bの新しいインスタンスが作成され、Cのコンストラクタに返されます。
代わりに、コンストラクターは、インスタンスアロケーターによって自動的に呼び出される初期化メソッドです。たとえば、「new」を呼び出してヒープ上にオブジェクトを作成すると、要求された型にメモリが割り当てられ(mallocなどのメモリアロケータを使用)、そのコンストラクタメソッドが呼び出されます。コンパイラーには、そのコンストラクターが他のコンストラクターを呼び出す方法と順序について特別な規則があります。たとえば、C#では、基本コンストラクターを明示的に呼び出さない場合、コンパイラーは基本デフォルトコンストラクターへの呼び出しを追加します。
「型のインスタンスを返す.ctorという名前の関数」とは異なるコンストラクタをコンパイラがどのように処理するかについての規則です。
いいえ、object
のコンストラクタは、object
と呼ばれ、object
を返す関数とは大きく異なります。コンストラクタには名前がありませんが、それは主に技術的なことです。さらに重要な違いは、コンストラクターには戻り値の型がないことと、直接呼び出すことはできないことです。
メモリのブロックが与えられ、そのメモリをその場で操作するため、コンストラクタは何も返しません。少しの間「コンストラクタ」の名前を忘れた場合に役立つことがあります。代わりにinitialiserと考えてください。コンストラクタの目的はnotで「薄い空気から」オブジェクトを構築することです。その目的は、オブジェクトをメモリ内の必要な場所に正確に初期化することです。
コンストラクタがオブジェクトを返す場合、その返されたオブジェクト(戻り値)はどこかに(おそらくスタック上に)存在する必要があり、そこで初期化する必要があります—ここでループが発生しています。
これは、コンストラクターを直接呼び出すことはできないという事実と関連しています。クラスobject
があり、式object()
をどこかで使用している場合、「object
のコンストラクターを呼び出す」というセマンティクスはありません。 「タイプobject
の一時オブジェクトを作成する」というセマンティクスがあります。コンパイラーはこれを、一時変数が(おそらく/一般的にはスタック上に)存在する場所を割り当て、コンストラクターを呼び出してその場所にオブジェクトを構築(=初期化)することに変換します。
new object()
を使用する場合も同じ原則が適用されます。これは、次の2つのことを行う新しい式です。
operator new
_(_void*
_を返す)を呼び出します。object
のコンストラクターを次のような関数と考えます。
_static object object();
_
間違っている。どちらかと言えば、コンストラクタの動作はこれに近いです:
_static void object(object &place_to_work_in);
_
決して直接呼び出すことはできないという例外があります。 alwaysは、別の言語構造によって指定された場合にのみ呼び出されます。新しい配置(「コンストラクターをここで呼び出す」トリックとも呼ばれます)であるnew (&place_for_object) object()
でさえ、コンストラクターを直接呼び出しません。これは、引数を返す_operator new
_の配置新しい形式の呼び出しに変換され、その後にコンストラクターの呼び出しが続きます(他の新しい式と同様)。
言い換える試みは正しくありません。
コンストラクターは、クラスの名前と同じ名前のメンバー関数のように見えますが、実際のところ、コンストラクターには名前がまったくありません。これは、C++標準(セクション[class.ctor]/1)で明示的に述べられています。
コンストラクターには名前がありません。
「コンストラクターは独立した関数として見られ、使用できると思います」というのは...まあ、あなたの意味がよくわかりません。無料の関数は、コンストラクターにすることはできません。コンストラクターはクラスのメンバーでなければなりません。同時に、オブジェクトを作成してそのオブジェクトのインスタンスを返す無料の関数を確実に定義できます。その自由な関数は、もしあなたがひどくやりたいなら、それが作成するオブジェクトのタイプと同じ名前を(ある種の)持つことさえできます。たとえば、名前空間内でクラスを定義してから、名前空間外で関数を定義できます。
namespace foo {
class bar {};
}
foo::bar bar() { return foo::bar(); }
これはreallyと同じ名前ではありません(関数はbar
で、クラスはfoo::bar
です)。ただし、視点によっては、それらを並べ替えることができますとにかくそのように。
このような自由な関数には、コンストラクターの他の重要な特徴はありません。コンストラクターの呼び出しは完全に暗黙的な場合があります。たとえば、次のようなクラスがあるとします。
class foo {
public:
foo (int) {}
foo operator+(foo const &other) {}
};
...次のようなコード:
foo f(1);
foo h(0);
h = f + 1;
...1
から一時的なfooオブジェクトを作成できるため、そのfooオブジェクトをfoo::operator+
に渡すことができます。これは、フリー関数が正しいパラメーターの型を取り、必要な結果の型を返すように定義されている場合でも、フリー関数では発生しません。このような暗黙の変換には、コンストラクターが必要です。
つまり、関数とコンストラクタの呼び出しに違いがある以上に、単語を選択するということです。 「オブジェクトのコンストラクタ」と名付けられたものは、「名前オブジェクトが型オブジェクトを返す関数」と呼ぶこともできます。
まず、コンストラクターは何も返さないため、結果として戻り値の型がありません。
第2に、コンストラクターは、関数を呼び出す方法でコンストラクターを直接呼び出すことができないという点で異なります。代わりに、変数を宣言すると、ランタイムによって適切なコンストラクターが呼び出されます。
3番目に、継承の場合、サブクラスの通常の関数は親クラスの関数をオーバーライドします。一方、コンストラクタはカスケードします。親クラスのコンストラクタが最初に呼び出され、次にサブクラスのコンストラクタが呼び出されます。
第4に、コンストラクターは初期化リストを持つことができますが、「通常の」関数はできません。