C++之动态内存

news/2024/11/28 7:33:37/

12 动态内存

12.1动态内存与智能指针

动态分配对象的生存期与它们在哪里创建无关,只有当显示地被释放时,这些对象才会销毁。

new在动态内存中为对象分配空间并返回一个指向该对象的指针,可以对对象进行初始化。

delete接受以一个动态内存指针,销毁该对象,并释放与之关联的内存。

12.1.1 shared_ptr类

shared_ptr<string> p1; //默认初始化的智能指针保存空指针

使用时判断是否为空

if(p1 && p1->empty())*p1 = "hi";

make_shared函数

shared_ptr<int> p3 = make_shared<int>(42) //指向一个值为42的int的shared_ptr
auto p6 = make_shared<vector<string>>() //p6指向一个动态分配的空 vector<string>

shared_ptr的拷贝和赋值

每个shared_ptr都有一个关联计数器,称为引用计数:

计数器增加:

1、当拷贝一个shared_ptr时。

2、当用一个shared_ptr初始化另一个shared_ptr时

3、当shared_ptr作为参数传递给一个函数时,或作为函数返回时

计数器递减:

1、当给shared_ptr赋予一个新值

2、当shared_ptr被销毁(局部shared_ptr离开其作用域)

当shared_ptr计数器变为0,它会自动释放自己所管理的对象。

auto q = make_shared<int>();
auto r = make_shared<int>(42);
r = q;   //q指向对象计数器加1,r原来指向对象引用计数器减1,计数器为0,r原来指向对象自动释放

shared_ptr自动销毁所管理的对象,并释放所关联的内存

shared_ptr类通过析构函数销毁所管理的对象。

12.1.2 直接管理内存

使用new动态分配与初始化对象

int *pi = new int; //pi指向一个动态分配的、未初始化的无名对象

默认初始化,内置类型对象值未定义。类类型对象使用默认构造函数初始化

string *ps = new string; //初始化为空string
int *pi = new int; //pi指向一个未初始化的int

直接初始化:

int *pi = new int(1024);

构造方式(圆括号)

string *ps = new string(10,'9');

列表初始化()

vector<int> *pv = new vector<int>{0,1,2,3,4,5,6,7,8,9};

值初始化

string *ps1 = new string();

由初始化器推断要分配对象类型:

auto p1 = new auto(obj); //p指向一个与obj类型相同的对象,该对象用obj进行初始化

动态分配const对象

const int *pci = new const int(1024);

内存耗尽

int *p1 = new int; //如果分配失败,new抛出std::bad_alloc
int *p2 = new (nothrow) int ; //如果分配失败,new返回一个空指针

释放动态内存

delete p; //p必须指向一个动态分配的对象或一个空指针

delete表达式执行两个动作:销毁给定指针指向的对象,释放对应的内存。

指针值和delete

不能释放一块非new分配的内存,或将相同的指针值释放多次。

new和delete管理动态内存的三个问题:

1、忘记delete内存。

2、使用已经释放掉的对象。

3、同一块内存释放两次。

空悬指针

delete一个指针后,指针无效,但该指针仍然保存着,这就是空悬指针。

在delete之后将nullptr赋予指针。

12.1.3 shared_ptr和new结合使用

使用new返回的指针来初始化智能指针

shared_ptr<int> p2(new int(42)); //p2指向一个值为42的int

不要混合使用普通指针和智能指针

void process(shared_ptr<int> ptr)
{//使用ptr//ptr离开作用域,被销毁
}
//正确
shared_ptr<int> p(new int(42));
process(p); //拷贝p会递增它的引用计数,在process中引用计数器为2
int i = *p;
//错误
int *x(new int(1024));
process(x); //不能减int*转换为一个shared_ptr<int>
process(shared_ptr<int>(x));//合法,当内存会被释放
int j = *x;//未定义,x是一个空悬指针

不要使用get初始化另一个智能指针或为智能指针赋值

shared_ptr<int> p(new int(42));
int *q = p.get();
{shared_ptr<int>(q); //未定义,两个独立的shared_ptr指向相同的内存
} //程序结束,q被销毁,指向的内存被释放
int foo = *p //未定义:p指向内存已经释放了

其他shared_ptr操作

p = new int(1024); //错误,不能将一个新的指针赋予shared_ptr
p.reset(new int(1024));

12.1.4 智能指针和异常

当发生异常时,直接管理内存时不会自动释放的。智能指针指向的内存却可以释放。

void f()
{shared_ptr<int> sp(new int(42));//出现异常,未被捕获
}  //函数结束时shared_ptr自动释放
void f()
{int *IP= new int(42);//出现异常,未被捕获delete ip; //内存不会被释放
}

使用自己的释放操作

在shared_ptr中定义删除器。

12.1.5 unique_ptr

与shared_ptr不同,某时刻一个unique_ptr只能指向一个给定对象。当unique_ptr被销毁时,它所指向的对象也被销毁。

初始化unique_ptr必须采用直接初始化的形式:

unique_ptr<int> p2(new int(42));

不支持普通拷贝和赋值

unique_ptr<string> p1(new string("abc"));
unique_ptr<string> p2(p1);  //错误
unique_ptr<string> p3;
p3 = p2; //错误

通过release或reset将指针所有权从一个unique_ptr转移到另一个unique。

unique_ptr<string> p2(p1.release()) //release将p1置为空,p1放弃指针控制权,返回指针
unique_ptr<string> p3(new string("def"));
//将所有权从p3转移到p2
p2.reset(p3.release());//reset释放了p2原来指向的内存。p2.release(); //错误 p2不会释放内存,而且会丢失指针
auto p = p2.release(); //正确,但必须记得delete(p)

传递unique_ptr参数和返回unique_ptr

可以拷贝或赋值一个将要被销毁的unique_ptr。

unique_ptr<int> clone(int p){return unique_ptr<int>(new int(p));
}

weak_ptr

weak_ptr指向一个shared_ptr管理的对象,将weak_ptr绑定到一个shared_ptr不会改变shared_ptr的引用计数。一旦指向对象的shared_ptr被销毁,对象就会被释放。

12.2 动态数组

12.2.1 new和数组

初始化动态分配对象的数组

	auto p =  new string[5]{"a","b","c","d","e"};for(auto q = p;q < p+5; q++){cout << *q << " ";}cout << endl;delete [] p; //释放动态数组

智能指针和动态数组

unique<int[]> up(new int[10]); //unique<T[]> u(p) u指向内置指针p所指向的动态分配的数组。
up.release();

不能使用点和箭头成员运算符,只能使用下标运算符。

for(size_t i = 0; i !=10;++i)up[i] = i;

使用shared_ptr管理动态数组

必须定义删除器。

12.2.2 allocator类

allocator

allocator<string> alloc;  //可以分配string的allocator对象
auto const p = alloc.allocate(n);   //分配n个未初始化的string

allocator分配未构造的内存

auto q = p;
alloc.construct(q++,10,'c');

销毁string

while(q != p)alloc.destory(--q);//元素销毁后,可以重新使用这部分内存保存其他string

释放内存

alloc.deallocate(p,n);

http://www.ppmy.cn/news/41801.html

相关文章

18从零开始学Java之switch分支语句中该怎么用?

作者&#xff1a;孙玉昌&#xff0c;昵称【一一哥】&#xff0c;另外【壹壹哥】也是我哦 CSDN博客专家、万粉博主、阿里云专家博主、掘金优质作者 前言 在上一篇文章中&#xff0c;壹哥给大家介绍了Java里的顺序、分支、循环结构的概念&#xff0c;并且重点给大家讲解了分支结…

Python 纯函数与副作用,可变参数与不可变参数

文章目录纯函数与副作用纯函数副作用参数传递不可变对象&#xff08;值类型&#xff09;可变对象&#xff08;引用类型&#xff09;参数传递方式纯函数与副作用 在Python的函数式编程中&#xff0c;Python既可以在调用时对实参进行修改&#xff0c;也可以通过返回值返回函数调…

咖啡卷到现在,他们开始往里面掺北京豆汁了

咖啡卷到现在&#xff0c;他们开始往里面掺北京豆汁了0. 导读1. 人手一杯&#xff0c;果咖大势所趋2. 双倍成瘾&#xff1a;茶咖和酒咖被重点推荐3. 地方小吃融入咖啡&#xff0c;比如北京豆汁4. 炙手可热的云南咖啡豆5. 咖啡、户外和环保&#xff1a;绑定可持续6. 小结0. 导读…

可选择的Elasticsearch好用的可视化客户端工具

前言 常言道&#xff1a;工欲善其事&#xff0c;必先利其器。对于我们开发和测试同学来说&#xff0c;在日常的工作中有一款趁手的工具那真实如虎添翼啊&#xff0c;工作效率可是蹭蹭蹭的往上长&#xff0c;节省下来的时间摸摸鱼该有多好啊。最近我们系统开始使用elasticsearc…

VAE 理论推导及代码实现

VAE 理论推导及代码实现 熵、交叉熵、KL 散度的概念 熵&#xff08;Entropy) 假设 p (x&#xff09;是一个分布函数&#xff0c;满足在 x 上的积分为 1&#xff0c;那么 p(x)p(x)p(x)的熵定义为 H(p(x))H (p (x))H(p(x))&#xff0c;这里我们简写为 H(p)H(p)H(p) H(p)∫p(x)…

45-Dockerfile-ARG/ENV指令

AGR/ENV指令前言ARG作用格式说明生效范围使用示例ENV作用格式说明使用环境变量使用示例ARG 和 ENV 的区别前言 本篇来学习下Dockerfile中的AGR/ENV指令 ARG 作用 定义一个可以在构建镜像时使用的变量 格式 ARG <name>[<default value>]说明 在执行 docker b…

21-CSS

目录 1.CSS是什么&#xff1f; 2.CSS基本语法 3.CSS类型 3.1.行内样式&#xff08;适用范围最小&#xff09; 3.2.内部样式&#xff08;适用范围适中&#xff09; 3.3.外部样式&#xff08;适用范围最大&#xff09; PS&#xff1a;关于缓存 3.4.多种样式优先级 4.代码…

Java语言中的Calendar和Date两个类区别

Java语言中的Calendar和Date两个类都用于处理日期和时间&#xff0c;但它们在实现和使用方面有着很大的不同。实现方式 Date类是一个时间戳&#xff0c;它以自UTC&#xff08;格林尼治标准时间&#xff09;1970年1月1日午夜&#xff08;零时&#xff09;起所经历的毫秒数来表示…