在C++中,动态内存是的管理是通过一对运算符来完成的:new ,在动态内存中为对象分配空间并返回一个指向该对象的指针,delete接受一个动态对象的指针,销毁该对象,并释放该对象关联的内存。
动态内存的使用很容器出现错误,确保在正确的时间释放动态内存是极其困难的。有时候我们会忘记释放动态内存,这样就会造成内存泄露;或者是一个对象还没有使用完,就释放了它所关联的内存,再去使用它,就会造成难以预估的错误。
因此,为了更好地管理使用动态内存,C++11新标准提供了两种智能指针:shared_ptr和unique_ptr来管理动态对象。它们负责自动释放所管理的内存,shared_ptr允许多个指针同时指向同一个动态对象,unique_ptr则独占所指向的资源。标准库还定义了一个weak_ptr,它是一种弱引用,指向shared_ptr所管理的对象,但不增加shared_ptr的引用计数。三个类型都定义在 <memory>头文件中
shared_ptr类
shared_ptr<string> p1; // 指向string类型的指针
shared_ptr<int> p2; // 指向int类型的指针
默认初始化的智能指针保存着一个空指针。
shared_ptr<T> sp | 空智能指针,可以指向类型为T的对象 |
unique_ptr<T> up | |
p | 将p作为一个判断条件,若p指向一个对象,则为true |
*p | 解引用p,获得它指向的对象 |
p->mem | 等价于(*p).mem |
p.get() | 返回p中保存的指针 |
swap(p, q) | 交换p和q中的指针 |
p.swap(q) |
下面是shared_ptr独有的操作:
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共享对象的智能指针数量,可能很慢,主要用于调试 |
- make_shared函数
// p3指向一个为1的int类型的shared_ptr
shared_ptr<int> p3 = make_shared<int>(1); // p4指向一个值为"9999999999"的string类型的shared_ptr
shared_ptr<string> p3 = make_shared<string>(10, '9'); // p5指向一个值初始化的int,即,值为0
shared_ptr<int> p5 = make_shared<int>();
make_shared用参数来构造给定类型的对象,例如,make_shared<string>的参数必须与string的某个构造函数的参数匹配,make_shared<int>的参数必须能初始化一个int。
如果我们不给make_shared传递任何参数,对象就会进行值初始化。
- shared_ptr的拷贝和赋值
auto p = make_shared<int>(1); // p指向的对象只有p一个引用者
auto q(p); // p和q指向相同对象,此对象有两个引用者
每个shared_ptr都有一个关联的计数器,叫做引用计数,无论何时拷贝一个shared_ptr,计数器都会递增,包括赋值,值传递的参数传递,作为返回值;当我们赋予shared_ptr
auto r= make_shared_ptr<int>(42); // r指向的int只有一个引用者
r = q; // 给r赋值,令它指向新对象
// 递增q指向的对象的引用计数
// 递减r指向的对象的引用计数
// r原来指向的对象已经没有引用者,会自动释放
- shared_ptr自动销毁所管理的对象......
- ......shared_ptr还会自动释放相关联的内存
shared_ptr<Foo> factory(T arg) {
// do some thing
// ...
return make_shared<Foo>(arg);
}
由于factory返回一个shared_ptr,所以我们可以确保它分配的对象会在恰当的时刻被释放。例如,我们可以将一个shared_ptr保存在局部变量中:
void use_factory(T arg) {
shared_ptr<Foo> p = factory(arg);
// 使用p
// p离开了作用域,它指向的内存被自动释放
}
p是use_factory的局部变量,在函数结束时它被销毁。在p被销毁时,会递减引用计数并检查是否为0。在此例中,p是唯一引用factory返回的内存的对象,由于p将要销毁,p指向的这个对象也被销毁,所占的内存也被释放。
void use_factory(T arg) {
shared_ptr<Foo> p = factory(arg);
// 使用p
return p; // 会拷贝p的一份实例,p的引用计数递增
}
这一次,use_factory在结束时,p被销毁,它指向的内存还有其他使用者,shared_ptr保证只要有任何shared_ptr对象引用它,它就不会被释放掉。
如果将一个shared_ptr存放于一个容器中,而后不再需要全部元素,而只使用其中一部分,要记得erase删除不再需要的那些元素
C++ Primer : 第十二章 : 动态内存之shared_ptr类的更多相关文章
-
C++ Primer : 第十二章 : 动态内存之shared_ptr类实例:StrBlob类
StrBlob是一个管理string的类,借助标准库容器vector,以及动态内存管理类shared_ptr,我们将vector保存在动态内存里,这样就能在多个对象之间共享内存. 定义StrBlob类 ...
-
C++ Primer : 第十二章 : 动态内存之shared_ptr与new的结合使用、智能指针异常
shared_ptr和new结合使用 一个shared_ptr默认初始化为一个空指针.我们也可以使用new返回的指针来初始化一个shared_ptr: shared_ptr<double> ...
-
C++ Primer : 第十二章 : 动态内存之allocator类
标准库allocator类定义在头文件 <memory>中.它帮助我们将内存分配和构造分离开来,它分配的内存是原始的.未构造的. 类似vector,allocator也是一个模板类,我们在 ...
-
C++ Primer : 第十二章 : 动态内存之动态内存管理(new和delete)
C++语言定义了两个运算符来分配和释放动态内存:运算符new分配内存,运算符delete释放new分配的内存. 运算符new和delete 使用new动态分配和初始化对象 在*空间分配的内存是无名的 ...
-
C++ Primer : 第十二章 : 动态内存之动态数组
动态数组的分配和释放 new和数组 C++语言和标准库提供了一次分配一个对象数组的方法,定义了另一种new表达式语法.我们需要在类型名后跟一对方括号,在其中指明要分配的对象的数目. int* arr ...
-
C++ Primer : 第十二章 : 动态内存之unique_ptr和weak_ptr
unique_ptr 一个unique_ptr拥有它所管理的对象,与shared_ptr不同,unique_ptr指向的对象只能有一个用户.当unique_ptr被销毁后,它所指向的对象也被销毁. 定 ...
-
C++Primer 第十二章
//1.标准库提供了两种智能指针类型来管理动态对象,均定义在头文件memory中,声明在std命名空间. // shared_ptr:允许多个指针指向同一个对象. // unique_ptr:独占所指 ...
-
C++ Primer 5th 第12章 动态内存
练习12.1:在此代码的结尾,b1 和 b2 各包含多少个元素? StrBlob b1; { StrBlob b2 = {"a", "an", "th ...
-
Linux内核设计与实现 总结笔记(第十二章)内存管理
内核里的内存分配不像其他地方分配内存那么容易,内核的内存分配不能简单便捷的使用,分配机制也不能太复杂. 一.页 内核把页作为内存管理的基本单位,尽管处理器最小寻址坑是是字或者字节.但是内存管理单元MM ...
随机推荐
-
sql server 有关锁的视图说明 syslockinfo
ransact-SQL 参考 https://msdn.microsoft.com/zh-cn/library/ms179881.aspx syslockinfo 包含有关所有已授权.正在转换和 ...
-
android selector(转)
Selector的结构描述: <?xml version="1.0" encoding="utf-8"?> <selector xmlns:a ...
-
HBase体系结构剖析
本文出自:http://wuyudong.com/archives/154 在上篇文章<HBase简介>中,已经提到过,HBase中的Table中的所有行都按照row key的字典序排列, ...
-
个人博客设计:创建Sql数据库操作类。
整体的博客框架如下 数据库操作java类如下 package com.yxq.dao; import java.sql.Connection; import java.sql.DriverManage ...
-
Python3常用网络编程模块介绍
一.socket模块 网络服务都是建立在socket基础之上的,socket是网络连接端点,是网络的基础:每个socket都被绑定到指定的IP和端口上: 1.首先使用socket(family=AF_ ...
-
Netty入门之HelloWorld
Netty系列入门之HelloWorld(一) 一. 简介 Netty is a NIO client server framework which enables quick and easy de ...
-
Spring:事务
摘要 本文摘抄了Spring事务相关的一些理论,主要讲述事务的特性.事务的传播行为.事务的隔离规则. 关键词:事务特性,事务传播,事务隔离 一.什么是事务 事务是用来保证数据的完整性和一致性,正如金钱 ...
-
PHP使用Zend Opcache之优化加速和缓存清理总结
简介 字节码缓存不是php的新特性,有很多独立性的扩展可以实现缓存,比如PHP Cache(APC),eAccelerator,ionCube和XCache等等.但是到目前为止,这些独立的扩展并没有集 ...
-
HTML5 Canvas 小例子 伸缩旋转方块
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...
-
java web 常用正则
什么是 RegExp? RegExp 是正则表达式(Regular expression)的缩写,作用是对字符串执行模式匹配. 通常用于格式验证.正则替换.查找子串等 各种编程语言的正则表达式基本相同 ...