博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
[Note] C++动态内存:智能指针、动态数组
阅读量:6331 次
发布时间:2019-06-22

本文共 4874 字,大约阅读时间需要 16 分钟。

C++ Primer 动态内存

先简单介绍一下我们常用的几种内存:静态内存、栈内存、动态内存

1)从静态内存分配空间,内存在程序编译的时候就已经被分配好,比如全局变量和static变量
2)栈内存是在执行函数时,函数内部定义局部变量所分配的内存,在函数结束时,这些内存就被释放,效率比较高,但栈空间往往较小
3)动态内存,又称堆,由程序员自主通过malloc或new来分配,通过free和delete来进行释放。如果忘记释放,就会造成内存泄漏;如果在尚有指针引用内存的情况下释放内存,就会产生引用非法内存的指针

一、智能指针

为了更容易地使用动态内存,C++提供了两种智能指针类型来管理动态对象。智能指针的行为类似常规指针,但是它们可以自动释放所指向的对象。shared_ptr允许多个指针指向同一个对象,unique_ptr则独占所指向的对象。

1. shared_ptr

头文件:memory
智能指针是模板类。其创建方法如下:

shared_ptr
p1;shared_ptr
> p2;

默认初始化为一个空指针。

智能指针的使用方式和普通指针类似:

if(p1 && p1->empty()) //如果p1为空,则将“hi”赋予string        *p1 = "h1";

还有其他一些用法:

1)shared_ptr和unique_ptr都支持的用法

p.get();//返回p中保存的指针swap(p,q);//交换p和q中指针q.swap(p);

2)仅有shared_ptr支持的操作

make_shared
(args);//返回一个shared_ptr指向一个T对象,并用args初始化对象shared_ptr
p(q);//p是q的拷贝,此操作递增q中计数器p = q;//递减p中计数器,递增q中计数器p.unique();//若p.use_count()为1,返回true,否则falsep.use_count();//返回指针的引用计数,一旦计数为0,指针会释放自己管理的对象

使用动态内存的原因:

1)程序不知道自己需要使用多少对象
2)程序不知道所需对象的准确类型「
3)程序需要在多个对象之间共享数据

下面我们看一个处于第3个原因使用动态内存的例子:

template
class Test{public: Test():data(make_shared
>()){} Test(std::initiallizer_list
li):data(make_shared
>(li)){} void pop_back() {data->pop_back();} T& top() const {return data->back();} bool empty() const {return data->empty();} size_t size() const {return data->size();} void push_back(const T& d){data->push_back(d);}private: shared_ptr
> data;};

使用new和delete管理动态内存的一些实例

因为这个在之前已经学过,简单的列几个例子:

string *s = new string;string *s = new string("2333");auto p = new auto(obj);//括号中必须只有单一初始化器才能这么用auto p = new auto({a,b});//错误用法//正常情况下,new失败会抛出std::bad_alloc异常int * p = new (nothrow) int;//若分配失败,返回空指针delete p;delete []p;shared_ptr和new结合使用shared_ptr
p(new int(10));//正确用法shared_ptr
p = new int();//错误用法shared_ptr
f(){ return new int;//错误用法}shared_ptr
f(){ returb shared_ptr
(new int);//正确用法}

定义和改变shared_ptr的其他办法

1)shared_ptr<T> p(q);

p管理内置指针q所指向的对象,q必须指向new分配的内存,且能转换成T×类型
2)shared_ptr<T> p(u);
p从unique_ptr u哪里接管对象的所有权,并将u置为空
3)shared_ptr<T> p(q,d);
p接管内置指针q所指向对象的所有权,p将使用可调用对象d来代替delete
4)shared_ptr<T> p(p2,d);
p是shared_ptr p2的拷贝,并p调用可调用对象d来代替delete

5)

p.reset();
如果p是唯一指向对象的shared_ptr,reset会释放此对象
p.reset(q);
若传递了可选的参数内置指针q,会令p指向q,否则将p置空
p.reset(q,d);
若还传递了可选参数d,将会调用d而不是delete释放q

注意:虽然C++提供了内置指针和智能指针的转换,但是请尽量避免两种指针混合使用

2. unique_ptr

与shared_ptr不同的是,某个时刻只能有一个unique_ptr指向一个给定对象,当ptr被销毁时,它所指向的对象也被销毁
其基本操作在前面已经列出,与shared_ptr不同的是,定义一个unique ptr时,必须将它绑定到一个new返回的指针上

unique_ptr
p1;unique_ptr
p2(new int(43));

因为一个unique拥有一个对象,所以它不支持普通的拷贝或赋值操作

unique_ptr支持的其他操作

1)unique_ptr<T,D> u2;
定义一个空的u2,u2会使用一个类型为D的可调用对象来释放它的指针
2)unique_ptr<T,D> u(d);
定义一个空的u,指向类型为T的对象,用类型为D的对象d代替delete
3)u = nullptr;
释放u指向的对象,将u设为空
4)u.release();
u放弃对指针的所有权,返回指针,并将u置为空
5)
u.reset();
释放u指向的对象
u,reset(q);
如果提供了内置指针q,则令u指向这个对象,否则将u置空
u.reset(nullptr);

3.weak_ptr

weak_ptr是一种不控制所指向对象生存周期的智能指针,它指向一个由shared_ptr管理的对象,将一个weak_ptr绑定到shared_ptr不会改变shared_ptr的引用计数
用法:

weak_ptr
w;weak_ptr
w(sp);w = p;//p 为weak ptr或者shared ptrw.reset();//将w置为空w.use_count();w.expired();//若w.use_count()为0,则为true,否则falsew.lock();//返回shared_ptr,或nullptr

二、动态数组

new和delete只能一次分配释放一个对象,一些场合,我们需要一次为很多对象分配内存,为了支持这种需求,C+=和标准库提供乐两种方法:new和allocator

1. new和数组

一些基本语法:

int *a = new int[get_size()];int *a = new string[100]();int *a = new string[10]{"a","b","c",string(3,'x')};typedef int att[10];int *a = new att;delete []a;

智能指针和动态数组

可以用unique_ptr管理动态数组,语法如下:

unique_ptr
up(new int[10]);up[i] = 10;up.release();//自动调用delete[]释放指针

此时

指向数组的unique ptr不支持成员访问运算符即 点 和 箭头 运算符
其他的unique ptr操作保持不变

与unique_ptr不同,shared_ptr不支持直接管理动态数组,需要自己ingoing删除器

shared_ptr
sp(new int[10],[](int *p){delete []p;});//此处使用了lambda表达式shared_ptr未定义下标运算符,访问数组元素很麻烦*(sp.get()+i) = i; //i为我们要访问的下标

2.allocator类

new和delete有一些灵活性的局限,它们将多个对象的构造和内存分配绑定在一起,有时这会造成浪费

allocater类定义在头文件memory中,它帮助我们将对象构造和内存分配分离凯,它提供一种类型感知的内存分配方法,它分配的内存是原始的,未构造的

基本用法:

allocator
a;//定义allocator对象,它可以为类型为T的对象分配内存auto p = a.allocate(n);//分配一段原始的、未构造的内存,保存n个类型为T的对象a.deallocate(p,n);//释放从T×指针 p中地址开始的内存,n必须是a.allocate(n)中的n,在这之前, 用户需要对在这块内存中创建的对象调用destroya.construct(p,args);//在p指向的内存中构造一个对象a.destroy(p);//对p指向的对象执行析构函数

例如,为string分配内存、构造对象、释放对象:

allocator
alloc;auto const p = alloc.allocate(n);//分配n个未初始化的stringauto q = p;alloc.construct(q++);//*q为空字符串alloc.construct(q++,10,'c');//*q为ccccccccccalloc.construct(q++,"h1");//*q为hiwhile(q!=p) alloc.destroy(q--);//被销毁后,可以重新构造alloc.deallocate(p,n);

标准库还为allocator类定义了两个伴随算法,用来进行拷贝和填充操作:

uninitialized_copy(b,e,b2);//从迭代器b和e指出的范围中拷贝元素到迭代器b2开始的未构造内存中uninitialized_copy(b,n,b2);//将从迭代其b开始n个元素拷贝到迭代器b2开始的内存中uninitialized_fill(b,e,t);//在迭代器b和e指出的范围中创建t的拷贝对象(多个)uninitialized_fill(b,n,t);//从b开始的内存中创建n个t对象

转载地址:http://zjboa.baihongyu.com/

你可能感兴趣的文章
在workflow中,无法为实例 ID“...”传递接口类型“...”上的事件“...” 问题的解决方法。...
查看>>
获取SQL数据库中的数据库名、所有表名、所有字段名、列描述
查看>>
Orchard 视频资料
查看>>
简述:预处理、编译、汇编、链接
查看>>
调试网页PAIP HTML的调试与分析工具
查看>>
路径工程OpenCV依赖文件路径自动添加方法
查看>>
玩转SSRS第七篇---报表订阅
查看>>
WinCE API
查看>>
POJ 3280 Cheapest Palindrome(DP 回文变形)
查看>>
oracle修改内存使用和性能调节,SGA
查看>>
SQL语言基础
查看>>
对事件处理的错误使用
查看>>
最大熵模型(二)朗格朗日函数
查看>>
深入了解setInterval方法
查看>>
html img Src base64 图片显示
查看>>
[Spring学习笔记 7 ] Spring中的数据库支持 RowMapper,JdbcDaoSupport 和 事务处理Transaction...
查看>>
FFMPEG中关于ts流的时长估计的实现(转)
查看>>
Java第三次作业
查看>>
【HDOJ 3652】B-number
查看>>
android代码混淆笔记
查看>>