c++11-スマートポインタと参照カウント
25075 ワード
一、本節の内容
このセクションの内容は次のとおりです.
標準ライブラリの拡張:スマートポインタと参照カウント RAIIと参照カウント
二、RAIIと引用数
従来のC++では、リソースを手動で解放するのは、必ずしもベストプラクティスではありません.私たちは資源を解放することを忘れて流出する可能性が高いからです.したがって、通常の方法は、オブジェクトにとって、関数を構築するときに空間を申請し、構造関数(役割ドメインから離れたときに呼び出される)を解析するときに空間を解放することです.つまり、RAIIリソース取得、すなわち初期化技術です.
何事にも例外があり、オブジェクトをフリーストレージに割り当てる必要があります.従来のC++では、
注意:リファレンスカウントはゴミ回収ではなく、リファレンス技術は使用されなくなったオブジェクトをできるだけ早く回収することができ、同時に回収の過程で成長時間の待ち時間を作ることもなく、資源のライフサイクルをより明確に示すことができる.
三、std::shared_ptr
しかし、
四、std::unique_ptr
make_uniqueは複雑ではありません.C++11はstd::make_を提供していません.Unique、自己実現可能:
なぜ提供されなかったのかについては、C++標準委員会のHerb Sutter議長がブログで「忘れられた」ためだと述べた.
独占である以上、言い換えれば複製できない.しかし、
五、std::weak_ptr
実行結果はA,Bともに破棄されない.これは、a,b内部のpointerが同時に
この問題を解決する方法は、弱参照ポインタ
上図では、最後のステップはBしか残っていませんが、Bにはスマートポインタが参照されていないため、このメモリリソースも解放されます.
正しいコードは次のとおりです.
まとめ
スマートポインタという技術は珍しくなく、多くの言語でよく見られる技術であり、C++1 xはこの技術を導入し、
このセクションの内容は次のとおりです.
標準ライブラリの拡張:スマートポインタと参照カウント
std::shared_ptr
std::unique_ptr
std::weak_ptr
二、RAIIと引用数
Objective-C
/Swift
を知っているプログラマーは、参照カウントの概念を知っているはずです.リファレンスカウントというカウントは、メモリの漏洩を防ぐために発生します.基本的な考え方は、動的に割り当てられたオブジェクトに対して参照カウントを行い、同じオブジェクトへの参照を1回増やすたびに参照オブジェクトの参照カウントが1回増加し、参照を削除するたびに参照カウントが1つ減少し、1つのオブジェクトの参照カウントがゼロに減少すると、指向するスタックメモリが自動的に削除されることです.従来のC++では、リソースを手動で解放するのは、必ずしもベストプラクティスではありません.私たちは資源を解放することを忘れて流出する可能性が高いからです.したがって、通常の方法は、オブジェクトにとって、関数を構築するときに空間を申請し、構造関数(役割ドメインから離れたときに呼び出される)を解析するときに空間を解放することです.つまり、RAIIリソース取得、すなわち初期化技術です.
何事にも例外があり、オブジェクトをフリーストレージに割り当てる必要があります.従来のC++では、
new
とdelete
を使用してリソースを解放するしかありません.C++11はスマートポインタの概念を導入し,参照カウントの考え方を用いて,プログラマが手動でメモリを解放する必要がなくなるようにした.これらのスマートポインタは、std::shared_ptr
/std::unique_ptr
/std::weak_ptr
を含み、ヘッダファイル
を含む必要がある.注意:リファレンスカウントはゴミ回収ではなく、リファレンス技術は使用されなくなったオブジェクトをできるだけ早く回収することができ、同時に回収の過程で成長時間の待ち時間を作ることもなく、資源のライフサイクルをより明確に示すことができる.
三、std::shared_ptr
std::shared_ptr
は、複数のshared_ptr
が共に1つのオブジェクトを指していることを記録することができ、表示された呼び出しdelete
を除去し、参照カウントがゼロになるとオブジェクトを自動的に削除するスマートポインタである.しかし、
std::shared_ptr
を使用するには、new
を使用して呼び出す必要があるため、コードはある程度非対称になる.std::make_shared
は、表示の使用を排除するために使用されるnew
であるため、std::make_shared
は、入力パラメータの作成中のオブジェクトを割り当て、このオブジェクトタイプのstd::shared_ptr
ポインタを返す.例:#include
#include
void foo(std::shared_ptr<int> i)
{
(*i)++;
}
int main()
{
// auto pointer = new int(10); // ,
// std::shared_ptr
auto pointer = std::make_shared<int>(10);
foo(pointer);
std::cout << *pointer << std::endl; // 11
// ,shared_ptr ,
return 0;
}
std::shared_ptr
は、get()
によって元のポインタを取得し、reset()
によって参照カウントを減少させ、get_count()
によってオブジェクトの参照カウントを表示することができる.例:auto pointer = std::make_shared<int>(10);
auto pointer2 = pointer; // +1
auto pointer3 = pointer; // +1
int *p = pointer.get(); //
std::cout << "pointer.use_count() = " << pointer.use_count() << std::endl; // 3
std::cout << "pointer2.use_count() = " << pointer2.use_count() << std::endl; // 3
std::cout << "pointer3.use_count() = " << pointer3.use_count() << std::endl; // 3
pointer2.reset();
std::cout << "reset pointer2:" << std::endl;
std::cout << "pointer.use_count() = " << pointer.use_count() << std::endl; // 2
std::cout << "pointer2.use_count() = " << pointer2.use_count() << std::endl; // 0, pointer2 reset
std::cout << "pointer3.use_count() = " << pointer3.use_count() << std::endl; // 2
pointer3.reset();
std::cout << "reset pointer3:" << std::endl;
std::cout << "pointer.use_count() = " << pointer.use_count() << std::endl; // 1
std::cout << "pointer2.use_count() = " << pointer2.use_count() << std::endl; // 0
std::cout << "pointer3.use_count() = " << pointer3.use_count() << std::endl; // 0, pointer3 reset
四、std::unique_ptr
std::unique_ptr
は、他のスマートポインタが同じオブジェクトを共有することを禁止し、コードのセキュリティを保証する独自のスマートポインタです.std::unique_ptr<int> pointer = std::make_unique<int>(10); // make_unique C++14
std::unique_ptr<int> pointer2 = pointer; //
make_uniqueは複雑ではありません.C++11はstd::make_を提供していません.Unique、自己実現可能:
template<typename T, typename ...Args>
std::unique_ptr make_unique( Args&& ...args ) {
return std::unique_ptr( new T( std::forward(args)... ) );
}
なぜ提供されなかったのかについては、C++標準委員会のHerb Sutter議長がブログで「忘れられた」ためだと述べた.
独占である以上、言い換えれば複製できない.しかし、
std::move
を使用して、他のunique_ptr
に移行することができます.例えば、#include
#include
struct Foo {
Foo() { std::cout << "Foo::Foo" << std::endl; }
~Foo() { std::cout << "Foo::~Foo" << std::endl; }
void foo() { std::cout << "Foo::foo" << std::endl; }
};
void f(const Foo &) {
std::cout << "f(const Foo&)" << std::endl;
}
int main() {
std::unique_ptr p1(std::make_unique());
// p1 ,
if (p1) p1->foo();
{
std::unique_ptr p2(std::move(p1));
// p2 ,
f(*p2);
// p2 ,
if(p2) p2->foo();
// p1 ,
if(p1) p1->foo();
p1 = std::move(p2);
// p2 ,
if(p2) p2->foo();
std::cout << "p2 " << std::endl;
}
// p1 ,
if (p1) p1->foo();
// Foo
}
五、std::weak_ptr
std::shared_ptr
をよく考えてみると、依然として資源が解放できない問題があることがわかります.次の例を見てください.#include
#include
class A;
class B;
class A {
public:
std::shared_ptr pointer;
~A() {
std::cout << "A " << std::endl;
}
};
class B {
public:
std::shared_ptrpointer;
~B() {
std::cout<「Bが される」<<std::endl;
}
};
int main() {
std::shared_ptra = std::make_shared();
std::shared_ptr b = std::make_shared();
a->pointer = b;
b->pointer = a;
return 0;
}
実行結果はA,Bともに破棄されない.これは、a,b内部のpointerが同時に
a,b
を参照しているためであり、a,b
の参照カウントはいずれも2となり、作用域を離れるとa,b
のスマートポインタが解析されるが、スマートによってこの領域の参照カウントが1減少し、これにより、a,b
オブジェクトが指すメモリ領域参照カウントがゼロではなく、外部ではこの領域を見つけることができなくなり、図に示すようにメモリ漏れが発生します.この問題を解決する方法は、弱参照ポインタ
std::weak_ptr
std::weak_ptr
を使用して弱参照である(比較的std::shared_ptr
は強参照である).弱参照は参照カウントの増加を引き起こすことはなく、弱参照を使用する場合、最終的な解放プロセスは下図のようになる.上図では、最後のステップはBしか残っていませんが、Bにはスマートポインタが参照されていないため、このメモリリソースも解放されます.
std::weak_ptr
には*
演算子と->
演算子がないので、リソースを操作することはできません.その唯一の役割は、std::shared_ptr
が存在するかどうかを確認することです.expired()
方法は、リソースが解放されていない場合、true
を返します.そうでなければfalse
を返します.正しいコードは次のとおりです.
#include
#include
class A;
class B;
class A {
public:
// A B weak_ptr
std::weak_ptr pointer;
~A() {
std::cout << "A " << std::endl;
}
};
class B {
public:
std::shared_ptrpointer;
~B() {
std::cout<「Bが される」<<std::endl;
}
};
int main() {
std::shared_ptra = std::make_shared();
std::shared_ptr b = std::make_shared();
a->pointer = b;
b->pointer = a;
return 0;
}
まとめ
スマートポインタという技術は珍しくなく、多くの言語でよく見られる技術であり、C++1 xはこの技術を導入し、
new
/delete
の乱用をある程度解消し、より成熟したプログラミングモデルである.