1

静的メンバーを確実に初期化するための唯一の信頼できる方法は、関数で行うことであることをずっと前に学びました。今、私がやろうとしていることは、非定数参照によって静的データを返し始めることであり、私を止める誰かが必要です。

function int& dataSlot()
{
    static int dataMember = 0;
    return dataMember;
}

私の知る限り、これは静的メンバーがゼロに初期化されることを保証する唯一の方法です。ただし、次のようなあいまいなコードが作成されます。

dataSlot() = 7; // perfectly normal?

もう1つの方法は、定義を翻訳単位に入れて、ヘッダーファイルに入れないようにすることです。私はそれ自体に反対することは何もありませんが、いつ、どのような状況で安全であるかについて、規格が何を示しているのかわかりません。

私がやりたいと思う絶対的な最後のことは、誤って初期化されていないデータにアクセスし、プログラムの制御を失うことです。

4

5 に答える 5

6

(グローバルの無差別使用に対する通常の注意を払って...)グローバルスコープで変数を宣言するだけです。コードが実行される前に、ゼロで初期化されることが保証されています。

自明でないコンストラクターを持つ型に関しては、もっと狡猾である必要がありますが、intはグローバルとして正常に機能します。

于 2012-08-23T11:52:11.557 に答える
4

非定数参照自体を返すこと自体はかなり無害です。たとえば、それが何をするかvector::at()、またはvector::iterator::operator*です。

構文が気に入らない場合はdataSlot() = 7;、次のように定義できます。

void setglobal(int i) {
    dataSlot() = i;
}
int getglobal() {
    return dataSlot();
}

または、次のように定義できます。

int *dataSlot() {
    static int dataMember = 0;
    return &dataMember;
}

*dataSlot() = 7; // better than dataSlot() = 7?
std::cout << *dataSlot(); // worse than std::cout << dataSlot()?

誰かに止めてもらいたい場合は、可変グローバル状態の使用に代わる方法を提案するために、より多くの情報が必要です。

于 2012-08-23T11:54:24.783 に答える
3

マイヤーズシングルターと呼ばれ、ほぼ完全に安全です。

関数dataSlot()が呼び出されたときにオブジェクトが作成されることに注意する必要がありますが、プログラムが存在するとき(グローバル変数が破棄されるとき)にオブジェクトが破棄されるため、特別な注意が必要です。デストラクタでこの関数を使用すると、特に危険であり、ランダムにクラッシュする可能性があります。

于 2012-08-23T11:49:41.597 に答える
2

静的メンバーを確実に初期化するための唯一の信頼できる方法は、関数で行うことであることをずっと前に学びました。

いいえ、そうではありません。標準はそれを保証します:

  1. 簡単なコンストラクターを備えた静的ストレージ(ブロックとファイルまたはクラス静的スコープの両方)を持つすべてのオブジェクトは、コードが実行される前に初期化されます。プログラムのすべてのコード。
  2. file / global / class-staticスコープと重要な構成を持つすべてのオブジェクトは、main関数が呼び出される前に初期化されます。オブジェクトAとBが同じ変換単位で定義され、AがBの前に定義されている場合、AはBの前に初期化されることが保証されます。ただし、異なる変換単位で定義されたオブジェクトの構築順序は指定されておらず、コンパイル間で異なることがよくあります。
  3. ブロック静的オブジェクトは、宣言に初めて到達したときに初期化されます。C ++ 03標準はスレッドをサポートしていないため、これはスレッドセーフではありません。
  4. 静的ストレージ(ブロックとファイル/グローバル/クラス静的スコープの両方)を持つすべてのオブジェクトは、関数が終了するか、システムコールを使用してアプリケーションが終了した後、コンストラクターが完了するのとは逆の順序で破棄されます。main()exit()

どちらの方法も、すべての場合に使用可能で信頼できるわけではありません。

今、私がやろうとしていることは、非定数参照によって静的データを返し始めることであり、私を止める誰かが必要です。

誰もあなたを止めるつもりはありません。それは合法であり、完全に合理的なことです。ただし、スレッドトラップに陥らないように注意してください。

たとえば、C ++用の妥当な単体テストライブラリは、すべてのテストケースを自動的に登録します。それは次のようなものを持つことによってそれを行います:

std::vector<TestCase *> &testCaseList() {
    static std::vector<TestCase *> test_cases;
    return test_cases;
}

TestCase::TestCase() {
    ...
    testCaseList().push_back(this);
}

それはそれを行うための2つの方法のうちの1つだからです。もう1つは:

TestCase *firstTest = NULL;

class TestCase {
    ...
    TestCase *nextTest;
}

TestCase::TestCase() {
    ...
    nextTest = firstTest;
    firstTest = this;
}

今回は、firstTest自明なコンストラクターを持っているという事実を使用しているため、自明でないコンストラクターを持つsの前に初期化されます。TestCase

dataSlot()= 7; //完全に正常ですか?

はい。ただし、本当に必要な場合は、次のいずれかを実行できます。

  1. の古いCのもの

    #define dataSlot _dataSlot()
    

    errno「変数」は通常、ある意味で定義されます。

  2. または、次のような構造体でラップすることもできます

    class dataSlot {
        Type &getSlot() {
            static Type slot;
            return slot;
        }
        operator const Type &() { return getSlot(); }
        operator=(Type &newValue) { getSlot() = newValue; }
    };
    

    (ここでの欠点は、dataSlotで直接呼び出しようとすると、コンパイラがTypeのメソッドを検索しないことです。そのため、operator =が必要です)

于 2012-08-23T12:40:04.260 に答える
0

実際のデータスロットのラッパーであるdataslot()とset_dataslot()の2つの関数を、次のように作成できます。

int &_dataslot() { static int val = 0; return val; }
int dataslot() { return _dataslot(); }
void set_dataslot(int n) { _dataslot() = n; }

おそらく、ヘッダーにそのロットをインライン化することは望まないでしょうが、とにかくそのようなことを試みた場合、いくつかのC++実装はかなりうまくいかないことがわかりました。

于 2012-08-23T12:13:56.747 に答える