标准库中的智能指针shared_ptr
智能指针的出现是为了能够更加方便的解决动态内存的管理问题。注:曾经记得有本书上说可以通过vector来实现动态分配的内存的自动管理,但是经过试验,在gcc4.8.5下是不行的。这个是容易理解的,vector是个模板,它不能辨别传入的数据类型是否是指针,从而也不能进行自动的释放内存操作。如果对非new出的对象进行delete操作,反而还会引起一些不必要的问题。
C++11标准库为了能够使程序员能够更安全的使用动态内存,提供了两种智能指针类型来管理动态对象。
shared_ptr类
智能指针也是模板,所以当我们创建一个智能指针时也需要提供额外的信息——指针可以指向的类型。例如:
shared_ptr<string> sp1; //sp1是这个智能指针的名字,尖括号里的string表示这个智能指针指向的是一个string类型的变量(记住,虽然这里没有我们熟悉的 * ,但sp1是一个指针),默认初始化的智能指针中保存者一个空指针。
智能指针的使用和普通指针一致,解引用一个指针返回它所指向的对象,如果在一个条件判断中使用智能指针效果就是检测它是否为空。
shared_ptr<string> sp1;
if(sp1)
{
std::cout << "判断智能指针sp1是否为空指针" << std::endl;
}
if(sp1!= nullptr )
{
if(sp1->empty())
{
*sp1 = "Hi Smart Pointer!"; //给该智能指针指向的字符串赋值
std::cout << "判断该智能指针指向的字符串是否为空!" << std::endl;
}
}
shared_ptr和unique_ptr都支持的操作:
shared_ptr<T> sp / unique_ptr<T> up | 声明一个智能指针,默认初始化时,该智能指针中保存着一个空指针 |
p | 将p作为一个条件判断,若p指向一个对象,则为true(和普通指针一样,就是判断指针本身是否为nullptr) |
*p | 解引用p,获得它指向的对象。和普通指针的*p语义一样。 |
p->mem | 等价于(*p).mem |
p.get() | 返回p中保存的指针,但是这个方法使用起来要小心,如果智能指针释放了其对象,返回的指针所指向的对象也就消失了 |
swap(p.q) | 交换p和q中的指针 |
p.swap(q) | 和swap(p.q)的作用是一样的 |
shared_ptr独有的操作
make_shared<T>(args) | args:这种表达在C++primer里就是参数列表的意思。make_shared<T>(args)返回一个shared_ptr指针,指向一个动态分配的类型为T的对象,使用args来初始化此对象(相当于是给构造函数传参) |
shared_ptr<T>p(q) | p是shared_ptr q的拷贝,此操作会递增q中的引用计数器。q中的指针必须要能转换成T* |
p = q | p和q都是shared_ptr,所保存的指针必须能够相互转换,此操作会递减p的引用计数,递增q的引用计数;若p的一用计数变为0,则将其管理的原内存释放 |
p.unique() | 若p.use_count()为1则返回true;否则返回false |
p.use_count() | 返回与p共享对象的智能指针的数量;可能很慢,主要用于调试。 |
shared_ptr和unique_ptr的区别:shared_ptr允许多个指针指向同一个对象,unique_ptr则“独占”所指向的对象。weak_ptr是一种伴随类,是一种弱引用,指向shared_ptr所管理的对象。这三种类型都定义在头文件memory中。
示例:
#include <iostream>
#include <string>
#include <memory> using namespace std;
int main(int argc,char *argv[])
{
shared_ptr<int> sp1;
if(nullptr == sp1)
{
std::cout << "shared_ptr默认初始化时,其内保存着一个空指针" << std::endl;
}
if(sp1.use_count() == )
{
std::cout << "默认初始化时,这个智能指针的引用计数值是:" << sp1.use_count() << std::endl;
} shared_ptr<int> sp2 = make_shared<int>(); //声明一个指向int类型的智能指针,并将其初始化为42,sp2这个智能指针的引用计数值应该是1
std::cout << "这个智能指针内保存的值是:" << *sp2 << ",它的引用计数器的值是:" << sp2.use_count() << std::endl;
sp1 = sp2; //无论何时,拷贝一个shared_ptr,它的引用计数器都会递增,例如将用一个shared_ptr初始化另外一个shared_ptr,或者将它作为参数传递给一个函数,以及作为函数的返回值
//当我们给shared_ptr赋予了一个新值或者是shared_ptr被销毁,它所关联的引用计数器就会递减,一旦一个shared_ptr的计数器变为0,它就会自动释放自己所管理的对象.
std::cout << "sp1的引用计数值是:" << sp1.use_count() << ",sp2的引用计数值是:" << sp2.use_count() << std::endl;
shared_ptr<int> sp3 = make_shared<int>();
sp2 = sp3; //给sp2赋予一个新的值,这里sp2里关联到原来指向的那个对象的引用计数器值会递减,但同时,它又被指向了新的对象,这个关联到新对象的引用计数值又会增加。
std::cout << "sp2关联的计数器值是:" << sp2.use_count() << ",sp3关联的引用计数器值是:" << sp3.use_count() << "sp1关联的应用计数器值是:" << sp1.use_count() << std::endl;
//当指向一个对象的最后一个shared_ptr被销毁时,shared_ptr类会自动销毁此对象,通过析构函数完成此工作。shared_ptr的析构函数会递减它所指向的对象的引用计数。如果引用计数变为0,则shared_ptr
//的析构函数就会销毁对象,并释放它占用的内存, {
shared_ptr<int> sp4 = make_shared<int>();
sp1 = sp4;
}
if(sp1!= nullptr)
{
std::cout << "上面的sp4虽然被销毁了,但是由于有sp1=sp4这个赋值操作,导致指向sp4原本指向的对象的指针对于0个,那么它申请的内存就不会随着sp4的析构而销毁。" << "sp1当前所保存的值是: " << *sp1 << std::endl;
}
//shared_ptr在无用之后仍然保留的一种情况是,你将shared_ptr存放在一个容器中,随后重排了容器,从而不再需要某些元素,在这种情况下,你应该确保erase删除那些不需要的shared_ptr元素。
return ();
}
分配动态内存的几个理由:
1.不知道自己想要多大的空间;
2.不知道对象的类型是什么,(void *)
3.需要多个对象共享数据
程序非自由空间被耗尽的情况还是有可能发生的,一旦一个程序用光了它所有可用的内存,new表达式就会失败。默认情况下,如果new不能分配所要求的的内存空间,它会抛出一个类型为bad_alloc的异常。可以改变使用new的方式来阻止它抛出异常。
int *p2 = new (nothrow) int; //此时如果分配内存失败,那么new就会返回一个空指针,这种形式的new称为定位new,定位new表达式允许我们向new传递额外参数。这个例子中传入的是一个由标准库定义的名为nothrow的对象,意思就是不抛出异常。这个都定义在头文件new中
new运算符包含两个动作:分配内存,构造对象。
delete运算符负责释放new运算符分配的内存,把它还给操作系统,delete也包含两个动作,销毁给定的指针指向的对象,释放对应的内存。
传给delete的指针必须是动态分配的内存或者是一个空指针,释放一块并非new分配的内存,或者将相同的指针释放多次,其行为是未定义的。
动态对象的生存期直到被释放时为止。(new/delete new出来的指针被称为内置指针)
动态内存的管理里容易出现的几个问题:
1.忘记delete内存。忘记释放动态内存常会导致“内存泄漏”问题,因为这种内存永远不可能归还给自由空间了。
2.使用已经释放掉的对象。通过在释放内存后将指针置为空,有时可以检测出这种错误。如果不置为空,那么会产生空悬指针(野指针)。这会造成灾难性的后果。
3.同一块内存释放两次,当有两个指针指向相同的动态分配对象时,可能发生这种错误。如果对其中一个指针进行了delete操作,对象的内存就被归还给自由空间了,如果随后又对第二个指针进行delete操作,自由空间就可能被破坏。
shared_ptr和new的结合使用
主要用于shared_ptr的初始化。使用示例如下:
shared_ptr<int> sp1 = new int(); //这是错误的
shared_ptr<int> sp2(new int()); //这才是正确的写法
默认情况下,一个用来初始化智能指针的普通指针必须要指向动态内存,因为智能指针默认使用delete来释放它所关联的对象。
shared_ptr<T> p(q) | p管理内置指针q所指向的对象,q必须指向new分配的内存,且能够转换为T* |
shared_ptr<T> p(u) | p从unqiue_ptr u那里接管了对象的所有权。将u置为空 |
shared_ptr<T> p(q,d) | p接管了内置指针q所指向的对象得到所有权。q必须能够转换为T*类型,p将使用可调用对象d来代替delete |
shared_ptr<T> p(p2,d) | p是shared_ptr p2的额拷贝,唯一的区别是,p将可调用对象d来代替delete |
p.reset() | 若p是唯一指向其对象的shared_ptr,reset会释放此对象。 |
p.reset(q) | 若传递了可选的参数内置指针q,会令p指向q,否则会将p置为空 |
p.reset(q,d) | 传递了内置指针q和可调用对象d,那么会使用d来替代delete |
不要使用get初始化另外一个智能指针或为智能指针赋值,get是用来给那些只能使用内置指针的地方来用的。
在使用reset之前,我们要检查自己是不是当前对象仅有的用户,如果不是,那么在使用之前要制作一份新的拷贝。函数退出的两种情况:正常退出和发生异常。无论哪种情况,局部对象都会被销毁。
智能指针使用规范:
- 不使用相同的内置指针值初始化或reset多个智能指针
- 不delete get返回的指针
- 不使用get()初始化或reset另一个智能指针
- 如果你使用了get()返回的指针,记住当最后一个对应的智能指针销毁后,你的指针就无效了
- 如果你使用智能指针管理的资源不是new分配内存,记住传递给它一个删除器。
标准库中的智能指针shared_ptr的更多相关文章
- 智能指针 shared_ptr 解析
近期正在进行<Effective C++>的第二遍阅读,书里面多个条款涉及到了shared_ptr智能指针,介绍的太分散,学习起来麻烦.写篇blog整理一下. LinJM @HQU s ...
- C++ 中的智能指针-基础
简介 在现代 C++ 编程中,标准库包含了智能指针(Smart pointers). 智能指针用来确保程序不会出现内存和资源的泄漏,并且是"异常安全"(exception-safe ...
- STL源码剖析-智能指针shared_ptr源码
目录一. 引言二. 代码实现 2.1 模拟实现shared_ptr2.2 测试用例三. 潜在问题分析 你可能还需要了解模拟实现C++标准库中的auto_ptr一. 引言与auto_ptr大同小异,sh ...
- Boost中的智能指针(转)
这篇文章主要介绍 boost中的智能指针的使用.(转自:http://www.cnblogs.com/sld666666/archive/2010/12/16/1908265.html) 内存管理是一 ...
- C++智能指针shared_ptr
shared_ptr 这里有一个你在标准库中找不到的—引用数智能指针.大部分人都应当有过使用智能指针的经历,并且已经有很多关于引用数的文章.最重要的一个细节是引用数是如何被执行的—插入,意思是说你将引 ...
- C++中的智能指针类模板
1,智能指针本质上是一个对象,这个对象可以像原生的指针一样使用,因为智能指 针相关的类通过重载的技术将指针相关的操作符都进行了重载,所以智能指针对象可以像原生指针一样操作,今天学习智能指针类模板,通过 ...
- RPCZ中的智能指针单例
RPCZ中的智能指针单例 (金庆的专栏) 智能指针单例应用于 RPCZ 库以实现库的自动初始化与自动清理. RPCZ: RPC implementation for Protocol Buffers ...
- 智能指针类模板(上)——STL中的智能指针
智能指针类模板智能指针本质上就是一个对象,它可以像原生指针那样来使用. 智能指针的意义-现代C++开发库中最重要的类模板之一-C++中自动内存管理的主要手段-能够在很大程度上避开内存相关的问题 1.内 ...
- STL笔记(6)标准库:标准库中的排序算法
STL笔记(6)标准库:标准库中的排序算法 标准库:标准库中的排序算法The Standard Librarian: Sorting in the Standard Library Matthew A ...
随机推荐
- bzoj 5498: [2019省队联测]皮配【dp】
是个神仙dp-- 参考:https://www.luogu.org/blog/xzz-233/solution-p5289 设f[i][j][k]是前i个有限制的城市,所有学校中选蓝色阵营有j人,有限 ...
- Python之单元测试——HTMLTestRunner
前置条件:把HTMLTestRunner.py文件拷贝到External Libraries—>site-packages里面 import unittestimport HTMLTestRun ...
- HDU - 6063 RXD and math
Bryce1010模板 http://acm.hdu.edu.cn/showproblem.php?pid=6063 打表发现规律是n^k #include <iostream> #inc ...
- Random Query CodeForces - 846F
题目 翻译: 给出一个n个数字的数列a[1],...,a[n],f(l,r)表示使a[l],a[l+1],...,a[r]组成的新序列中的重复元素只保留一个后,剩下元素的数量(如果l>r,则在计 ...
- OGG How to Resync Tables / Schemas on Different SCN s in a Single Replicat
To resync one or more tables/schemas on different SCN's using a single or minimum number of replicat ...
- 【转】grep 用法详解
有时会使用到,但老忘,转到博客以便学习收藏 转自http://blog.csdn.net/tenfyguo/article/details/6387786 首先要记住的是: 正则表达式与通配符不一样, ...
- XSS漏洞解析(二)
上篇我们讲了XSS的一些相关的内容,这篇我们就直接上代码demo解决实际问题吧. 主要的问题是xssfilter的编写,我们直接去网上找一下框架,一般有js,php,java等语言都有相关的XSS的相 ...
- CF989C A Mist of Florescence
思路: 有趣的构造题. 实现: #include <bits/stdc++.h> using namespace std; ][]; void fillin(int x, int y, c ...
- sql server 2012 从删库到跑路
问题: 向sql server 2012单个数据库中导入1500万+条数据的时候,报错: 错误 0xc0202009: 数据流任务 1: SSIS 错误代码 DTS_E_OLEDBERROR.出现 O ...
- 洛谷P2764 最小路径覆盖问题(二分图)
题意 给出一张有向无环图,求出用最少的路径覆盖整张图,要求路径在定点处不相交 输出方案 Sol 定理:路径覆盖 = 定点数 - 二分图最大匹配数 直接上匈牙利 输出方案的话就不断的从一个点跳匹配边 # ...