c++11 智能指针学习汇总
c++为什么要引入智能指针?
C/C++ 语言最为人所诟病的特性之一就是存在内存泄露问题,因此后来的大多数语言都提供了内置内存分配与释放功能,有的甚至干脆对语言的使用者屏蔽了内存指针这一概念。这里不置贬褒,手动分配内存与手动释放内存有利也有弊,自动分配内存和自动释放内存亦如此,这是两种不同的设计哲学。有人认为,内存如此重要的东西怎么能放心交给用户去管理呢?而另外一些人则认为,内存如此重要的东西怎么能放心交给系统去管理呢?在 C/C++ 语言中,内存泄露的问题一直困扰着广大的开发者,因此各类库和工具的一直在努力尝试各种方法去检测和避免内存泄露,如 boost,智能指针技术应运而生
C#、Java、python和go等语言中都有垃圾自动回收机制,在对象失去引用的时候自动回收,而且基本上没有指针的概念,而C++语言不一样,C++充分信任程序员,让程序员自己去分配和管理堆内存,如果管理的不好,就会很容易的发生内存泄漏问题,而C++11增加了智能指针(Smart Pointer)。主要分为shared_ptr、unique_ptr和weak_ptr三种,使用时需要引用头文件。c++98中还有auto_ptr(自动指针),基本被淘汰了,不推荐使用。而c++11中shared_ptr和weak_ptr都是参考的boost库中实现的。
智能指针主要用于管理在堆上分配的内存,它将普通的指针封装为一个栈对象。当栈对象的生存周期结束后,会在析构函数中释放掉申请的内存,从而防止内存泄漏。简要的说,智能指针利用了 C++ 的 RAII 机制,在智能指针对象作用域结束后,会自动做内存释放的相关操作,不需要我们再手动去操作内存。
RAII是C++的发明者Bjarne Stroustrup提出的概念,RAII全称是“Resource Acquisition is Initialization”,直译过来是“资源获取即初始化”,也就是说在构造函数中申请分配资源,在析构函数中释放资源。因为C++的语言机制保证了,当一个对象创建的时候,自动调用构造函数,当对象超出作用域的时候会自动调用析构函数。所以,在RAII的指导下,我们应该使用类来管理资源,将资源和对象的生命周期绑定。
怎么说呢?c++在设计之初过分注重了性能和内存操作的灵活性,而低估了他长期给程序员带来的困扰,就没有设计垃圾回收机制。后来发现这并非趋势,其它语言都有,自己反而显得另类,所以在c++98标准中匆匆引入auto_ptr,使用效果并不好,安全性方面也存在缺陷,为了弥补这个遗憾,首先在boost库smart_ptr(智能指针)组件中引入了
scoped_ptr:只能在本作用域内使用的智能指针
scoped_array:scoped_ptr的数组版本
shared_ptr:最有价值的智能指针。共享指针(早期起名为counted_ptr)
shared_array:shared_ptr的数组版本
weak_ptr:弱指针,与shared_ptr相比,复制不会产生引用计数。lock()返回shared_ptr,并会产生引用计数。
intrusive_ptr:也维护一个引用计数器,比shared_ptr有更好的性能。但是要求T自己提供这个计数器
最后在c++11中参考boost中的智能指针模式,取其精华精简推出智能指针unique_ptr,shared_ptr和weak_ptr,而之前的auto_ptr基本被unique_ptr替代了,基本被抛弃了,虽然还保留着。智能指针的完善不仅为内存管理提供了便利,同时也在性能上做了优化的提升。
c++11智能指针
三种智能指针介绍
unique_ptr:独占指针 实现独占式拥有或严格拥有概念,保证同一时间内只有一个智能指针可以指向该对象。可以通过标准库的move()函数实现指针转移
shared_ptr:共享指针 实现共享式拥有概念。多个智能指针可以指向相同对象,该对象和其相关资源会在“最后一个引用被销毁”时候释放。资源可以被多个指针共享,它使用计数机制来表明资源被几个指针共享
weak_ptr:弱指针 是一种不控制对象生命周期的智能指针, weak_ptr 设计的目的是为配合 shared_ptr 而引入的一种智能指针来协助 shared_ptr 工作, 它只可以从一个 shared_ptr 或另一个 weak_ptr 对象构造, 它的构造和析构不会引起引用记数的增加或减少

三种智能指针原理
多个指针将指向同一个对象。会造成程序试图删除同一个对象多次,造成内存访问异常崩溃的现象。要避免这种问题,方法有多种:1.定义陚值运算符,使之执行深复制。这样两个指针将指向不同的对象,其中的一个对象是另一个对象的副本,缺点是浪费空间,所以智能指针都未采用此方案。2.建立所有权(ownership)概念。对于特定的对象,只能有一个智能指针可拥有,这样只有拥有对象的智能指针的构造函数会删除该对象。然后让赋值操作转让所有权。这就是用于auto_ptr和uniqiie_ptr 的策略,但unique_ptr的策略更严格。3.创建智能更高的指针,跟踪引用特定对象的智能指针数。这称为引用计数。例如,赋值时,计数将加1,而指针过期时,计数将减1,。当减为0时才调用delete。这是shared_ptr采用的策略。
shared_ptr:允许多个该智能指针都指向同一块堆分配对象的内存。它是通过引用计数去解决可能出现的删除一个对象多次的问题的。通过引用计数跟踪和记录有多少个shared_ptr共同指向一个对象。例如,赋值时,计数将加1,而指针过期时,计数将减1。一旦最后一个这样的指针被销毁,也就是一旦某个对象的引用计数变为0,这个对象会被自动删除。因为多了个计数对象,所以内存开销变大了,安全好用开销大。
unique_ptr:都是基于所有权的概念去解决可能出现的删除一个对象两次的问题的。他还摒弃了赋值和拷贝构造函数,要求必须显式采用移动语义move转移所有权的方式来避免出现悬空指针,安全性得到了提高,而且unique_ptr的开销跟裸指针一样的,安全好用性能高。
weak_ptr:关联时不会造成对象计数变化,完全是为了弥补shared_ptr天生有缺陷的问题,其实相比于上一代的智能指针auto_ptr来说,shared_ptr可以说近乎完美,但是通过引用计数实现的它,虽然解决了指针独占的问题,但也引来了引用成环的问题,这种问题靠它自己是没办法解决的,所以在C++11的时候将shared_ptr和weak_ptr一起引入了标准库,用来解决循环引用的问题,这个指针是个陪跑的角色,通常在特殊场景下使用。
三种智能指针使用的场景
unique_ptr 性能高,没有特殊要求的话可以直接用来取代raw pointer(原始指针),他还支持下标取值哦。
shared_ptr 开销大,在前者不能满足的场景例如需要多个智能指针同时拥有同一个控件的所有权的时候使用。shared_ptr的引用计数本身是安全且无锁的,但shared_ptr中封装的基础对象的读写则不是。
weak_ptr 不单独使用,通常用来配合shared_ptr使用,避免循环引用的问题。
三种智能指针使用实列
unique_ptr
std::unique_ptr 有几个常用函数如下:
void swap (unique_ptr& x)
将 shared_ptr 对象的内容与对象 x 进行交换,在它们两者之间转移管理指针的所有权而不破坏或改变二者的引用计数。
void reset()
void reset (ponit p)
没有参数时,先将管理的计数器引用计数减一并将管理的指针和计数器置清零。有参数 p 时,先做面前没有参数的操作,再管理 p 的所有权和设置计数器。
element_type* get()
得到其管理的指针。
long int use_count()
返回与当前智能指针对象在同一指针上共享所有权的 shared_ptr 对象的数量,如果这是一个空的 shared_ptr,则该函数返回 0。如果要用来检查 use_count 是否为 1,可以改用成员函数 unique 会更快。
bool unique()
返回当前 shared_ptr 对象是否不和其他智能指针对象共享指针的所有权,如果这是一个空的 shared_ptr,则该函数返回 false。
element_type& operator\*()
重载指针的 * 运算符,返回管理的指针指向的地址的引用。
element_type* operator->()
重载指针的 -> 运算符,返回管理的指针,可以访问其成员。
explicit operator bool()
返回存储的指针是否已经是空指针,返回的结果与 get() != 0 相同。
支持下标取值和数组管理
int main()
{
//初始化方式1
std::unique_ptr<int> up1(new int(123));
//初始化方式2
std::unique_ptr<int> up2;
up2.reset(new int(123)); std::unique_ptr<int> up3;
up3 = std::move(up2);
//对于数组的管理,默认带删除器
std::unique_ptr<int[]> up4(new int[10]);
//如果希望自定义删除器,需要自定义删除器类型
std::unique_ptr<int[],void(*)[int*]> up5(new int[10],[](int* p){delete []p;});
}
shared_ptr
std::shared_ptr 有几个常用函数如下:
void swap (unique_ptr& x)
将 shared_ptr 对象的内容与对象 x 进行交换,在它们两者之间转移管理指针的所有权而不破坏或改变二者的引用计数。
void reset()
void reset (ponit p)
没有参数时,先将管理的计数器引用计数减一并将管理的指针和计数器置清零。有参数 p 时,先做面前没有参数的操作,再管理 p 的所有权和设置计数器。
element_type* get()
得到其管理的指针。
long int use_count()
返回与当前智能指针对象在同一指针上共享所有权的 shared_ptr 对象的数量,如果这是一个空的 shared_ptr,则该函数返回 0。如果要用来检查 use_count 是否为 1,可以改用成员函数 unique 会更快。
bool unique()
返回当前 shared_ptr 对象是否不和其他智能指针对象共享指针的所有权,如果这是一个空的 shared_ptr,则该函数返回 false。
element_type& operator\*()
重载指针的 * 运算符,返回管理的指针指向的地址的引用。
element_type* operator->()
重载指针的 -> 运算符,返回管理的指针,可以访问其成员。
explicit operator bool()
返回存储的指针是否已经是空指针,返回的结果与 get() != 0 相同。
#include<iostream>
#include<memory>
void func(shared_ptr<int>& p)
{ }
void func1(int* p)
{ }
int main()
{
//初始化方式1
std::shared_ptr<int> sp1(new int(123)); //初始化方式2
std::shared_ptr<int> sp2;
sp2.reset(new int(123));
//或者
//sp2 = sp1; //初始化方式3
std::shared_ptr<int> sp3;
sp3 = std::make_shared<int>(123); //调用func()函数
func(sp3);
func1(sp3.get()); return 0;
}
weak_ptr
std::weak_ptr 有几个常用函数如下:
void swap (weak_ptr& x)
将当前 weak_ptr 对象的内容与 x 的内容交换。
void reset()
将当前 weak_ptr 对象管理的指针和计数器变成空的,就像默认构造的一样。
long int use_count()
返回与当前 weak_ptr 对象在同一指针上共享所有权的 shared_ptr 对象的数量。
bool expired()
检查是否过期,返回 weak_ptr 对象管理的指针为空,或者和他所属共享的没有更多 shared_ptr。lock 函数一般需要先调用 expired 判断,如果已经过期,就不能通过 weak_ptr 恢复拥有的 shared_ptr。此函数应返回与(use_count() == 0)相同的值,但是它可能以更有效的方式执行此操作。
shared_ptr<element_type> lock()
#include <iostream>
#include <memory> using namespace std; class B;
class A
{
public:
shared_ptr<class B> m_spB;
}; class B
{
public:
shared_ptr<class A> m_spA;
}; //weak reference
class WeakB;
class WeakA
{
public:
weak_ptr<class WeakB> m_wpB;
}; class WeakB
{
public:
weak_ptr<class WeakA> m_wpA;
}; void test_loop_ref()
{
weak_ptr<class A> wp1; {
auto pA = make_shared<class A>();
auto pB = make_shared<class B>(); pA->m_spB = pB;
pB->m_spA = pA; cout << "pA count: " << pA.use_count() << ", " << "pB count: " << pB.use_count() << endl; wp1 = pA;
}//内存泄漏 cout << "wp1 count: " << wp1.use_count() << "\n"; weak_ptr<class WeakA> wp2;
{
auto pA = make_shared<class WeakA>();
auto pB = make_shared<class WeakB>(); pA->m_wpB = pB;
pB->m_wpA = pA; cout << "pA count: " << pA.use_count() << ", " << "pB count: " << pB.use_count() << endl; wp2 = pA;
}//无内存泄漏 cout << "wp2 count: " << wp2.use_count() << "\n";
} int main()
{
//std::weak_ptr 用来避免 std::shared_ptr 的循环引用
test_loop_ref(); return 0;
}
auto_ptr和unique_ptr
auto_ptr可以由shared_ptr或unique_ptr替换,具体取决于具体情况
1. auto_ptr支持拷贝构造与赋值操作,但unique_ptr不直接支持,auto_ptr通过拷贝构造或者operator=赋值后,对象所有权转移到新的auto_ptr中去了,原来的auto_ptr对象就不再有效,这点不符合人的直觉。unique_ptr则直接禁止了拷贝构造与赋值操作
无论是auto_ptr还是unique_ptr,都是基于所有权的概念去解决可能出现的删除一个对象两次的问题的。这种情况下会有什么问题呢?先看auto_ptr,看一个小例子:
auto_ptr<string> films[2] =
{
auto_ptr<string> (new string("Fowl Balls")),
auto_ptr<string> (new string("Duck Walks"))
};
auto_ptr<string> pwin;
pwin = films[0];//注:films[0]对内存失去了所有权 cout << films[0] << endl;//注:会出现错误,因为film[0]指向的内存此时已经不确定了
如上面代码中注释的一样,auto_ptr在智能指针赋值时,原指针会失去对内存的所有权。程序运行时,再次访问访问该内存的话会出错。(当然了,如果你对原指针film1[0]重新赋新值的话是不会有问题的)。
unique_ptr相比auto_ptr更好的点:首先,unique_ptr是在编译的时候,编译器就会发现原指针失去所有权的错误,也就是在编译阶段就报错,而不是像auto_ptr运行的时候出现一些未知的问题。其次,另一个优点是,除了可以使用new和delete之外,unique_ptr还有一个可用于数组的变体,即unique_ptr有使用new[]和delete[]的版本,而auto_ptr和shared_ptr只能使用new和delete。
2. auto_ptr不可做为容器元素,会导致编译错误。虽然unique_ptr同样不能直接做为容器元素,但可以通过move语意实现
3. 标准库提供了一个可以管理动态数组的unique_ptr版本
int main() {
unique_ptr<int[]> p(new int[5] {1, 2, 3, 4, 5});
p[0] = 0; // 重载了operator[]
}
参考资料:
https://www.cnblogs.com/xumaomao/p/15175448.html
https://blog.csdn.net/phdongou/article/details/117600943
c++11 智能指针学习汇总的更多相关文章
- 【C++11新特性】 C++11智能指针之weak_ptr
如题,我们今天要讲的是C++11引入的三种智能指针中的最后一个:weak_ptr.在学习weak_ptr之前最好对shared_ptr有所了解.如果你还不知道shared_ptr是何物,可以看看我的另 ...
- Qt 智能指针学习(7种指针)
Qt 智能指针学习 转载自:http://blog.csdn.net/dbzhang800/article/details/6403285 从内存泄露开始? 很简单的入门程序,应该比较熟悉吧 ^_^ ...
- c++11 智能指针 unique_ptr、shared_ptr与weak_ptr
c++11 智能指针 unique_ptr.shared_ptr与weak_ptr C++11中有unique_ptr.shared_ptr与weak_ptr等智能指针(smart pointer), ...
- C++11——智能指针
1. 介绍 一般一个程序在内存中可以大体划分为三部分——静态内存(局部的static对象.类static数据成员以及所有定义在函数或者类之外的变量).栈内存(保存和定义在函数或者类内部的变量)和动态内 ...
- C++11智能指针之std::unique_ptr
C++11智能指针之std::unique_ptr uniqut_ptr是一种对资源具有排他性拥有权的智能指针,即一个对象资源只能同时被一个unique_ptr指向. 一.初始化方式 通过new云 ...
- 详解C++11智能指针
前言 C++里面的四个智能指针: auto_ptr, unique_ptr,shared_ptr, weak_ptr 其中后三个是C++11支持,并且第一个已经被C++11弃用. C++11智能指针介 ...
- C++11 智能指针
C++ 11标准库引入了几种智能指针 unique_ptr shared_ptr weak_ptr C++内存管理机制是当一个变量或对象从作用域过期的时候就会从内存中将他干掉.但是如果变量只是一个指针 ...
- C++11智能指针
今晚跟同学谈了一下智能指针,突然想要看一下C++11的智能指针的实现,因此下了这篇博文. 以下代码出自于VS2012 <memory> template<class _Ty> ...
- C++11智能指针的深度理解
平时习惯使用cocos2d-x的Ref内存模式,回过头来在控制台项目中觉得c++的智能指针有点生疏,于是便重温一下.首先有请c++智能指针们登场: std::auto_ptr.std::unique_ ...
- C++11智能指针 share_ptr,unique_ptr,weak_ptr用法
0x01 智能指针简介 所谓智能指针(smart pointer)就是智能/自动化的管理指针所指向的动态资源的释放.它是存储指向动态分配(堆)对象指针的类,用于生存期控制,能够确保自动正确的销毁动 ...
随机推荐
- css可继承与不可继承的属性
一.可继承性的属性 字体相关的:font-size/font-family/font-weight/font-style/font-variant/font-stretch 文本相关的:color/t ...
- P5736 质数筛
原题连接 一看到这个熟悉的输入,我们就立马反应过来要请出一维数组来记录一下输入的数据.现在数据的存储解决了,紧接着来剖析一下步骤: 输入数据 一个一个的判断是否为质数 筛去合数 输出质数 理清了思路后 ...
- python的惊艳之举--源于一个同事分享16种字符串反转方式
日期:2018/12/19 最近刚学习了Python的入门课程,准备寻找你的进阶门路,偶尔得到一个老同事微信分享,标题让我这种greenhand心砰砰砰... 好了,看标题: 面试官让用 5 种 py ...
- The Semantics of Constructors——2.4 成员初始化列表
2.4 成员初始化列表(Member Initialization List) 当你写下一个constructor时,就有机会设定class members的初值.要不是经由member initia ...
- .Net的产品变迁
从开始接触.Net以来,转眼过了好几年.在这许多年里,不得不感慨.Net产品线也在不断发生着变化, 下面就简单的进行一下梳理. .Net能做桌面应用程序,也就是C/S,也能做网站开发,就是通常说的B/ ...
- 阿里云ECS部署单机kafka 并对外提供服务(带认证)
1. zk配置(单机) zoo.cfg tickTime=2000initLimit=10syncLimit=5dataLogDir=/data/zookeeper/log/dataDir=/data ...
- 富文本 HTML String 转化为 nodes 数组
https://github.com/ant-mini-program/mini-html-parser?spm=ding_open_doc.document.0.0.4ef9722fXz27PV
- C# string的2个特殊方法
1. Strings.Join 方法:用指定的连接符连接一个数组中包含的若干子字符串创建的字符串.e.g List<string> list = new List<string> ...
- ORACLE之自治事务
在创建存储过程时,可以用关键字: PRAGMA AUTONOMOUS_TRANSACTION 创建自治事务. 常用场景: 触发器抛错导致事务回滚,日志的记录需要用自治事务进行记录 代码抛错,导致数据库 ...
- HDLbits—— 3-input look-up-table
// a 3-input look-up-table // In this question, you will design a circuit for an 8x1 memory, // wher ...