12

グローバル オブジェクト、オブジェクトの配列、および別のクラス/構造体に含まれるオブジェクトの 3 つの状況で、次のクラスのコンストラクターをどのように呼び出しますか?

コンストラクターを持つクラス (3 つの例すべてで使用):

class Foo {
    public:
        Foo(int a) { b = a; }

    private:
        int b;
};

そして、このコンストラクターを呼び出す私の試みは次のとおりです。

グローバル オブジェクト

Foo global_foo(3); // works, but I can't control when the constructor is called.

int main() {
    // ...
}

オブジェクトの配列

int main() {
    // Array on stack
    Foo array_of_foos[30](3); // doesn't work

    // Array on heap
    Foo *pointer_to_another_array = new Foo(3) [30]; // doesn't work
}

そこで、配列のすべての要素に対してコンストラクターを呼び出そうとしていますが、個々の要素に対してコンストラクターを呼び出す方法も知りたいです。

クラス/構造体に含まれるオブジェクト

class Bar {
    Foo foo(3); // doesn't work
};

int main() {
    Bar bar;
}
4

8 に答える 8

11

グローバル オブジェクト

あなたの方法しかありません。一方、これを避けるようにしてください。代わりに、関数 (または他のオブジェクト) をファクトリとして使用することをお勧めします。そうすれば、作成の時間を制御できます。

オブジェクトの配列

これを直接行う方法はありません。非 POD オブジェクトは常にデフォルトで構築されます。std::fill多くの場合、大きな助けになります。アロケータとstd::uninitialized_fill.

クラス/構造体に含まれるオブジェクト

コンストラクターで初期化リストを使用します。

class Bar {
    Foo foo;

    Bar() : foo(3) { }
};

静的メンバーは、実際にはクラスの外で定義する必要があります。

class Bar {
    static Foo foo;
};

Foo Bar::foo(3);
于 2008-11-16T19:39:42.017 に答える
5

グローバルに関するいくつかの誤解を修正するには:

  • 順序は、コンパイル単位内で明確に定義されています。
    • 定義順と同じです
  • コンパイル単位間の順序は未定義です。
  • 破壊の順序は創造の正反対です

私が推奨するものではありませんが、簡単な解決策は、すべてのグローバルを単一のコンパイル単位に入れることです。

あるいは、関数の静的変数の使用を微調整することもできます。
基本的に、必要なグローバルへの参照を返す関数を使用できます(関数内でグローバルを定義します)。最初の使用時に作成されます (作成とは逆の順序で破棄されます)。

Foo& getGlobalA() // passed parameters can be passed to constructor
{
    static Foo  A;
    return A;
}
Foo& getGlobalB()
{
    static Foo  B;
    return B;
}
etc. 
于 2008-11-16T20:04:01.983 に答える
3

Konrad の返信は問題ありません。配列に関する句読点に過ぎません。(ポインタではなく) 項目の配列を作成する方法があり、次のようになります。

//allocate raw memory for our array
void *rawMemory = operator new[](30 * sizeof(Foo))

// point array_of_foos to this memory so we can use it as an array of Foo
Foo *array_of_foos = static_cast<Foo *>(rawMemory);

// and now we can create the array of objects(NOT pointers to the objects)
//  using the buffered new operator
for (int i = 0; i < 30; i++)
    new(array_of_foos[i])Foo(3);

このアプローチについては、http: //www.amazon.com/gp/product/0321334876?ie=UTF8&tag=aristeia.com-20&linkCode=as2&camp=1789&creative=9325&creativeASIN=0321334876で説明されています。

于 2008-11-16T20:10:09.443 に答える
2

グローバルな場合、いつ呼び出されるかを制御する方法はありません。C++ の仕様では、基本的に main() の前に呼び出され、後で破棄されると述べています。それ以外は、コンパイラは自由に行うことができます。

最初の配列のケースでは、Foo オブジェクトの静的配列を作成しています。デフォルトでは、配列内の各値は Foo() のデフォルト コンストラクターで初期化されます。生の C++ 配列を使用して、オーバーロードされた特定のコンストラクターを強制的に呼び出す方法はありません。配列の代わりにベクトルに切り替えることで、少しの制御を推測できます。ベクトル コンストラクターにはオーバーロードされたコンストラクター vector(size,defaultValue) があり、探しているものを実現する必要があります。ただし、この場合、Foo(3) を呼び出す代わりに Foo(const Foo& other) を呼び出すため、other が Foo(3) であるため、注意が必要です。

2 番目の配列のケースは、最初のケースと非常によく似ています。唯一の実際の違いは、メモリが割り当てられる場所 (スタックではなくヒープ上) です。コンストラクターの呼び出しに関しては、同じ制限があります。

含まれているケースは別の問題です。C++ では、オブジェクト内のフィールドの定義とフィールドの初期化が明確に分離されています。これを C++ で動作させるには、Bar 定義を次のように変更する必要があります。

class Bar{
  Foo foo;
  Bar() : foo(3){}
};
于 2008-11-16T19:40:10.893 に答える
1

このスレッドには、デフォルトのコンストラクターを使用する以外に配列のメンバーを初期化できないという一般的な要点があるようです。別のコンストラクターを呼び出すためだけに、別の型を作成する回答もあります。できますが(配列がクラスのメンバーとして含まれていない場合!):

struct foo {
    foo(int a): a(a) { }
    explicit foo(std::string s): s(s) { }
private:
    int a;
    std::string s;
};

/* global */
foo f[] = { foo("global"), foo("array") };

int main() {
    /* local */
    foo f[] = { 10, 20, 30, foo("a"), foo("b") };
}

ただし、型はコピー可能である必要があります。指定された項目は、配列のメンバーにコピー初期化されます。

クラスのメンバーとしての配列の場合、現時点ではコンテナーを使用するのが最善です。

struct bar {
    /* create a vector of 100 foo's, initialized with "initial" */
    bar(): f(100, foo("initial")) { }
private:
    std::vector<foo> f;
};

andy.gurin で説明されている手法を使用するplacement-newこともオプションです。しかし、それは物事を複雑にすることに注意してください。デストラクタを自分で呼び出す必要があります。そして、コンストラクターがスローした場合、まだ配列を構築しているときに、どこで停止したかを把握する必要があります...全体として、クラスに配列が必要であり、それらを初期化したい場合、 a の使用はstd::vector簡単ですベット。

于 2008-11-16T22:31:24.727 に答える
0

オブジェクトの配列の構築:

デフォルトのパラメーターを使用して、元の例を変更できます。

現在、デフォルトのコンストラクターのみがサポートされています。
これは、次のバージョンで対処されているものです (誰もがこの質問をするため)

于 2008-11-16T20:10:47.787 に答える
0

C++0X 初期化子リストは、オブジェクトの配列の場合にこの問題を解決します。このHerb Sutter ブログ エントリを参照してください。彼はそれらについて詳しく説明しています。

それまでの間、次のように問題を回避できる場合があります。

class Foo {
public:
    Foo(int a) : b(a) {}

private:
    int b;
};

class Foo_3 : public Foo {
public:
    Foo_3() : Foo(3) {}
};

Foo_3 array_of_foos[30];

ここで、クラスは、正しい引数でコンストラクターをFoo_3呼び出すためだけに存在します。Fooテンプレートにすることもできます:

template <int i>    
class Foo_n : public Foo {
public:
    Foo_n() : Foo(i) {}
};

Foo_n<3> array_of_foos[30];

繰り返しますが、これはあなたが望んでいることとまったく同じではないかもしれませんが、考える材料を提供するかもしれません.

(また、Fooクラスでは、上記の例のように、コンストラクターで代入の代わりにメンバー初期化子リストを使用する習慣を身に付ける必要があることに注意してください)

于 2008-11-16T20:34:57.957 に答える
0

グローバルクラスオブジェクトのコンストラクターが「作成」時に安全に呼び出されるようにする方法は2つあると思います。

  1. それらを名前空間で宣言し、その名前空間をグローバルにアクセスできるようにします。

  2. それをクラス オブジェクトへのグローバル ポインタにし、main() で新しいクラス オブジェクトをそれに割り当てます。オブジェクトにアクセスする他のグローバル オブジェクトのコンストラクタのコードは、この前に実行されます。

ちょうど私の2セント。

于 2011-03-29T23:43:58.783 に答える