C++で動的に割り当てられたオブジェクトへのポインタのベクトルを使用する場合、メモリリークを回避する方法は?
質問
オブジェクトへのポインタのベクトルを使用しています。これらのオブジェクトはベースクラスから派生したもので、動的に割り当てられて格納されます。
例えばこんな感じです。
vector<Enemy*> Enemies;
で、Enemyクラスから派生して、派生クラスのメモリを動的に確保する、こんな感じです。
enemies.push_back(new Monster());
メモリリークなどの問題を回避するために注意すべきことは何ですか?
どのように解決するのですか?
std::vector
はいつものようにあなたのためにメモリを管理しますが、このメモリはオブジェクトではなくポインタになります。
このことが意味するのは、ベクターがスコープ外に出た時点で、あなたのクラスはメモリから失われるということです。例えば
#include <vector>
struct base
{
virtual ~base() {}
};
struct derived : base {};
typedef std::vector<base*> container;
void foo()
{
container c;
for (unsigned i = 0; i < 100; ++i)
c.push_back(new derived());
} // leaks here! frees the pointers, doesn't delete them (nor should it)
int main()
{
foo();
}
必要なことは、ベクターがスコープ外に出る前に、すべてのオブジェクトを削除することです。
#include <algorithm>
#include <vector>
struct base
{
virtual ~base() {}
};
struct derived : base {};
typedef std::vector<base*> container;
template <typename T>
void delete_pointed_to(T* const ptr)
{
delete ptr;
}
void foo()
{
container c;
for (unsigned i = 0; i < 100; ++i)
c.push_back(new derived());
// free memory
std::for_each(c.begin(), c.end(), delete_pointed_to<base>);
}
int main()
{
foo();
}
しかし、これは何らかのアクションを実行することを覚えておかなければならないので、保守が困難です。さらに重要なのは、要素の割り当てと割り当て解除のループの間に例外が発生した場合、割り当て解除のループは実行されず、どのみちメモリリークで立ち往生することになります! これは例外安全性と呼ばれ、割り当て解除が自動的に行われる必要がある重要な理由です。
より良いのは、ポインタが自分自身を削除する場合です。これはスマートポインタと呼ばれ、標準ライブラリが提供する
std::unique_ptr
と
std::shared_ptr
.
std::unique_ptr
は、あるリソースへの一意な(非共有、単一所有の)ポインタを表します。これはデフォルトのスマートポインタであり、生のポインタの使用を完全に置き換えるものであるべきです。
auto myresource = /*std::*/make_unique<derived>(); // won't leak, frees itself
std::make_unique
は C++11 標準からは見落とされていますが、自分で作ることができます。直接
unique_ptr
(を直接作成するには、以下のようにします。
make_unique
よりはお勧めしません)、こうしてください。
std::unique_ptr<derived> myresource(new derived());
一意なポインタは移動セマンティクスのみを持ち、コピーすることはできません。
auto x = myresource; // error, cannot copy
auto y = std::move(myresource); // okay, now myresource is empty
と、これだけで、コンテナの中で使うことができます。
#include <memory>
#include <vector>
struct base
{
virtual ~base() {}
};
struct derived : base {};
typedef std::vector<std::unique_ptr<base>> container;
void foo()
{
container c;
for (unsigned i = 0; i < 100; ++i)
c.push_back(make_unique<derived>());
} // all automatically freed here
int main()
{
foo();
}
shared_ptr
は参照カウントコピーセマンティクスを持ち、複数の所有者がオブジェクトを共有することを可能にします。これは
shared_ptr
がいくつ存在するかを追跡し、最後の 1 つが存在しなくなると (そのカウントがゼロになると)、ポインタを解放します。コピーは単に参照カウントを増やすだけです(そして移動は、より低い、ほとんど無料のコストで所有権を移します)。これらを作るには
std::make_shared
を使って作ります (あるいは上に示したように直接作りますが
shared_ptr
は内部でアロケーションを行う必要があるため、 一般に、より効率的で技術的に例外安全な方法として
make_shared
).
#include <memory>
#include <vector>
struct base
{
virtual ~base() {}
};
struct derived : base {};
typedef std::vector<std::shared_ptr<base>> container;
void foo()
{
container c;
for (unsigned i = 0; i < 100; ++i)
c.push_back(std::make_shared<derived>());
} // all automatically freed here
int main()
{
foo();
}
一般に
std::unique_ptr
をデフォルトとして使いたいことを思い出してください。さらに
std::shared_ptr
から構成することができます。
std::unique_ptr
(から構成することができます(逆はできません)ので、小さなことから始めても大丈夫です。
あるいは、オブジェクトへのポインタを格納するために作成されたコンテナ、例えば
boost::ptr_container
:
#include <boost/ptr_container/ptr_vector.hpp>
struct base
{
virtual ~base() {}
};
struct derived : base {};
// hold pointers, specially
typedef boost::ptr_vector<base> container;
void foo()
{
container c;
for (int i = 0; i < 100; ++i)
c.push_back(new Derived());
} // all automatically freed here
int main()
{
foo();
}
一方
boost::ptr_vector<T>
は C++03 で明らかに使用されていましたが、現在ではその関連性を語ることはできません。
std::vector<std::unique_ptr<T>>
を使用でき、おそらく同等のオーバーヘッドがほとんどないため、今は関連性を話すことはできませんが、この主張は検証されるべきです。
関係なく コード内で明示的に物事を解放しない . ラップして、リソース管理が自動的に行われるようにします。コードの中でポインターを生で所有することはないはずです。
ゲームでのデフォルトとして、私はおそらく
std::vector<std::shared_ptr<T>>
. いずれにせよ、私たちは共有することを期待していますし、プロファイリングがそうでないと言うまでは十分に速く、安全で、使いやすいからです。
関連
-
[解決済み】C++エラー。アーキテクチャ x86_64 に対して未定義のシンボル
-
[解決済み】クラステンプレートの引数リストがない
-
[解決済み] error: 'ostream' does not name a type.
-
[解決済み】cc1plus:エラー:g++で認識されないコマンドラインオプション"-std=c++11"
-
[解決済み】浮動小数点例外エラーが発生する: 8
-
[解決済み】Visual Studio 2013および2015でC++コンパイラーエラーC2280「削除された関数を参照しようとした」が発生する
-
[解決済み】エラー:不完全な型へのメンバーアクセス:前方宣言の
-
[解決済み】デバッグアサーションに失敗しました
-
[解決済み] C++の参照変数を返す習慣は悪なのか?
-
[解決済み】画像処理。コカ・コーラ缶」認識のためのアルゴリズム改良
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み】coutはstdのメンバではない
-
[解決済み】C++ クラスヘッダが含まれているときに「不明な型」があるのはなぜですか?重複
-
[解決済み】Cygwin Make bash コマンドが見つかりません。
-
[解決済み】テンプレートの引数1が無効です(Code::Blocks Win Vista) - テンプレートは使いません。
-
[解決済み】「std::operator」で「operator<<」にマッチするものがない。
-
[解決済み】1つ以上の多重定義されたシンボルが見つかる
-
[解決済み】なぜ、サイズ8の初期化されていない値を使用するのでしょうか?
-
[解決済み】デバッグアサーションに失敗しました
-
[解決済み】 ベクターの重複を消去してソートする最も効率的な方法は何ですか?
-
[解決済み] ポインターのベクトルを消去する [重複] 。