用語の意味と概念を理解する-RAII(Resource Acquisition is Initialization)


110

C ++開発者に、RAIIとは何か、なぜそれが重要なのか、そして他の言語との関連性があるかどうかについての説明を教えていただけませんか?

少し知っています。「リソース獲得は初期化」の略だと思います。ただし、その名前は、RAIIが何であるか(おそらく正しくない)を理解しているとは言えません。RAIIはスタック上のオブジェクトを初期化する方法であり、これらの変数がスコープから外れると、デストラクターが自動的にリソースがクリーンアップされる原因となる。

それでは、なぜ「スタックを使用してクリーンアップをトリガーする」(UTSTTC :)と呼ばれないのですか?そこから「らい」までどうやって行くの?

そして、スタック上に存在する何かのクリーンアップを引き起こすスタック上に何かをどのように作成できますか?また、RAIIが使えない場合はありますか?ガベージコレクションを希望していることはありますか?少なくとも、他のオブジェクトを管理しながら、いくつかのオブジェクトに使用できるガベージコレクターはありますか?

ありがとう。


27
UTSTTC?私はそれが好きです!RAIIよりもはるかに直感的です。RAIIの名前不適切ですが、C ++プログラマーが異議を唱えることはないでしょう。しかし、変更するのは簡単ではありません。;)
2009

10
これについてのStroustrupの見解:groups.google.com/group/comp.lang.c++
msg

3
@sbi:とにかく、歴史研究のためだけにコメントに+1してください。コンセプトの名前(RAII)について著者(B. Stroustrup)の視点を持つことは、独自の答えを得るのに十分興味深いと思います。
paercebal 2011年

1
@paercebal:歴史研究?今、あなたは私をとても古く感じさせました。:(私は当時、スレッド全体を読んでいて、自分自身をC ++の初心者だとさえ考えていませんでした。
sbi 2011年

3
+1、私は同じ質問をしようとしていました。コンセプトを理解しているが名前を理解できないのは私だけではないことをうれしく思います。RAOI-初期化時のリソース獲得と呼ばれるべきだったようです。
laurent

回答:


132

それでは、なぜ「スタックを使用してクリーンアップをトリガーする」(UTSTTC :)と呼ばれないのですか?

RAIIは何をすべきかを指示しています:コンストラクターでリソースを取得してください!追加します:1つのリソース、1つのコンストラクター。UTSTTCはそのアプリケーションの1つにすぎません。RAIIはそれだけではありません。

リソース管理は最低です。ここで、リソースは使用後にクリーンアップが必要なものです。多くのプラットフォームにまたがるプロジェクトの調査によると、バグの大部分はリソース管理に関連しており、Windowsでは特に多くの種類のオブジェクトとアロケータが原因です。

C ++では、例外と(C ++スタイル)テンプレートの組み合わせにより、リソース管理が特に複雑になります。中身をのぞくには、GOTW8を参照してください。


C ++は、コンストラクタが成功した場合にのみ、デストラクタが呼び出されることを保証します。これに依存して、RAIIは、平均的なプログラマが気付かないかもしれない多くの厄介な問題を解決できます。「ローカル変数は戻るたびに破棄される」以外のいくつかの例を次に示します。

まず、FileHandleRAIIを採用した過度に単純化したクラスから始めましょう。

class FileHandle
{
    FILE* file;

public:

    explicit FileHandle(const char* name)
    {
        file = fopen(name);
        if (!file)
        {
            throw "MAYDAY! MAYDAY";
        }
    }

    ~FileHandle()
    {
        // The only reason we are checking the file pointer for validity
        // is because it might have been moved (see below).
        // It is NOT needed to check against a failed constructor,
        // because the destructor is NEVER executed when the constructor fails!
        if (file)
        {
            fclose(file);
        }
    }

    // The following technicalities can be skipped on the first read.
    // They are not crucial to understanding the basic idea of RAII.
    // However, if you plan to implement your own RAII classes,
    // it is absolutely essential that you read on :)



    // It does not make sense to copy a file handle,
    // hence we disallow the otherwise implicitly generated copy operations.

    FileHandle(const FileHandle&) = delete;
    FileHandle& operator=(const FileHandle&) = delete;



    // The following operations enable transfer of ownership
    // and require compiler support for rvalue references, a C++0x feature.
    // Essentially, a resource is "moved" from one object to another.

    FileHandle(FileHandle&& that)
    {
        file = that.file;
        that.file = 0;
    }

    FileHandle& operator=(FileHandle&& that)
    {
        file = that.file;
        that.file = 0;
        return *this;
    }
}

(例外を除いて)構築が失敗した場合、他のメンバー関数(デストラクタでさえ)は呼び出されません。

RAIIは、無効な状態のオブジェクトの使用を回避します。オブジェクトを使用する前でも、それはすでに生活を楽にします。

ここで、一時オブジェクトを見てみましょう。

void CopyFileData(FileHandle source, FileHandle dest);

void Foo()
{
    CopyFileData(FileHandle("C:\\source"), FileHandle("C:\\dest"));
}

処理するエラーケースは3つあります。ファイルを開くことができない、1つのファイルしか開くことができない、両方のファイルを開くことができるが、ファイルのコピーに失敗した。非RAII実装でFooは、3つのケースすべてを明示的に処理する必要があります。

RAIIは、1つのステートメント内で複数のリソースが取得された場合でも、取得されたリソースを解放します。

次に、いくつかのオブジェクトを集約します。

class Logger
{
    FileHandle original, duplex;   // this logger can write to two files at once!

public:

    Logger(const char* filename1, const char* filename2)
    : original(filename1), duplex(filename2)
    {
        if (!filewrite_duplex(original, duplex, "New Session"))
            throw "Ugh damn!";
    }
}

のコンストラクターLoggerが失敗した場合originalfilename1オープンできなかったため)、duplexのコンストラクターが失敗した場合(filename2オープンできなかったため)、またはLoggerのコンストラクター本体内のファイルへの書き込みが失敗した場合、のコンストラクターは失敗します。これらのいずれの場合でも、Loggerのデストラクタは呼び出されません-したがってLogger、のデストラクタを使用してファイルを解放することはできません。しかし、original構築された場合、そのデストラクタは、Loggerコンストラクタのクリーンアップ中に呼び出されます。

RAIIは部分的な構築後のクリーンアップを簡素化します。


負の点:

ネガティブポイント?すべての問題はRAIIとスマートポインターで解決できます;-)

RAIIは、取得の遅延が必要な場合に扱いにくく、集約されたオブジェクトをヒープにプッシュします。
ロガーにが必要だと想像してくださいSetTargetFile(const char* target)。その場合、まだのメンバーでLoggerある必要があるハンドルは、ヒープ上に存在する必要があります(たとえば、ハンドルの破棄を適切にトリガーするために、スマートポインター内)。

私は本当にガベージコレクションを望んでいません。C#を実行するとき、気にする必要のない至福の瞬間を感じることがありますが、さらに、決定論的な破壊によって作成できるすべてのクールなおもちゃを見逃しています。(使用IDisposableしてもカットされません)

私は、GCの恩恵を受ける可能性のある特に複雑な構造を1つ持っていました。この場合、「単純な」スマートポインタが複数のクラスに対する循環参照を引き起こします。強いポインタと弱いポインタのバランスを慎重にとることで混乱しましたが、何かを変更したいときはいつでも、大きな関係図を調査する必要があります。GCの方が良かったかもしれませんが、一部のコンポーネントはできるだけ早くリリースする必要があるリソースを保持していました。


FileHandleサンプルに関するメモ:これは完全なものではなく、単なるサンプルでしたが、正しくありませんでした。指摘してくれたJohannes Schaubと、それを正しいC ++ 0xソリューションに変えてくれたFredOverflowに感謝します。時間が経つにつれ、ここに記載されているアプローチで解決しまし


1
+1 GCとASAPがメッシュしないことを示します。傷つくことはあまりありませんが、診断するのは簡単ではありません:/
Matthieu M.

10
特に私が以前の読みを見落とした一文。「RAII」は「コンストラクター内のリソースを取得する」と言っているとおっしゃっていました。それは意味があり、ほぼ「RAII」の逐語的な言い換えです。今、私はそれをさらに良くします(もし私ができるなら、あなたに再度投票します:)
チャーリーフラワー

2
GCの主な利点の1つは、メモリ割り当てフレームワークが、「安全でない」コードがない場合にぶら下がる参照の作成を防止できることです(「安全でない」コードが許可されている場合、フレームワークは何も防止できません)。多くの場合、GCは、所有者が明確でなくクリーンアップが不要な文字列などの共有不変オブジェクトを処理するときに、RAIIよりも優れています。ほとんどのアプリケーションには不変オブジェクト(GCが最適)とクリーンアップが必要なオブジェクト(RAIIが最適)が混在するため、より多くのフレームワークがGCとRAIIを組み合わせようとしないのは残念です。
スーパーキャット2012年

@supercat:私は一般にGCが好きですが、GCが「理解する」リソースのリソースに対してのみ機能します。たとえば、.NET GCはCOMオブジェクトのコストを認識していません。ループでそれらを単に作成および破棄する場合、アプリケーションは、GCを実行することさえ考えずに、アドレス空間または仮想メモリ(最初に来るものは何でも)に関してアプリケーションを喜んで実行します。---さらに、完全にGCされた環境でも、確定的破壊の力を逃します。同じパターンを他のアーティファクトに適用できます(たとえば、certian条件下でのUI要素の表示)。
peterchen

@peterchen:OOP関連の多くの考え方に欠けていると私が思う1つのことは、オブジェクトの所有権の概念です。多くの場合、所有権を追跡することは、リソースを持つオブジェクトには明らかに必要ですが、リソースのない可変オブジェクトにも必要です。一般に、オブジェクトは、変更可能な状態を、共有される可能性のある不変オブジェクトへの参照で、またはオブジェクトが排他的所有者である変更可能なオブジェクトでカプセル化する必要があります。そのような排他的な所有権は、必ずしも排他的な書き込みアクセスを意味するわけではありませんが、Foo所有しBar、それをBoz変更する場合、...
supercat

42

すばらしい答えがあるので、忘れてしまったことをいくつか追加します。

0. RAIIはスコープに関するものです

RAIIは両方についてです:

  1. コンストラクターで(どのリソースでも)リソースを取得し、デストラクターでリソースの取得を解除します。
  2. 変数が宣言されたときにコンストラクターが実行され、変数がスコープ外になったときにデストラクターが自動的に実行されます。

他の人はすでにそれについて答えたので、詳しくは述べません。

1. JavaまたはC#でコーディングする場合、すでにRAIIを使用しています...

MONSIEUR JOURDAIN:なんと!「ニコール、スリッパを持ってきて、寝酒をくれ」って言ったら、それは散文ですか?

哲学のマスター:はい、サー。

MONSIEUR JOURDAIN:40年以上の間、私はそれについて何も知らずに散文を話してきました。

—モリエール:中産階級の紳士、第2幕、シーン4

Monsieur Jourdainが散文で行ったように、C#やJavaの人々でさえすでにRAIIを使用していますが、隠れた方法で使用されています。たとえば、次のJavaコード(C#ではで置き換えsynchronizedて同じ方法で記述しますlock):

void foo()
{
   // etc.

   synchronized(someObject)
   {
      // if something throws here, the lock on someObject will
      // be unlocked
   }

   // etc.
}

...はすでにRAIIを使用しています。mutexの取得はキーワード(synchronizedまたはlock)で行われ、スコープの終了時に取得解除が行われます。

その表記法はとても自然で、RAIIについて聞いたことがない人でもほとんど説明を必要としません。

JavaとC#に対するC ++の利点は、RAIIを使用して何でも作成できることです。たとえば、C ++ にsynchronizedlockに相当する組み込みの直接ビルドインはありませんが、それらは引き続き使用できます。

C ++では、次のように記述されます。

void foo()
{
   // etc.

   {
      Lock lock(someObject) ; // lock is an object of type Lock whose
                              // constructor acquires a mutex on
                              // someObject and whose destructor will
                              // un-acquire it 

      // if something throws here, the lock on someObject will
      // be unlocked
   }

   // etc.
}

Java / C#の方法で簡単に記述できます(C ++マクロを使用)。

void foo()
{
   // etc.

   LOCK(someObject)
   {
      // if something throws here, the lock on someObject will
      // be unlocked
   }

   // etc.
}

2. RAIIには別の用途があります

ホワイトラビット:[歌う]遅刻/遅刻/非常に重要な日付。/「こんにちは」と言う時間はありません。/ さようなら。/私は遅れています、私は遅れています、私は遅れています。

—不思議の国のアリス(ディズニー版、1951年)

(オブジェクト宣言で)コンストラクターが呼び出されるタイミングと、対応するデストラクターが(スコープの出口で)呼び出されるタイミングがわかっているため、1行だけでほぼ魔法のコードを記述できます。C ++ワンダーランドへようこそ(少なくとも、C ++開発者の観点から)。

たとえば、カウンターオブジェクトを作成し(これを演習として使用します)、変数を宣言するだけで使用できます(上記のロックオブジェクトを使用した場合と同様)。

void foo()
{
   double timeElapsed = 0 ;

   {
      Counter counter(timeElapsed) ;
      // do something lengthy
   }
   // now, the timeElapsed variable contain the time elapsed
   // from the Counter's declaration till the scope exit
}

もちろん、これも、マクロを使用してJava / C#で記述できます。

void foo()
{
   double timeElapsed = 0 ;

   COUNTER(timeElapsed)
   {
      // do something lengthy
   }
   // now, the timeElapsed variable contain the time elapsed
   // from the Counter's declaration till the scope exit
}

3. C ++にはなぜ欠けているのfinallyですか?

[叫ぶ]それは最後のカウントダウンです!

—ヨーロッパ:ファイナルカウントダウン(申し訳ありませんが、引用符が足りませんでした。ここで... :-)

このfinally句はC#/ Javaで使用され、スコープが終了した場合(returnまたは例外がスローされた場合)にリソースを破棄します。

明敏な仕様の読者は、C ++にfinally句がないことに気づくでしょう。RAIIはすでにリソースの破棄を処理しているため、C ++では必要ないため、これはエラーではありません。(そして、私を信じて、C ++デストラクタを書くことは、適切なJava finally節を書くこと、またはC#の正しいDisposeメソッドを書くことよりもはるかに簡単です)。

それでも、時には、finally節はクールです。C ++でできますか?はい、できます!また、RAIIの代替使用を使用します。

結論:RAIIはC ++の哲学以上のものです:それはC ++です

RAII?これはC ++です!!!

— C ++開発者の憤慨したコメント、あいまいなスパルタ王と彼の300人の友人によって恥知らずにコピーされた

C ++である程度の経験を積むと、RAIIの観点から、コンストラクタとデストラクタの自動実行の観点から考え始めます。

スコープの観点から考え始めると、{and }文字はコードで最も重要なものになります。

そして、RAIIの点でほぼすべてが適切に適合します:例外の安全性、ミューテックス、データベース接続、データベース要求、サーバー接続、クロック、OSハンドルなど、そして最後に、メモリです。

最終的にすべての変更をコミットするかどうかを決定するまで、行とコードの行を実行する「トランザクションプログラミング」スタイルで書き込むこともできるため、データベース部分は無視できません。または、不可能であれば、すべての変更を元に戻します(各行が少なくとも強力な例外保証を満たす限り)。(トランザクションプログラミングについては、このハーブのサッターの記事の 2番目の部分を参照してください)。

そして、パズルのように、すべてが収まります。

RAIIはC ++の大部分を占めているため、C ++はそれなしではC ++にはなり得ません。

これは、経験豊富なC ++開発者がRAIIに夢中になっている理由と、RAIIが別の言語を試すときに最初に検索する理由であることを説明しています。

そしてそれは、ガベージコレクター自体が素晴らしい技術であるにもかかわらず、C ++開発者の観点からはそれほど印象的でない理由を説明しています。

  • RAIIは、GCで処理されるほとんどのケースをすでに処理しています
  • GCは、純粋に管理されたオブジェクトの循環参照を使用して、RAIIよりも適切に処理します(弱いポインターのスマートな使用によって軽減されます)。
  • それでも、GCはメモリに制限されていますが、RAIIはあらゆる種類のリソースを処理できます。
  • 上記のように、RAIIはさらに多くのことができます...

Javaファン:すべてのメモリを処理し、多くの潜在的なバグからユーザーを解放するため、GCはRAIIよりもはるかに有用だと思います。GCを使用すると、循環参照を作成し、参照を返して保存することができます。また、参照を誤って取得するのが困難です(存続期間が短いと考えられるオブジェクトへの参照を保存すると、存続時間が長くなります。これは一種のメモリリークですが、それだけが問題です)。 。GCによるリソースの処理は機能しませんが、アプリケーション内のほとんどのリソースには些細なライブサイクルがあり、残りのいくつかのリソースは大した問題ではありません。GCとRAIIの両方を使用できればいいのですが、それは不可能のようです。
maaartinus

16

1
それらのいくつかは私の質問と一致していますが、検索でそれらが表示されず、新しい質問を入力した後に表示される「関連する質問」リストも表示されませんでした。リンクをありがとう。
チャーリーフラワーズ

1
@Charlie:組み込みの検索は、いくつかの点で非常に弱いです。タグ構文( "[topic]")の使用は非常に役立ち、多くの人がgoogle ...を使用しています
dmckee --- ex-moderator kitten

10

RAIIはC ++デストラクタセマンティクスを使用してリソースを管理しています。たとえば、スマートポインターについて考えてみます。オブジェクトのアドレスでこのポインターを初期化するポインターのパラメーター化されたコンストラクターがあります。スタックにポインタを割り当てます。

SmartPointer pointer( new ObjectClass() );

スマートポインターがスコープから外れると、ポインタークラスのデストラクターが接続されたオブジェクトを削除します。ポインタはスタックに割り当てられ、オブジェクトはヒープに割り当てられます。

RAIIが役に立たない場合があります。たとえば、参照カウントスマートポインター(boost :: shared_ptrなど)を使用して、サイクルのあるグラフのような構造を作成すると、サイクル内のオブジェクトがお互いに解放されなくなるため、メモリリークに直面するリスクがあります。ガベージコレクションはこれに役立ちます。


2
したがって、UCDSTMRと呼ぶ必要があります:)
ダニエルダラナス

もう一度考えてみると、UDSTMRの方が適切だと思います。言語(C ++)が指定されているため、頭字語に「C」という文字は必要ありません。UDSTMRは、デストラクタセマンティクスを使用してリソースを管理することを意味します。
Daniel Daranas 2013

9

前回の回答よりももう少し強くお願いします。

RAII、Resource Acquisition Is Initializationは、取得したすべてのリソースをオブジェクトの初期化のコンテキストで取得する必要があることを意味します。これは、「裸の」リソース獲得を禁止します。その理由は、C ++でのクリーンアップは、関数呼び出しではなくオブジェクトベースで機能するということです。したがって、すべてのクリーンアップは、関数呼び出しではなく、オブジェクトによって行われる必要があります。この意味で、C ++はJavaよりもオブジェクト指向です。Javaクリーンアップは、finally句の関数呼び出しに基づいています。


すばらしい答えです。そして、「オブジェクトの初期化」は「コンストラクタ」を意味しますね?
チャーリーフラワー

@チャーリー:はい、特にこの場合。
MSalters 2010年

8

私はcpitisに同意します。ただし、リソースはメモリだけでなく、何でもかまいません。リソースは、ファイル、クリティカルセクション、スレッド、またはデータベース接続です。

これは、リソースを制御するオブジェクトの構築時にリソースが取得されるため、リソース取得は初期化と呼ばれます。コンストラクターが失敗した場合(つまり、例外のため)、リソースは取得されません。次に、オブジェクトがスコープ外になると、リソースが解放されます。c ++は、正常に構築されたスタック上のすべてのオブジェクトが破棄されることを保証します(これには、スーパークラスコンストラクターが失敗した場合でも、基本クラスとメンバーのコンストラクターが含まれます)。

RAIIの背後にある合理的な理由は、リソース取得の例外を安全にすることです。取得したすべてのリソースは、例外が発生しても適切に解放されること。ただし、これはリソースを取得するクラスの品質に依存します(これは例外セーフである必要があり、これは困難です)。


名前の背後にある根拠を説明していただきありがとうございます。私が理解しているように、RAIIを「(コンストラクタベースの)初期化以外のメカニズムを通じてリソースを取得しないでください」と言い換えることもできます。はい?
チャーリーフラワーズ

はい、これは私の方針ですが、自分のRAIIクラスは例外的に安全である必要があるため、自分で書くことには非常に注意しています。それらを作成するときは、専門家によって作成された他のRAIIクラスを再利用することで、例外の安全性を確保しようとします。
iain

書くのが難しいとは思いませんでした。クラスが十分に小さい場合、それらはまったく難しくありません。
Rob K

7

ガベージコレクションの問題は、RAIIにとって重要な決定論的破壊を失うことです。変数がスコープ外になると、オブジェクトが再利用されるのはガベージコレクタに任されます。オブジェクトが保持しているリソースは、デストラクタが呼び出されるまで保持され続けます。


4
問題は決定論だけではありません。本当の問題は、ファイナライザ(Javaネーミング)がGCの邪魔になることです。GCは、死んだオブジェクトをリコールしないので効率的ですが、無視して忘却の対象にします。GCは、それらが呼び出されることを保証するために、異なる方法でファイナライザを持つオブジェクトを追跡する必要があります
dribeas -デビッド・ロドリゲス

1
java / c#を除いて、ファイナライザではなく、finallyブロックでクリーンアップする可能性があります。
jk。

4

RAIIはResource Allocation Is Initializationから来ています。基本的には、コンストラクターが実行を完了すると、構築されたオブジェクトが完全に初期化され、使用できるようになります。また、デストラクタがオブジェクトが所有するすべてのリソース(メモリ、OSリソースなど)を解放することも意味します。

ガベージコレクションされた言語/テクノロジ(Java、.NETなど)と比較すると、C ++ではオブジェクトの寿命を完全に制御できます。スタックに割り当てられたオブジェクトの場合、オブジェクトのデストラクタがいつ呼び出されるか(実行がスコープ外になるとき)、ガベージコレクションの場合は実際には制御されないことがわかります。C ++でスマートポインター(例:boost :: shared_ptr)を使用しても、ポイントされたオブジェクトへの参照がない場合は、そのオブジェクトのデストラクターが呼び出されることがわかります。


3

そして、スタック上に存在する何かのクリーンアップを引き起こすスタック上に何かをどのように作成できますか?

class int_buffer
{
   size_t m_size;
   int *  m_buf;

   public:
   int_buffer( size_t size )
     : m_size( size ), m_buf( 0 )
   {
       if( m_size > 0 )
           m_buf = new int[m_size]; // will throw on failure by default
   }
   ~int_buffer()
   {
       delete[] m_buf;
   }
   /* ...rest of class implementation...*/

};


void foo() 
{
    int_buffer ib(20); // creates a buffer of 20 bytes
    std::cout << ib.size() << std::endl;
} // here the destructor is called automatically even if an exception is thrown and the memory ib held is freed.

int_bufferのインスタンスが存在するときは、そのサイズが必要であり、必要なメモリを割り当てます。スコープ外になると、デストラクタが呼び出されます。これは、同期オブジェクトなどに非常に役立ちます。検討する

class mutex
{
   // ...
   take();
   release();

   class mutex::sentry
   {
      mutex & mm;
      public:
      sentry( mutex & m ) : mm(m) 
      {
          mm.take();
      }
      ~sentry()
      {
          mm.release();
      }
   }; // mutex::sentry;
};
mutex m;

int getSomeValue()
{
    mutex::sentry ms( m ); // blocks here until the mutex is taken
    return 0;  
} // the mutex is released in the destructor call here.

また、RAIIが使えない場合はありますか?

いいえ、そうではありません。

ガベージコレクションを希望していることはありますか?少なくとも、他のオブジェクトを管理しながら、いくつかのオブジェクトに使用できるガベージコレクターはありますか?

決して。ガベージコレクションは、動的リソース管理の非常に小さなサブセットのみを解決します。


私はJavaとC#をほとんど使用していなかったので、見逃すことはありませんでしたが、リソース管理に関しては、RAIIを使用できなかったため、GCを使用する必要があるときにスタイルが窮屈になりました。
Rob K

1
私はC#をよく使用しており、100%同意します。実際、非決定的GCは言語の責任であると考えています。
Nemanja Trifunovic

2

ここにはすでに良い答えがたくさんありますが、私は付け加えておきたいと思います
。RAIIの簡単な説明は、C ++では、スタックに割り当てられたオブジェクトがスコープ外になると破棄されるということです。つまり、オブジェクトデストラクタが呼び出され、必要なクリーンアップをすべて実行できます。
つまり、「新規」なしでオブジェクトが作成された場合、「削除」は必要ありません。そして、これは「スマートポインタ」の背後にある考え方でもあります。それらはスタック上に存在し、基本的にヒープベースのオブジェクトをラップします。


1
いいえ、ありません。しかし、ヒープ上にスマートポインターを作成する正当な理由はありますか?ちなみに、スマートポインターは、R​​AIIが役立つ場所のほんの一例にすぎません。
E Dominique

1
多分私の「スタック」対「ヒープ」の使用は少しずさんです-「スタック」上のオブジェクトによって、私はローカルオブジェクトを意味しました。当然、ヒープなどのオブジェクトの一部にすることができます。「ヒープ上にスマートポインターを作成する」とは、スマートポインター自体にnew / deleteを使用することを意味しました。
E Dominique

1

RAIIはResource Acquisition Is Initializationの頭字語です。

この手法はC ++に非常に独特です。これは、コンストラクタとデストラクタの両方と、渡される引数に一致するコンストラクタ、またはデフォルトコンストラクタが呼び出される最悪の場合にデフォルトコンストラクタが呼び出される&デストラクタがサポートされるため、デフォルトコンストラクタが呼び出される場合はデフォルトコンストラクタと呼ばれるためです。 C ++コンパイラによって追加されたものは、C ++クラスのデストラクタを明示的に記述しなかった場合に呼び出されます。これは、自動管理されているC ++オブジェクト、つまり、フリーストア(new、new [] / delete、delete [] C ++演算子を使用して割り当て/割り当て解除されたメモリ)を使用していない場合にのみ発生します。

RAIIテクニックは、この自動管理オブジェクト機能を利用して、new / new []を使用してヒープ/フリーストア上に作成されたオブジェクトを明示的に要求し、delete / delete []を呼び出して明示的に破棄する必要があります。 。自動管理オブジェクトのクラスは、ヒープ/空きストアメモリに作成されたこの別のオブジェクトをラップします。したがって、自動管理オブジェクトのコンストラクターが実行されると、ラップされたオブジェクトがヒープ/フリーストアメモリ上に作成され、自動管理オブジェクトのハンドルがスコープ外になると、その自動管理オブジェクトのデストラクターが自動的に呼び出され、ラップされたオブジェクトがオブジェクトは削除を使用して破棄されます。OOPの概念では、そのようなオブジェクトをプライベートスコープ内の別のクラス内にラップすると、ラップされたクラスのメンバーとメソッドにアクセスできなくなります。これが、スマートポインタ(ハンドルクラス)が設計されている理由です。これらのスマートポインターは、ラップされたオブジェクトを型付きオブジェクトとして外部の世界に公開します。公開されたメモリオブジェクトを構成するメンバー/メソッドを呼び出すことができます。スマートポインターには、さまざまなニーズに基づいてさまざまなフレーバーがあることに注意してください。詳細については、Andrei AlexandrescuによるModern C ++プログラミングまたはブーストライブラリ(www.boostorg)のshared_ptr.hpp実装/ドキュメントを参照してください。これがRAIIの理解に役立つことを願っています。詳細については、Andrei AlexandrescuによるModern C ++プログラミングまたはブーストライブラリ(www.boostorg)のshared_ptr.hpp実装/ドキュメントを参照してください。これがRAIIの理解に役立つことを願っています。詳細については、Andrei AlexandrescuによるModern C ++プログラミングまたはブーストライブラリ(www.boostorg)のshared_ptr.hpp実装/ドキュメントを参照してください。これがRAIIの理解に役立つことを願っています。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.