-1

次のものがあると仮定します。

//! SomeClass.hpp
class 
{
public:
    SomeClass( void );

   ~SomeClass( void ) 
    { 
       delete mFoo; 
       delete mBar; 
    }
    ...

private:
    Foo* mFoo;
    Bar* mBar;
    StackObj mStackFoo;
};

//! SomeClass.cpp
SomeClass::SomeClass( void )
{
     mFoo = new Foo;
     mBar = new Bar; 
     mStackFoo = StackObj( ... );
}

さて、ポインタを初期化すると、コンストラクタがSomeClassのメンバーの不要なコピーを作成し、メモリを割り当てるためだけにメモリを割り当ててから割り当てを解除することを理解しています。


これを回避する方法として、初期化リストを別の初期化関数(ヒープに割り当てられたメモリ用)と組み合わせて使用​​するのが一般的です。SaySomeClassには、として定義されたプライベートメンバー関数がありvoid initHeapMem( void )ます。その後、私たちはすることができます、

SomeClass::SomeClass( void )
    : mFoo( NULL ),
      mBar( NULL ),
      mStackFoo( ... )
{
     initHeapMem();
}

void SomeClass::initHeapMem( void )
{
    mFoo = new Foo;
    mBar = new Bar;
}

当然、これで問題はある程度解決します。ここでの問題は、実行されている別の関数呼び出しのオーバーヘッドがまだあることだと思います。

生のポインタに初期化リストを使用できない理由は、それらがスレッドセーフではないためです。何か問題が発生し、プログラムが例外をスローした場合でも、メモリリークが発生します。注:これは私が読んだ内容によるものです。これが間違っている場合はお詫びします


したがって、boost / C ++ 11を使用する#include <tr1/memory>と、ヘッダーファイルのディレクティブからスマートポインターを使用できます(STLを使用していると仮定)。

たとえば、を使用する場合はstd::unique_ptr< T >、次のようBar* mBarFoo* mFoo置き換えます。

std::unique_ptr< Foo > mFoo;
std::unique_ptr< Bar > mBar;

そうすれば、それが可能になります。

SomeClass::SomeClass( void )
   mFoo( new Foo ),
   mBar( new Bar ),
   mStackFoo( ... )
{
}

スマートポインタはメモリ割り当てを独自のコンストラクタで効果的にラップするためです。

これは優れたソリューションですが、私は個人的に、作成するすべてのヒープオブジェクトにスマートポインターを使用するわけではありません。C++コミュニティには同じように感じる人がいることを知っています。


tl; dr

これらすべてが邪魔にならないので、私が本当に疑問に思っているのは、上記でリストしたものとは別に、オブジェクト内でクラスメンバーを初期化する(特にC ++ 11の出現で)より効率的な代替手段があるかどうかです。

4

2 に答える 2

4

unique_ptrここで正しい解決策です。これにはいくつかの利点があります。

  • 所有権を明示的に文書化します。生のポインタに関する問題の1つは、それらが誰がそれらを所有しているかについて何も示さないことです。あなたunique_ptrには単一の所有者がいて、moveそれを譲渡したい場合は明示的に所有権を持っている必要があります。

  • 基本的にオーバーヘッドはありません。唯一のことunique_ptrは、とにかくやろうとしていた削除機能を呼び出すことです。これで、手動のメモリ管理を行わなくても、決定論的なメモリ動作のパフォーマンス上の利点が得られます。

  • RAIIのおかげで、スレッドと例外の安全性をはるかに簡単に実現できます。つまり、命令の順序についての悩みが少なくなり、クリーンアップコードが明確になりません。多くのC++03コードで例外を回避する原因となったすべての問題なしに、例外の利点を得ることができます。

shared_ptr私の経験では、必要な頻度は。よりはるかに少ないですunique_ptr。共有所有権は、主にテクスチャやオーディオファイルなどの不変のリソースがあり、ロードとコピーの両方にコストがかかるが、使用されていないときにアンロードする場合に役立ちます。shared_ptrまた、追加された安全性(特にスレッドセーフ)と参照カウントのオーバーヘッドを課します。

もちろん、欠点は、スマートポインタが構文上のオーバーヘッドを課すことです。それらは、生のポインタほど「ネイティブ」ではありません。そのために、、、、、などの独自の便利な関数をローリングしtypedefます。autodecltypemake_unique

于 2013-03-07T01:24:01.623 に答える
3

なぜあなたはこれをすることができないのですか?

SomeClass::SomeClass( void ) : 
mFoo(new Foo)
, mBar(new Bar)
{
}

それらは生のポインタであり、不要なコピーは作成されません。

また、初期化子リストを使用する理由は、コンストラクター本体の実行時にオブジェクトが有効な状態(つまり、すべてのメンバーが有効な値を持つ)になるためです。

SomeClass::SomeClass( void )
{
     //before this point, mFoo and mBar's values are unpredictable
     mFoo = new Foo;
     mBar = new Bar;
}

例外に関しては、SomeClassのデストラクタは、例外がコンストラクタ自体の内部でスローされた場合にのみ呼び出されません。

最後に、スレッドセーフであるかどうかについては、各スレッドに独自のSomeClassのコピーがあるかどうか、およびSomeClassに書き込まれる静的メンバーが含まれているかどうかによって異なります。

于 2013-03-07T01:17:55.227 に答える