「生の」ポインタは管理されていません。つまり、次の行:
SomeKindOfObject *someKindOfObject = new SomeKindOfObject();
...付随するものdelete
が適切な時間に実行されない場合、メモリがリークします。
auto_ptr
これらのケースを最小限にするためstd::auto_ptr<>
に導入されました。ただし、2011標準以前のC ++の制限により、auto_ptr
メモリリークは非常に簡単です。ただし、次のような限られた場合には十分です。
void func() {
std::auto_ptr<SomeKindOfObject> sKOO_ptr(new SomeKindOfObject());
// do some work
// will not leak if you do not copy sKOO_ptr.
}
最も弱いユースケースの1つはコンテナです。これは、のコピーauto_ptr<>
が作成され、古いコピーが慎重にリセットされない場合、コンテナがポインタを削除してデータを失う可能性があるためです。
unique_ptr
代替として、C ++ 11が導入されましたstd::unique_ptr<>
。
void func2() {
std::unique_ptr<SomeKindofObject> sKOO_unique(new SomeKindOfObject());
func3(sKOO_unique); // now func3() owns the pointer and sKOO_unique is no longer valid
}
そのようなa unique_ptr<>
は、関数間で渡された場合でも、正しくクリーンアップされます。これは、ポインタの「所有権」を意味的に表すことによってこれを行います-「所有者」がそれをクリーンアップします。これにより、コンテナでの使用に最適です。
std::vector<std::unique_ptr<SomeKindofObject>> sKOO_vector();
とは異なりauto_ptr<>
、unique_ptr<>
ここでは行儀がよく、vector
サイズ変更時に、vector
バッキングストアのコピー中にオブジェクトが誤って削除されることはありません。
shared_ptr
そして weak_ptr
unique_ptr<>
確かに便利ですが、コードベースの2つの部分で同じオブジェクトを参照し、ポインターをコピーしながら、適切なクリーンアップを保証したい場合があります。たとえば、次を使用する場合、ツリーは次のようになりますstd::shared_ptr<>
。
template<class T>
struct Node {
T value;
std::shared_ptr<Node<T>> left;
std::shared_ptr<Node<T>> right;
};
この場合、ルートノードの複数のコピーを保持することもできます。ルートノードのすべてのコピーが破棄されると、ツリーは適切にクリーンアップされます。
これは、それぞれshared_ptr<>
がオブジェクトへのポインターだけでなくshared_ptr<>
、同じポインターを参照するすべてのオブジェクトの参照カウントも保持するために機能します。新しいものが作成されると、カウントが上がります。1つが破壊されると、カウントは減少します。カウントがゼロに達すると、ポインターはdelete
dになります。
そのため、これにより問題が発生します。二重リンク構造は最終的に循環参照になります。parent
ツリーにポインタを追加するとしますNode
。
template<class T>
struct Node {
T value;
std::shared_ptr<Node<T>> parent;
std::shared_ptr<Node<T>> left;
std::shared_ptr<Node<T>> right;
};
ここで、を削除するとNode
、それへの循環参照があります。これは、ことは決してないだろうdelete
その参照カウントがゼロになることはありませんので、D。
この問題を解決するには、次を使用しstd::weak_ptr<>
ます。
template<class T>
struct Node {
T value;
std::weak_ptr<Node<T>> parent;
std::shared_ptr<Node<T>> left;
std::shared_ptr<Node<T>> right;
};
現在、物事は正しく機能し、ノードを削除しても、親ノードへの参照がスタックしたままになりません。ただし、ツリーを歩くのは少し複雑になります。
std::shared_ptr<Node<T>> parent_of_this = node->parent.lock();
このようにして、ノードへの参照をロックできます。また、ノードを保持しているため、作業中にノードが消えないという合理的な保証がありますshared_ptr<>
。
make_shared
そして make_unique
今、いくつかの小さな問題がshared_ptr<>
あり、unique_ptr<>
それを解決する必要があります。次の2行に問題があります。
foo_unique(std::unique_ptr<SomeKindofObject>(new SomeKindOfObject()), thrower());
foo_shared(std::shared_ptr<SomeKindofObject>(new SomeKindOfObject()), thrower());
場合はthrower()
例外をスロー、両方のラインは、メモリリークが発生します。さらにshared_ptr<>
、参照カウントを、それが指すオブジェクトから遠く離して保持します。これは、2番目の割り当てを意味します。それは通常望ましくありません。
この問題を解決するためにstd::make_shared<>()
、C ++ 11が提供し、C ++ 14が提供std::make_unique<>()
します。
foo_unique(std::make_unique<SomeKindofObject>(), thrower());
foo_shared(std::make_shared<SomeKindofObject>(), thrower());
これで、どちらの場合でもthrower()
、例外がスローされても、メモリリークは発生しません。ボーナスとして、管理対象オブジェクトと同じメモリ空間にmake_shared<>()
参照カウントを作成する機会があります。これにより、例外の安全性を保証しながら、高速で数バイトのメモリを節約できます。
Qtに関する注意
ただし、C ++ 11より前のコンパイラをサポートする必要があるQtには、独自のガベージコレクションモデルがあります。多くQObject
のsには、ユーザーがdelete
それらを必要とせずに適切に破棄されるメカニズムがあります。
QObject
C ++ 11マネージポインターによって管理された場合のsの動作がわからないので、それshared_ptr<QDialog>
は良いアイデアとは言えません。私は確かに言うことはQtとの十分な経験を持っていないが、私は信じてい Qt5は、このユースケースのために調整されていること。