web-dev-qa-db-ja.com

C ++でのオブジェクトの早期破棄

これがこの質問の正しいフォーラムかどうかはわかりませんが、C++言語に関するこの質問からここで試してみます。問題:

私のコードでは、グラフィックスオブジェクトである1つのグローバル変数を使用しています。問題は、プログラムをシャットダウンしたときに、作成されたすべてのオブジェクトを削除しようとしたことです(C APIからいくつかのプロセスも終了したかったので、closeという関数で)。しかし、そのグローバルグラフィックスオブジェクトを削除すると、メインの最後でメモリアクセス違反エラーが発生しました。これは、メインが終了したときにデストラクタが再び呼び出されたためです。これは私が知らなかったものでした。これにより、このプログラムは(GUIが閉じるまで変数を保持したかったので)まったく無関係でしたが、C++プログラミングでは本当に関連のある質問になりました。 メインの終了前にグローバルc ++オブジェクトを破棄することは不可能ですか?

しかし、私はこれを読みました

静的:グローバルスコープまたは名前空間スコープ(§6.3.4)で宣言されたオブジェクトと関数(§12.1.8)またはクラス(§16.2.12)で宣言された静的オブジェクトは、一度だけ作成され、初期化されます(のみ)および 'プログラムが終了するまで「ライブ」(§15.4.3)。そのようなオブジェクトは静的オブジェクトと呼ばれます。静的オブジェクトは、プログラム実行の存続期間を通じて同じアドレスを持っています。静的オブジェクトは、マルチスレッドプログラムで深刻な問題を引き起こす可能性があります。それらはすべてのスレッド間で共有され、通常はデータ競合を回避するためにロックが必要です(§5.3.1、§42.3)

ただし、このテキストのバージョンはインターネット全体に表示されますが、私の質問はこの説明では完全には回答されていません。暗黙のうちにあるかもしれませんが、グローバル変数は常に静的であるように見えるので、正直な答えを持っているといいでしょう。

6
patrik

ただし、このテキストのバージョンはインターネット全体に表示されますが、私の質問はこの説明では完全には回答されていません。暗黙のうちにあるかもしれませんが、グローバル変数は常に静的であるように見えるので、正直な答えを持っているといいでしょう。

明示的に作成しなかった場合は、明示的に破棄する必要はありません。標準の引用からいくつかの単語を削除してみましょう。

静的:グローバルスコープまたは名前空間スコープで宣言されたオブジェクト(§6.3.4)...作成および初期化は1回(のみ)、プログラムが終了するまで(「15.4.3」)「ライブ」。このようなオブジェクトは静的オブジェクトと呼ばれます。

したがって、グローバルスコープまたは名前空間スコープを持つオブジェクトを宣言した場合、そのオブジェクトは静的であり、その存続期間はおおよそプログラムの存続期間です。


類推して、通常スコープのローカル変数を検討してください:

void foo() {
    std::string s;
    // code
}

現在、囲んでいるスコープを終了せずにdestroysを実行する方法はありません。インプレースで明示的に破棄した場合でも、スコープが終了したときにデストラクタが再び呼び出されます。

ここで、グローバルスコープと名前空間スコープをプログラムの最上位のスコープと見なします。このスコープはmain(グローバルスコープ内にネストされています)の前に入力し、mainを終了した後に終了します。このトップレベルのスコープはプログラムと同じ長さであり、他のスコープのオブジェクトである場合よりも早くオブジェクトを破棄することはできません。


コンストラクターが呼び出されたとき(通常、メインの開始前、プラットフォームの詳細について話す必要なく動的にロードされたライブラリーを許可するためのファッジ)、デストラクターが呼び出されたとき(終了ハンドラーなど)について、微妙な点があります。異なるstaticに対するこれらの相対的な順序(明確に定義されていないため、グローバルの初期化または破棄を他のグローバルに依存させないでください)。


もちろん、便宜上オブジェクトをグローバルにしたい場合は、これにctor/dtorを使用する代わりに、明示的なsetup/cleanup呼び出しを選択できます。

10
Useless

これは単純なC++ルールです。単純な整数やポインタなど(ネイティブ型ではない)よりも複雑なオブジェクトには、コンストラクタとデストラクタがあります。そのようなオブジェクトのインスタンスがスコープ内でby valueと宣言されると、そのコンストラクターが自動的に呼び出されます。宣言されたスコープが終了すると、デストラクタが自動的に呼び出されます。この例を見てみましょう:

_// Constructor gets called now, which is before 'main()' 
// enters for a global object like this one.
GraphicsObject graphics;

int main()
{
    // Do stuff with 'graphics'
    // ...

} // When we get here, at the end of main, the destructor of 'graphics' is called.
_

この動作を変更する方法はありませんが、オブジェクトの存続期間をさらに制御する必要がある場合は、これを回避できます。 1つの方法は、ポインターを使用することです。次の例では、簡単にするために生のポインタを使用します。ただし、この方法を採用するべきではありません。常に smart pointer を優先してください。

_// Allocated dynamically with 'new'. Constructor still called here, before 'main()' enters. 
// Destructor will not be called until you 'delete' it yourself.
GraphicsObject * graphics = new GraphicsObject;

int main()
{
    // Do stuff with 'graphics'
    // ...

    // 'graphics' destructor only gets called now because we have explicitly
    // deleted it. If we didn't delete the dynamic object, the destructor
    // would never be called. Not deleting would qualify as a "memory leak", BTW.
    delete graphics;
}
_

宣言の代わりにpointerを使用するby valueは、スコープを超えてオブジェクトの存続期間を延長するデフォルトの方法です。ただし、あなたの特定のケースでは、cleanup()の最後に呼び出すグラフィックスオブジェクトにmain()メソッドを導入することで、問題をより簡単に解決できると思います。 cleanup()がすべての関連リソースを解放するが、main()の最後に自動的に呼び出されたときにデストラクタが引き続き機能できる状態でオブジェクトを残すことを確認してください。

また、いくつかの異なる方法で実装できる Singleton Pattern を実装しようとしているようです。それについてインターネット検索を行うと、ニーズに合った他のシングルトン設定が見つかるかもしれません。

6
glampert