shared_ptrの基本的な使い方
(A)unique_ptrをと比較
比較します |
shared_ptrの |
unique_ptrを |
リマーク |
初期化 |
①shared_ptr<T> SP。 sp.reset(新しいT()); ②shared_ptr<T> SP(新しいT())。 ③shared_ptr<T> SP1 = SP; // コピーコンストラクタ ④autoSP = make_shared <整数>(10)。 |
①unique_ptr<T>まで。 up.reset(新しいT()); ②unique_ptr<T>アップ(新しいT()); ③unique_ptr<T> :: STD UP1 =移動(アップ); // モバイル設定 ④autoアップ= make_unique <整数>(10)。 |
宣言され、両方のコンストラクタ許可されない、明示的、暗黙的型変換、 shared_ptrのような<整数> SP =新しいINT (10)。 |
条件付きの |
このように、IF(SP){...} |
如、IF(最大){...} |
二つは両方とも)(オーバーロードされた演算子ブール値 |
逆参照 |
* SP |
*アップ |
逆参照、それが指しているオブジェクトを取得します。 |
- > MEM |
SP-> MEM |
アップ> MEM |
過負荷 - >演算子 |
取得する() |
sp.get() |
up.get() |
保存されたリターンスマートポインタ素手では、使用するように注意してください。 |
p.swap(Q) |
sp.swap(Q)。 |
up.swap(Q)。 |
ポインタpとqを切り替えます |
ユニークな操作 |
①shared_ptr<T> P(Q); //コピーコンストラクタ ②p= Q; //代入 ③p.unique(); p.use_count IF()は、trueを返し、そうでない場合はfalse 1です。 ④p.use_count()が強く、カウントを参照返す// |
①up= nullptr;上向きオブジェクトを解放し、アップ空白。 ②up.release(); //までポインタの制御を放棄するために、ポインタが裸戻り、アップブランク ③up.reset();リリースは、ポイントをオブジェクト。 up.reset(Q); qは、生のポインタです。Qの参照先を補うために指しています。 up.reset(nullptr);置空 |
注意: ①unique_ptr コピーと割り当てが、移動させることができるではありません。 ②releaseunique_ptrを、オブジェクトとその元の管理との間の接続を切断します。もう一つは、一般的にスマートポインタを初期化するために使用されます。 |
(B)指定されたデバイスを削除します
1のshared_ptr <T> SP1(Q 、deleter1); 異なるとunique_ptrを除去しないshared_ptrのタイプの構成要素。shared_ptrの<T> SP2(Q、と仮定し 、deleter2) SP1およびSP2削除手段が異なっていますが、両方が同じタイプであるが、shared_ptrの容器のベクトル<< T >>同じ型に配置することができます。
2.のstd :: unique_ptrを異なると、カスタムのstd :: shared_ptrのサイズを変更しません削除します。それは、常に裸のポインタの2倍のサイズです。
shared_ptrのを使用している場合3. 動的配列を管理するために、あなたは指定されたデバイスを削除する必要があります。デフォルトの削除は、オブジェクトの配列をサポートしていませんので。shared_ptr <整数> SP(新しい新しいINT [10]、:: default_delete STD < INT [] >)。
4.削除共通機能することができ、オブジェクトやラムダ式を機能。デフォルトはSTD :: default_deleteを削除することで、その内部の機能は、削除によって達成されます。
II。のstd :: shared_ptrの分析
(A)のstd :: shared_ptrのメモリモデル
1のshared_ptrが含まオブジェクトと制御ブロックを示すポインタへのポインタ。std :: shared_ptrの管理対象オブジェクトのそれぞれが持っている制御ブロックだけでなく、参照カウントが含まれているが、だけでなく、カスタムフィルタのコピーのコピーが含まれており、ディスペンサーおよび他の追加のデータを削除します。
2. 制御ブロックは、ルールを作成するには:
(1)のstd :: make_sharedは常に制御ブロックを作成します。
(2)から出発構成はstd :: shared_ptrのタイトルポインタを含み、制御ブロックが作成されます。(このような制御ブロックにunique_ptrを場合unique_ptrをブランクながら自体unique_ptrを、制御ブロックを使用しないためSTD :: shared_ptrのは、作成されます)
(3)当std::shared_ptr构造函数使用裸指针作为实参时,会创建一个控制块。这意味从同一个裸指针出发来构造不止一个std::shared_ptr时会创建多重的控制块,也意味着对象会被析构多次。如果想从一个己经拥有控制块的对象出发创建一个std::shared_ptr,可以传递一个shared_ptr或weak_ptr而非裸指针作为构造函数的实参,这样则不会创建新的控制块。
【经验】
①尽可能避免将裸指针传递给一个std::shared_ptr的构造函数,常用的替代手法是使用std::make_shared。
②如果必须将一个裸指针传递给shared_ptr的构造函数,就直接传递new运算符的结果,而非传递一个裸指针变量。如shared_ptr<Widget> spw (new Widget, logginDel);
③不要将this指针返回给shared_ptr。当希望将this指针托管给shared_ptr时,类需要继承自std::enable_shared_from_this,并且从shared_from_this()中获得shared_ptr指针。(具体见《enable_shared_from_this》部分的分析)
3. 引用计数(强引用计数)
(1)shared_ptr的构造函数会使该引用计数递增,而析构函数会使该计数递减。但移动构造时表示从一个己有的shared_ptr移动构造到一个新的shared_ptr。这意味着一旦新的shared_ptr产生后,原有的shared_ptr会被置空,其结果是引用计数没有变化。
(2)复制赋值同时执行两种操作(如sp1 和sp2是指向不同对象的shared_ptr,则sp1 = sp2时,将修改sp1使得其指向sp2所指的对象。而最初sp1所指向的对象的引用计数递减,同时sp2所指向的对象引用计数递增)
(3)reset函数,如果不带参数时,则引用计数减1。如果不带参数时,如sp.reset(p)则sp原来指向的对象引用计数减1,同时sp指向新的对象(p)
(4)如果实施一次递减后最后的引用计数变成0,即不再有shared_ptr指向该对象,则会被shared_ptr析构掉。
(5)引用计数的递增和递减是原子操作,即允许不同线程并发改变引用计数。
【编程实验】shared_ptr的陷阱分析
#include <iostream> #include <vector> #include <memory> // for smart pointer using namespace std; class Widget{}; void func(shared_ptr<Widget> sp){} int funcException() { /*throw 1;*/ return 0; } //假设该函数会抛出异常 void demo(shared_ptr<int> sp, int f) { } int main() { //1. 陷阱:用同一裸指针创建多个shared_ptr //1.1 错误做法 auto pw = new Widget; std::shared_ptr<Widget> spw1(pw); //强引用计数为1,为pw创建一个控制块 //std::shared_ptr<Widget> spw2(pw); //强引用计数为1,为pw创建另一个新的控制块,会导致多次析构 auto sp = new Widget; func(shared_ptr<Widget>(sp)); //慎用裸指针,sp将在func结束后被释放! //1.2 正确做法 std::shared_ptr<Widget> spw3(spw1); //ok,pw的强引用计数为2。使用与spw1同一个控制块。 std::shared_ptr<Widget> spw4(new Widget); //将new的结果直接传递给shared_ptr std::shared_ptr<Widget> spw5 = std::make_shared<Widget>(); //强烈推荐的做法! //2. 陷阱:在函数实参中创建shared_ptr //2.1 shared_ptr与异常安全问题 //由于参数的计算顺序因编译器和调用约定而异。假定按如下顺序计算 //A.先前new int,然后funcException(); //B.假设恰好此时funcException产生异常。 //C.因异常出现shared_ptr还来不及创建,于是int内存泄露 demo(shared_ptr<int>(new int(100)), funcException()); //2.2 正确做法 auto p1 = std::make_shared<int>(100); demo(p1, funcException()); //3. 陷阱:shared_ptr的循环引用(应避免)(见第22课 weak_ptr) //4. 删除器 auto deleter1 = [](Widget* pw) {cout << "deleter1"<< endl; delete pw; }; auto deleter2 = [](Widget* pw) {cout << "deleter2"<< endl; delete pw; }; std::shared_ptr<Widget> pw1(new Widget, deleter1); std::shared_ptr<Widget> pw2(new Widget, deleter2); std::shared_ptr<Widget> pw3(pw1); pw3.reset(new Widget); //deleter恢复为默认的std::default_delete vector<std::shared_ptr<Widget>> vecs; vecs.emplace_back(pw1); vecs.emplace_back(pw2); //pw1和pw2虽然有不同的删除器,但类型相同,可以放入同一容器内。 //5. 其它 //5.1 shared_ptr的大小 cout << sizeof(spw1) << endl;//8 cout << sizeof(pw1) << endl; //8 //5.2 shared_ptr管理动态数组(建议用std::array、std::vector取代) std::shared_ptr<int> pArray1(new int[10], [](int* p) {delete[] p; }); //使用delete[] std::shared_ptr<int> pArray2(new int[10], std::default_delete<int[]>()); //使用default_delete<int[]>() //5.3 常见操作 cout << pw1.use_count() << endl; //2 if (pw1) //pw1.use_count >= 1 ? { cout << "pw1.use_count >= 1" << endl; } else { cout << "pw1.use_count == 0" << endl; } return 0; }
四. enable_shared_from_this模板的分析
(一)模板分析(以boost::enable_shared_from_this为例)
template<class T> class enable_shared_from_this { protected: enable_shared_from_this() BOOST_NOEXCEPT { } enable_shared_from_this(enable_shared_from_this const &) BOOST_NOEXCEPT { } enable_shared_from_this & operator=(enable_shared_from_this const &) BOOST_NOEXCEPT { return *this; } ~enable_shared_from_this() BOOST_NOEXCEPT // ~weak_ptr<T> newer throws, so this call also must not throw { } public: shared_ptr<T> shared_from_this() { shared_ptr<T> p( weak_this_ ); BOOST_ASSERT( p.get() == this ); return p; } shared_ptr<T const> shared_from_this() const { shared_ptr<T const> p( weak_this_ ); BOOST_ASSERT( p.get() == this ); return p; } public: // actually private, but avoids compiler template friendship issues // Note: invoked automatically by shared_ptr; do not call template<class X, class Y> void _internal_accept_owner( shared_ptr<X> const * ppx, Y * py ) const { if( weak_this_.expired() ) { weak_this_ = shared_ptr<T>( *ppx, py ); } } private: mutable weak_ptr<T> weak_this_; };
1. enable_shared_from_this模板类提供两个public属性的shared_from_this成员函数。这两个函数内部会通过weak_this_(weak_ptr类型)成员来创建shared_ptr。
2. _internal_accept_owner函数不能手动调用,这个函数会被shared_ptr自动调用,该函数是用来初始化唯一的成员变量weak_this_。
3. 根据对象生成顺序,先初始化基类enable_shared_from_this,再初始化派生类对象本身。这时对象己经生成,但weak_this_成员还未被初始化,最后应通过shared_ptr<T> sp(new T())等方式调用shared_ptr构造函数(内部会调用_internal_accept_owner)来初始化weak_this_成员。而如果在调用shared_from_this函数之前weak_this_成员未被初始化,则会通过ASSERT报错提示。
(二)使用说明
1. 基类必须为enable_shared_from_this<T>,其中T为派生类的类名。(这种方法叫奇妙递归模板模式)
2. 通过调用shared_from_this()成员函数获得一个和this指针指向相同对象的shared_ptr。
3. 从内部实现看,shared_from_this会查询当前对象的控制块,并创建一个指向该控制块的新shared_ptr。这样的设计就要求当前对象己有一个与其关联的控制块。为了实现这一点,就必须有一个己经存在指向当前对象的std::shared_ptr,如果不存在,则通常shared_from_this会抛出异常。
【编程实验】安全地从this指针创建shared_ptr
#include <iostream> #include <vector> #include <memory> using namespace std; //1. 从this指针创建shared_ptr //1.1 错误的做法 class Test1 { public: //析构函数 ~Test1() { cout <<"~Test1()" << endl; } //获取指向当前对象的指针 std::shared_ptr<Test1> getObject() { shared_ptr<Test1> pTest(this); //危险! 直接从this指针创建,会为this对象创建新的控制块! //从而可能导致this所指对象被多次析构 return pTest; } }; //1.2 正确的做法 class Test2 : public std::enable_shared_from_this<Test2> //继承! 注意Test2为基类的模板参数 (递归模板模式) { public: //析构函数 ~Test2() { cout << "~Test2()" << endl; } std::shared_ptr<Test2> getObject() { return shared_from_this(); //调用enable_shared_from_this模板的成员函数,获取this对象的shared_ptr } }; //2. shared_from_this函数的正确调用 //2.1 一般做法 class Test3 : public std::enable_shared_from_this<Test3> { public: //构造函数中不能使用shared_from_this Test3() { //std::shared_ptr<Test3> sp = shared_from_this(); //error,此时基类(enable_shared_from_this<Test3>) //虽己构造完,但shared_ptr的构造函数还没被调用,weak_this_指针 //未被初始化,因此调用shared_from_this会抛出异常 } //调用process之前,必须确保shared_ptr的构造函数己被执行(即weak_this_被初始化) void process() { std::shared_ptr<Test3> sp = shared_from_this(); } }; //2.2 改进做法:利用工厂函数来提供shared_ptr class Test4 : public std::enable_shared_from_this<Test4> { Test4() {} //构造函数设为private public: //提供工厂函数 template<typename... Ts> static std::shared_ptr<Test4> create(Ts&& ... params) { std::shared_ptr<Test4> ret(new Test4(params...)); return ret; } void process() { std::shared_ptr<Test4> sp = shared_from_this(); } }; //3. enable_shared_from_this的应用举例 class Widget; std::vector<std::shared_ptr<Widget>> processWidgets; //记录己被处理过的Widgets class Widget : public std::enable_shared_from_this<Widget> //需要从这里继承 { public: void process() { //错误做法:直接将this传给shared_ptr<Widget> //processWidgets.emplace_back(this); //将处理完的Widget加入链表。 //error,这种做法会本质上是用裸指针来创建shared_ptr,会为this对象创建 //新的控制块。如果外部new Widget时,也将指针交给shared_ptr管理时,会出现为同 //一个this对象创建多个控制块,从而造成this对象的多次析构! //正确做法:(为了确保shared_from_this在shared_ptr构造函数后被调用,可以采用工厂函数的方式来创建Widget, //具体见前面的例子) processWidgets.emplace_back(shared_from_this()); //将指向当前对象的shared_ptr加入到链表中 } ~Widget() { cout <<"~Widget()" << endl; } }; int main() { //1. 从this指针创建shared_ptr //1.1 错误做法:对象被多次析构 { //std::shared_ptr<Test1> pt1(new Test1()); //std::shared_ptr<Test1> pt2 = pt1->getObject(); } //1.2 正确做法 { std::shared_ptr<Test2> pt1(new Test2()); std::shared_ptr<Test2> pt2 = pt1->getObject(); } //2. shared_from_this的正确调用 { //2.1 错误方法: Test3 t; //t.process(); //错误,shared_ptr构造函数没有被执行 Test3* pt = new Test3(); //pt->process(); //错误,原因同上。 delete pt; //正确做法 std::shared_ptr<Test3> spt(new Test3); //shared_ptr构造被执行,weak_this_被正确初始化 spt->process(); //2.2 工厂方法提供shared_ptr,确保shared_ptr构造函数被执行! std::shared_ptr<Test4> spt2 = Test4::create(); spt2->process(); } //3. enable_shared_from_this的应用举例 { std::shared_ptr<Widget> sp(new Widget); sp->process(); } return 0; }