C++11中的智能指针
2016-11-05 16:51
323 查看
转载自:http://www.jellythink.com/archives/684
上一篇《从auto_ptr说起》中详细的总结了C++98标准中的
先来一段简单的代码,看看C++11中到底有哪些智能指针。
C++11中主要提供了
C++11中的
只有在使用者显示的调用
由于在
在最开始的那段代码中,也简单的使用了一下
– 智能指针-引用计数实现
– COM中的引用计数1
– COM中的引用计数2
我这里注重的总结
自己单独想想程序的输出。输出如下:
shared_ptr指向数组
在默认情况下,
上面的代码看不懂的,请参考这篇《C++中的Lambda表达式》文章。如果确实需要共享地托管一个对象,使用
线程安全
关于多线程中使用
1. 同一个
2. 同一个
3. 共享引用计数的不同的
对于第一点,没有什么说的;对于第二点,同一个
对于线程中传入的外部
环形引用
对于使用引用计数实现的智能指针,总是避免不了这个问题的。如果出现类似下面的代码,那就出现了环形引用的问题了。
要解决环形引用的问题,没有特别好的办法,一般都是在可能出现环形引用的地方使用
所以,我们在使用
这篇文章有点长,但是很详细的总结了C++11中的智能指针相关的问题,希望这篇文章对大家有用。
2014年10月18日 于深圳。
上一篇《从auto_ptr说起》中详细的总结了C++98标准中的
auto_ptr,但是随着C++11的到来,
auto_ptr已经不再了,即将成为历史;好的东西总是会受到大家的欢迎的,随着大家都在使用“准”标准库boost中的
shared_ptr;C++标准委员会终于觉的是时候将
shared_ptr加入到C++11中去了。欢呼声一片,至少我是这么觉的了;至少
shared_ptr让我用起来,还是不错的。接下来,就总结一下C++11中的这些智能指针吧。
C++11有哪些智能指针
先来一段简单的代码,看看C++11中到底有哪些智能指针。/************************************************************************* > File Name: SmartPointDemo.cpp > Author: Jelly > Mail: vipygd#126.com(#->@) > Created Time: 2014年10月16日 星期四 15时25分43秒 ************************************************************************/ #include <iostream> #include <memory> using namespace std; int main() { unique_ptr<int> up1(new int(10)); // 不能复制的unique_ptr // unique_ptr<int> up2 = up1; // 这样是错的 cout<<*up1<<endl; unique_ptr<int> up3 = move(up1); // 现在up3是数据唯一的unique_ptr智能指针 cout<<*up3<<endl; // cout<<*up1<<endl; // 运行时错误 up3.reset(); // 显示释放内存 up1.reset(); // 即使up1没有拥有任何内存,但是这样调用也没有问题 // cout<<*up3<<endl; // 已经释放掉up3了,这样会运行时错误 shared_ptr<int> sp1(new int(20)); shared_ptr<int> sp2 = sp1; // 这是完全可以的,增加引用计数 cout<<*sp1<<endl; cout<<*sp2<<endl; sp1.reset(); // 减少引用计数 cout<<*sp2<<endl; return 0; }
C++11中主要提供了
unique_ptr、
shared_ptr和
weak_ptr这三个智能指针来自动回收堆分配的对象。看看上面的代码,感觉用起来也还挺轻松的,也还不错,至少是比
auto_ptr好点。
unique_ptr
C++11中的unique_ptr是
auto_ptr的替代品,它与
auto_ptr一样拥有唯一拥有权的特性,与
auto_ptr不一样的是,
unique_ptr是没有复制构造函数的,这就防止了一些“悄悄地”丢失所有权的问题发生,如果需要将所有权进行转移,可以这样操作:
unique_ptr<int> up3 = move(up1); // 现在up3是数据唯一的unique_ptr智能指针 // 或者 unique_ptr<int> up4(move(up1));
只有在使用者显示的调用
std::move之后,才会发生所有权的转移,这样就让使用者知道自己在干什么。再来一段代码,看看将
unique_ptr作为函数参数和返回值的使用情况:
/************************************************************************* > File Name: unique_ptrDemo.cpp > Author: Jelly > Mail: vipygd#126.com(#->@) > Created Time: 2014年10月16日 星期四 17时10分49秒 ************************************************************************/ #include <iostream> #include <memory> using namespace std; unique_ptr<int> Func(unique_ptr<int> a) { cout<<*a<<endl; return a; } int main() { unique_ptr<int> up1(new int(10)); up1 = Func(move(up1)); cout<<*up1<<endl; return 0; }
由于在
unique_ptr中是没有复制构造函数的,所以在直接传参时,进行值传递时,建立临时变量时,就会出错了,所以需要显示的调用
move,转移所有权;而函数的返回值已经进行了
move操作,而不用显示的进行调用。
shared_ptr
在最开始的那段代码中,也简单的使用了一下shared_ptr。
shared_ptr名如其名,它允许多个该智能指针共享地“拥有”同一堆分配对象的内存;由于它的资源是可以共用的,所以也就可以透过
operator=等方法,来分享
shared_ptr所使用的资源。由于
shared_ptr内部实现上使用的是引用计数这种方法,所以一旦一个
shared_ptr指针放弃了“所有权”,其它的
shared_ptr对对象的引用并不会发生变化;只有在引用计数归零的时候,
shared_ptr才会真正的释放所占有的堆内存空间的。对于引用计数的问题,我这里就不再多总结了,可以参考以下文章:
– 智能指针-引用计数实现
– COM中的引用计数1
– COM中的引用计数2
我这里注重的总结
shared_ptr的使用,并不会对
shared_ptr进行源码级别的分析。再来一段简单的代码,看看
shared_ptr的一些应用。
#include <iostream> #include <memory> using namespace std; void Func1(shared_ptr<int> a) { cout<<"Enter Func1"<<endl; cout<<"Ref count: "<<a.use_count()<<endl; cout<<"Leave Func1"<<endl; } shared_ptr<int> Func2(shared_ptr<int> a) { cout<<"Enter Func2"<<endl; cout<<"Ref count: "<<a.use_count()<<endl; cout<<"Leave Func2"<<endl; return a; } int main() { shared_ptr<int> aObj1(new int(10)); cout<<"Ref count: "<<aObj1.use_count()<<endl; { shared_ptr<int> aObj2 = aObj1; cout<<"Ref count: "<<aObj2.use_count()<<endl; } Func1(aObj1); Func2(aObj1); shared_ptr<int> aObj3 = Func2(aObj1); cout<<"Ref count:"<<aObj3.use_count()<<endl; return 0; }
自己单独想想程序的输出。输出如下:
Ref count: 1 Ref count: 2 Enter Func1 Ref count: 2 Leave Func1 Enter Func2 Ref count: 2 Leave Func2 Enter Func2 Ref count: 2 Leave Func2 Ref count:2
shared_ptr指向数组
在默认情况下,
shared_ptr将调用
delete进行内存的释放;当分配内存时使用
new[]时,我们需要对应的调用
delete[]来释放内存;为了能正确的使用
shared_ptr指向一个数组,我们就需要定制一个删除函数,例如:
#include <iostream> #include <memory> using namespace std; class A { public: A() { cout<<"constructor"<<endl; } ~A() { cout<<"destructor"<<endl; } }; int main() { shared_ptr<A> arrayObj(new A[5], [](A *p){delete[] p;}); return 0; }
上面的代码看不懂的,请参考这篇《C++中的Lambda表达式》文章。如果确实需要共享地托管一个对象,使用
unique_ptr也许会更简单一些,比如:
#include <iostream> #include <memory> using namespace std; class A { public: A() { cout<<"constructor"<<endl; } ~A() { cout<<"destructor"<<endl; } }; int main() { unique_ptr<A[]> arrayObj(new A[5]); return 0; }
线程安全
关于多线程中使用
shared_ptr,有如下几点描述:
1. 同一个
shared_ptr被多个线程读,是线程安全的;
2. 同一个
shared_ptr被多个线程写,不是 线程安全的;
3. 共享引用计数的不同的
shared_ptr被多个线程写,是线程安全的。
对于第一点,没有什么说的;对于第二点,同一个
shared_ptr在不同的线程中进行写操作不是线程安全的,那基于第三点,我们一般会有以下方案来实现线程安全:
对于线程中传入的外部
shared_ptr对象,在线程内部进行一次新的构造,例如: sharedptr AObjTmp = outerSharedptrObj;
环形引用
对于使用引用计数实现的智能指针,总是避免不了这个问题的。如果出现类似下面的代码,那就出现了环形引用的问题了。
class Parent { public: shared_ptr<Child> child; }; class Child { public: shared_ptr<Parent> parent; }; shared_ptr<Parent> pA(new Parent); shared_ptr<Child> pB(new Child); pA->child = pB; pB->parent = pA;
要解决环形引用的问题,没有特别好的办法,一般都是在可能出现环形引用的地方使用
weak_ptr来代替
shared_ptr。说到了
weak_ptr,那下面就接着总结
weak_ptr吧。
weak_ptr
weak_ptr是最麻烦的,也比较拗口的;它可以指向
shared_ptr指针指向的对象内存,却并不拥有该内存。但是,使用
weak_ptr成员
lock,则可返回其指向内存的一个
shared_ptr对象,且在所指对象内存已经无效时,返回指针空值(nullptr)。由于
weak_ptr是指向
shared_ptr所指向的内存的,所以,
weak_ptr并不能独立存在。例如以下代码:
#include <iostream> #include <memory> using namespace std; void Check(weak_ptr<int> &wp) { shared_ptr<int> sp = wp.lock(); // 重新获得shared_ptr对象 if (sp != nullptr) { cout<<"The value is "<<*sp<<endl; } else { cout<<"Pointer is invalid."<<endl; } } int main() { shared_ptr<int> sp1(new int(10)); shared_ptr<int> sp2 = sp1; weak_ptr<int> wp = sp1; // 指向sp1所指向的内存 cout<<*sp1<<endl; cout<<*sp2<<endl; Check(wp); sp1.reset(); cout<<*sp2<<endl; Check(wp); sp2.reset(); Check(wp); return 0; }
所以,我们在使用
weak_ptr时也要当心,时刻需要判断对应的
shared_ptr是否还有效。对于上面的环形引用的问题,由于
weak_ptr并不会增加
shared_ptr的引用计数,所以我们就可以使用
weak_ptr来解决这个问题。
class Parent { public: weak_ptr<Child> child; }; class Child { public: weak_ptr<Parent> parent; }; shared_ptr<Parent> pA(new Parent); shared_ptr<Child> pB(new Child); pA->child = pB; pB->parent = pA;
总结
这篇文章有点长,但是很详细的总结了C++11中的智能指针相关的问题,希望这篇文章对大家有用。2014年10月18日 于深圳。
相关文章推荐
- 使用 C++11 智能指针时要避开的 10 大错误
- C++实现单例模式(包括采用C++11中的智能指针)
- C++11 理解 (二十三) 之 通用智能指针
- 【C++11新特性】 C++11智能指针之weak_ptr
- 到C++11中的智能指针
- C++11 Intro - C++11智能指针之unique_ptr
- C++11智能指针之std::unique_ptr
- 【C++11】新特性——共享资源的智能指针shared_ptr
- C++11智能指针之unique_ptr
- c++11 智能指针
- C++11新特性之智能指针
- 浅析c++11的智能指针
- c++11 智能指针
- C++11新特性之智能指针
- C++11特性:智能指针介绍
- C++11 FAQ中文版:共享资源的智能指针——shared_ptr
- C++11 智能指针之 std::shared_ptr 初级学习
- [置顶] c++(智能指针 c++11 智能指针)
- C++11智能指针之weak_ptr
- C++11 shared_ptr共享智能指针