0

私は自分のコードでこの奇妙なポインターのない状況に遭遇し、それを検出する方法(クラッシュ以外)があるかどうか疑問に思っています。コードは、なんとか未構築のオブジェクトのメソッドに入ることができます。b1が作成されたとき、aは作成されておらず、bはそれを使用しようとします。b2が構築されるまでに、aは適切に構築され、コードは期待どおりに機能します。

「コードでこれを行わないでください」という明白なことを超えて、コンパイル時または実行時にこれを検出する方法があるかどうか疑問に思っています。コンパイラはそれをまったく検出しませんでした。プログラムがクラッシュしたときにDLLを初期化するときに、マネージコードを実行することについて、あいまいで非常に役に立たないメッセージが表示されました。

「this」をテストしようとしましたが、メモリが割り当てられているためNULLではありません。コンストラクタが呼び出されていないため、メモリが不確定な状態になっています。

コンパイラがこれがいつ発生するかを検出するためにデバッグコードに固執すると思っていたでしょうが、そうではないと思います。

この状況を検出するために使用できるアサーション、テスト、またはコンパイル時の切り替えはありますか、それとも「痛い場合は実行しないでください」という結果になりますか?

 OUTPUT:
 (NULL)
 test

#include "stdafx.h"
#include "cstring"

class Apple 
{
    char *sometimesinitialized;

    public:
    Apple () { 
        sometimesinitialized = new char[15];
        strcpy_s(sometimesinitialized, 5, "test");
    };
    void test()
    {
        printf("%s\n", sometimesinitialized);
    }
};

class Ball
{
    public:
    Ball();
};

Ball b1; // OOPS!
Apple a;
Ball b2; // Works as expected

Ball::Ball()
{
    a.test();
}

int _tmain(int argc, _TCHAR* argv[])
{
    scanf_s("%i");
    return 0;
}
4

3 に答える 3

5

のインスタンスが のインスタンスBにアクセスする必要がある場合は、のコンストラクタAに渡す必要があります。B

struct B {
    B(A &a) {
        a.test();
    }
};


A a;
B b(a);
于 2012-08-31T06:06:23.803 に答える
2

静的ストレージを持つオブジェクトが、標準で完全に保証された方法で適切に初期化されることを保証する一般的なパターンは次のとおりです。

グローバル.hpp:

struct Foo;
Foo & globalFoo();

グローバル.cpp:

#include <foo.hpp>

Foo & globalFoo()
{
    static Foo impl;
    return impl;
}

グローバルFooオブジェクトにアクセスする必要がある人は、呼び出しglobalFoo()て参照を取得するだけです。オブジェクトは、最初に使用する前に初期化され、使用したすべてのユーザーが破棄された後、プログラムの最後に破棄されます。

于 2012-08-31T08:55:01.230 に答える
0

C++ プログラムの起動とシャットダウンは、かなりのあいまいさがある 2 つの領域であり、一般に、必要最小限の作業を行うように努める必要があります。

標準では、モジュール内の関数を実行すると、コンパイル単位内のすべての静的期間変数が初期化されると記載されていますが、静的期間オブジェクトの初期化または破棄中の静的期間オブジェクトの状態については、自分で判断してください。または、循環依存関係についてはどうなりますか。

mainさらに、標準では、コンパイル単位での静的期間変数の初期化は前に実行する必要はありませんが、アクセスするまで遅延する可能性があるため、モジュールの「サービス登録」などを行う静的期間オブジェクトを持つことに依存することはできません。モジュール内の関数。したがって、モジュールへのアクセスがモジュールの登録に依存している場合、初期化は単純にスキップされ、モジュールは未登録のままアクセスできなくなります。この遅い初期化の文言は、言語で動的ライブラリをサポートできるようにするためだけに追加されましたが、標準では、そのような手法が機能することを保証することを義務付けていないという事実が残っています。

もう 1 つの重要な点は、実装上の問題についてです。たとえば、私が経験した Windows では、シャットダウン中のエラーは単純に無視される可能性があり (そのため、アプリケーションは問題なく終了しているように見えますが、実際にはシャットダウン中のアクセス違反で停止しています)、デバッグ機能も開始前と開始後に正しく機能していませんでした。の完了によりmain、これら 2 つの領域での問題のデバッグが非常に難しくなります。

たとえば、ロギング機能の使用を検討してください。静的期間変数の破棄中にロギングを呼び出したい場合、ロギング自体が静的期間変数に依存する場合に問題が発生します (ロギングを呼び出すときに、ロギング オブジェクトが既に破棄されている可能性があります)。

また、構築と破棄が発生する順序は保証されません (コンパイル単位で関数を呼び出す前に、その単位の静的な期間変数が初期化されることを示す句を除きます)。何も変更せずにプロジェクトを再構築するだけで、おそらくスマートなインクリメンタル リンク手法が原因で、異なるシーケンスが生成される可能性があることを経験しました)。

私の提案は、静的な初期化と破棄中に必要最小限のことを行い、失敗する可能性のあることは何もしないことです。main に入ったら、必要な初期化を必要な順序で明示的に実行します。同様に、シャットダウン時に何が発生し、どのシーケンスで発生するかを明示的に制御しておくと、デバッグ時間を大幅に節約できます。

以前は、遅延自動初期化手法の支持者でしたが、IMO C++ は十分な保証がないため、このアプローチが実用的な言語ではありません。

于 2012-08-31T06:43:32.973 に答える