2

次の例を検討してください

struct Foo
{
    int bar;    
    Foo(int i):bar(i){cout << "real ctor\n";}   
    Foo(){cout << "default ctor\n";}
};

int main()
{   
    Foo fooArr[3];//default ctor called 3 times 
    for(int i=0;i!=3;++i)cout << fooArr[i].bar << endl;//bare memory junk
    cout << endl;

    vector<Foo> fooVec;
    for(int i=0;i!=3;++i){
        fooVec.push_back(Foo(i));     //only real ctor called
        cout << fooVec[i].bar << endl;//real thing 
    }
    cout << endl;

    int iArr[3];
    for(int i=0;i!=3;++i)cout << iArr[i] << endl;//bare memory junk
}

Foo私の設計にないため、ユーザーがデフォルトのコンストラクターを呼び出すことは望ましくありません。しかし、ユーザーが の配列を使用できるようにしたいのですがFoo、それをサポートするために、無意味で紛らわしい Foo::Foo() を提供することを余儀なくされました。なぜC++標準がプログラマーにそのようなことを強制するのか理解できません。その背後にある理論的根拠は何ですか?矛盾の理由は?これを理解した頭のいい人は、私に説明してくれませんか? 前もって感謝します!

4

3 に答える 3

4

Fooデフォルトのコンストラクターがない場合でも、配列を作成できます。配列を宣言するときに要素を構築する必要があるだけです。だからあなたはこれを行うことができます:

Foo fooArr[] = { Foo( 1 ), Foo( 2 ), Foo( 3 ) };

別の方法は、動的配列(vector<Foo>おそらく最良の例)またはFoo(のようなshared_ptr<Foo> arrFoo[3])へのポインタの配列を使用することです

shared_ptr<Foo> arrFoo[3];
arrFoo[2].reset( new Foo(3) );

に関する最後の注意: 配列のサイズは事前にわかっているため、将来のすべてのs のvector<Foo>ためにベクトルに十分なスペースを確保することでパフォーマンスを向上させることができます。Foo

vector<Foo> arrFoo;
arrFoo.reserve( 3 );

for( int i = 0; i<3; ++i )
    arrFoo.push_back( Foo( i ) );

編集:あなたの質問は、タイプの静的配列を作成するためにデフォルトのコンストラクターが必要な理由でした。答えは明らかだと思いましたが、説明してみます。

Foo bar1; Foo bar2;引数が指定されていないため、既定のコンストラクターを使用して 2 つのオブジェクトを作成します。

Foo bar[2];本質的に同じものです。構築する必要がある 2 つのオブジェクトを宣言します。構築せずにオブジェクトを宣言する方法はありません。これが、最初にオブジェクトを宣言するポイントです。

C++ の静的配列は、メモリに連続して配置された単なるオブジェクトの集まりです。個別のオブジェクトではありません。

それが理にかなっていることを願っています。

于 2012-10-13T07:30:37.253 に答える
3

根拠は、配列がデフォルトで構築された要素でいっぱいであるため、要素の型はデフォルトで構築可能でなければならないということです。いくつかの値で配列を初期化した場合、デフォルトの構築は必要ありません。

Foo fooArr1[3]; // full of default constructed Foos
Foo fooArr[3] = {1,2,3}; // default constructor not required. Foo(int) called.

コード例の 2 行目では、暗黙的な変換コンストラクターによって提供されるからintへの暗黙的な変換が使用されていることに注意してください。FooFoo(int)

独自のデフォルト コンストラクターを提供する必要があるのは、コンストラクターを 1 つ宣言したために、デフォルト コンストラクターの自動生成が無効になっているためです。この背後にある理論的根拠は、何らかのコンストラクターを提供する必要がある場合、デフォルトのコンストラクターで何か特別なことをしたい可能性が高いということです。

ユーザー提供のコンストラクターが本当に心配な場合は、クラスを実際の集約にして、集約の初期化を使用できます。

struct Foo
{
  // no user declared constructors
  int foo;
};

int main()
{   
    Foo fooArr1[3]; // OK
    Foo fooArr[3] = { {1}, {2}, {3} }; // aggregate construction 
}

C++11 では、以下を使用して、コンパイラが生成したデフォルト コンストラクタを有効にできますdefault

Foo()=default;
于 2012-10-13T07:08:53.700 に答える
1

選択する必要があります: デフォルトのコンストラクターを定義しないため、の配列を宣言できませんFoo。または、デフォルトのコンストラクター (空であっても) を宣言し、の配列を宣言できFooます。

以前に C# や Java などの OOP 言語を扱ったことがあり、class FooandがあるFoo[] arr場合、これらの言語の配列はオブジェクトへの参照 (アドレス) のみを運ぶため、デフォルト コンストラクターを宣言する必要はありません。配列自体はオブジェクトなのでarr、作成すると == になりnullます。を使用arr = new Foo[3];すると、次の 3 つの参照を含む配列の新しいオブジェクトを作成しますarr == { null, null, null }。次に、各参照にオブジェクトを割り当てますfor (int i = 0; i < 3; ++i) arr[i] = new Foo(i);

ただし、配列はオブジェクトへの参照ではなくオブジェクト自体を保持するため、C++ は異なります。したがって、オブジェクト自体を運ぶときは、各オブジェクトで呼び出されるパラメーターなしのコンストラクターが必要です。(つまり、C++ のFoo arr[3];場合:arr = { objectOfFoo, objectOfFoo, objectOfFoo }

問題の解決策は、ポインターの配列を宣言することで見つかる場合があります。

Foo * arr[10] = { 0 }; // arr = { NULL, NULL, NULL, ... , NULL }
for (int i = 0; i < 10; ++i) arr[i] = new Foo(3); // you don't have to declare default constructor

// some using of array
// C++ doesn't have a garbage collector
for (int i = 0; i < 10; ++i) delete arr[i];
于 2012-10-13T07:26:31.190 に答える