2009-05-06 56 views
19

私は、スコープ解決演算子(つまり、className::className())を使用して、C++でクラスのコンストラクタを明示的に呼び出すことができます。私はそのような呼びかけをどこで正確に行う必要があるのだろうと思っていました。C++で明示的にコンストラクタを呼び出す理由

+5

コンストラクタを直接呼び出すことはできません。規格には明示的に(12.1/1)があります。「コンストラクタには名前がありません。ファンクションスタイルのキャストや新しい配置など、他のコンストラクトを介してのみコンストラクタを呼び出すことができます。 –

答えて

13

:例えば

class BaseClass 
{ 
public: 
    BaseClass(const std::string& name) : m_name(name) { } 

    const std::string& getName() const { return m_name; } 

private: 

    const std::string m_name; 

//... 

}; 


class DerivedClass : public BaseClass 
{ 
public: 

    DerivedClass(const std::string& name) : BaseClass(name) { } 

// ... 
}; 

class TestClass : 
{ 
public: 
    TestClass(int testValue); //... 
}; 

class UniqueTestClass 
    : public BaseClass 
    , public TestClass 
{ 
public: 
    UniqueTestClass() 
     : BaseClass("UniqueTest") 
     , TestClass(42) 
    { } 

// ... 
}; 

...。

それ以外は、ユーティリティは表示されません。あまりにも若かったときに、私が実際にやっていたことを知るために、他のコードでコンストラクタを呼び出すだけでした...

+4

派生クラスのインスタンスが構築されたときに、C++は暗黙的に親クラスのコンストラクタを呼び出しますが、明示的に特定の親クラスコンストラクタをイニシャライザリストで呼び出さない限り、デフォルトコンストラクタを呼び出します。 –

+0

はい、私の例では、BaseClassの唯一の有効なコンストラクタがいくつかのパラメータを必要としていることを確認しました。明示的なデフォルトのコンストラクタ呼び出しが必要な場合は覚えていません。多分仮想継承で? – Klaim

0

私はあなたが典型的にコンストラクタのために使用するとは思わない、少なくともあなたが記述している方法ではない。ただし、異なる名前空間に2つのクラスがある場合は、そのクラスが必要になります。たとえば、これら2つのメークアップクラスの差異を指定するには、Xml::ElementChemistry::Elementを指定します。

通常、クラスの名前はスコープ解決演算子で継承されたクラスの親に対して関数を呼び出すために使用されます。したがって、Animalを継承するクラスDogがあり、両方のクラスがEat()関数を別々に定義している場合は、someDogというDogオブジェクトでAnimal版のeatを使用する場合があります。私のC++構文は少し錆びていますが、その場合はsomeDog.Animal::Eat()と言います。ほとんどの場合、いくつかのパラメータが必要な子クラスのコンストラクタで

42

時には、一時的に構築するためにコンストラクタを明示的に使用することもあります。たとえば、コンストラクタといくつかのクラスがある場合:

class Foo 
{ 
    Foo(char* c, int i); 
}; 

と機能

void Bar(Foo foo); 

をしかし、あなたの周りにはFooを持っていない、あなたは

Bar(Foo("hello", 5)); 

これを行うことができますキャストのようなものです。実際、パラメータが1つしかないコンストラクタがある場合、C++コンパイラはそのコンストラクタを使用して暗黙のキャストを実行します。

ではありません既存のオブジェクトでコンストラクタを呼び出すことは合法です。つまり、行うことはできません

Foo foo; 
foo.Foo(); // compile error! 

あなたが何をしていても。しかし、あなたはメモリを割り当てずにコンストラクタを呼び出すことができます。それはの配置の新しいのためのものです。

char buffer[sizeof(Foo)];  // a bit of memory 
Foo* foo = new(buffer) Foo(); // construct a Foo inside buffer 

新しいメモリを割り当てる代わりに、新しいメモリを割り当て、その場所にオブジェクトを作成します。この使用法は悪いとみなされ、ほとんどのタイプのコードではまれですが、埋め込み型およびデータ構造体のコードでは一般的です。

たとえば、std::vector::push_backは、この手法を使用してコピーコンストラクタを呼び出します。この方法では、空のオブジェクトを作成して代入演算子を使用する代わりに、1つのコピーを実行するだけで済みます。

+4

+1の新しい配置。それは変ですが、あなたが何をしているのかを知っていると便利です。 –

+0

"実際、1つのパラメータしか取らないコンストラクタがある場合、C++コンパイラはそのコンストラクタを使用して暗黙のキャストを実行します。これは、多くの人々がデフォルトで単一引数のコンストラクタに明示的なキーワードを置く理由であり、パラメータ型からクラス型へのimplictのキャストが必要な場合にのみ使用します。 –

+1

-1コンストラクタを呼び出していません。構文は '(ctor-argリスト)'であり、それは ' :: (ctor-argリスト)'と同じではないペダンティックである。構文は、あなたがコンストラクタを呼び出すように見えるようにします。実際、関数のようにコンストラクタを "呼び出す"ことはありません。 –

3

私はコンパイラエラーC2585のエラーメッセージは、あなたが実際にコンストラクタのスコープ解決演算子を使用する必要がありますなぜ最良の理由を与え、それがチャーリーの答えでないと思う:クラスからの変換

または複数の継承に基づく構造タイプです。型が同じ基本クラスを複数回継承する場合、変換関数または演算子は、変換で使用する継承クラスを指定するためにスコープ解決(:) :)を使用する必要があります。

BaseClassを持っていて、BaseClassAとBaseClassBの両方がBaseClassを継承し、DerivedClassがBaseClassAとBaseClassBの両方を継承しているとします。

DerivedClassをBaseClassAまたはBaseClassBに変換するために変換または演算子のオーバーロードを行っている場合は、変換に使用するコンストラクタ(コピーコンストラクタIIRCなどと考えています)を特定する必要があります。

2

通常、コンストラクタを直接呼び出すことはありません。 new演算子がそれを呼び出すか、サブクラスが親クラスのコンストラクタを呼び出します。 C++では、基底クラスは、派生クラスのコンストラクタが開始される前に完全に構​​築されている必要があります。

コンストラクタを直接呼び出す唯一の時間は、新しいものを使用せずにメモリを管理する非常にまれなケースです。そしてそれでも、あなたはそれをしてはいけません。代わりに、演算子newの配置書式を使用する必要があります。

0

クラスコンストラクタを公開する有効な使用例があります。たとえば、アレーナアロケータを使用して独自のメモリ管理を行う場合は、アロケーションとオブジェクトの初期化からなる2段階構成が必要です。

私が取るアプローチは他の多くの言語のアプローチと似ています。私は単純によく知られているパブリックメソッド(Construct(),init()のようなもの)に私の建設コードを入れて、必要なときにそれらを直接呼び出します。

コンストラクタに一致するこれらのメソッドのオーバーロードを作成できます。あなたの普通のコンストラクタは、それらに呼び出すだけです。あなたが間違った場所に重要な建設コードを追加しないように、あなたがこれをやっていることを他の人に警告するためにコードに大きなコメントを入れてください。

どの建設用オーバーロードが使用されていてもデストラクタメソッドが1つしかないので、デストラクタを初期化されていないメンバーに対して堅牢にしてください。

再初期化できる初期化子の書き込みを試みることをお勧めします。初期化されていないメモリと実際には実データを保持しているため、ゴミが入っているオブジェクトを見ているケースを示すのは難しいです。

最も困難な問題は、仮想メソッドを持つクラスにあります。この場合、コンパイラは通常、vtable関数テーブルポインタをクラスの先頭の隠しフィールドとしてプラグインします。このポインタを手動で初期化することはできますが、基本的にはコンパイラ固有の動作に依存しており、お互いに面白いものを見せてくれる可能性があります。

新しいプレースメントは多くの点で壊れています。配列の構築/破壊には1つのケースがありますので、私はそれを使用しない傾向があります。

0

以下のプログラムを検討してください。

template<class T> 
double GetAverage(T tArray[], int nElements) 
{ 
T tSum = T(); // tSum = 0 

for (int nIndex = 0; nIndex < nElements; ++nIndex) 
{ 
    tSum += tArray[nIndex]; 
} 

// Whatever type of T is, convert to double 
return double(tSum)/nElements; 
} 

これは、変数を初期化するために明示的にデフォルトのコンストラクタを呼び出します。

関連する問題