C++Primer学习——动态内存
静态内存:用来保存static 栈内存:保存非static
智能指针:
shared_ptr:允许多个指针指向一个对象
unique_ptr:独占所指对象
weak_ptr:一种弱引用,指向shared_ptr的对象
shared_ptr:
shared_ptr<T> p;
p.get(); //返回p中保存的指针
p.unique(); //指针数量为1返回
p.use_count(); //返回共享指针数量
shared_ptr<string > p = make_shared_ptr<string>(10,'9');
shared_ptr<int> p = make_share_ptr<int>();
auto p = make_share_ptr<int>();
拷贝会使shared_ptr的计数器增加.初始化,函数参数或者函数返回值。
一旦计数器为0时就会通过析构函数释放自己
函数结束会返回一个p的拷贝,增加计数器,所以函数结束含有指针指向p的内存,所以不会被销毁
void use_factory(T arg)
{
shared_ptr<Foo> p = factory(arg);
return p;
}
所以如果你将shared_ptr放在一个容器中,而后不再需要全部的元素,记得把不用元素删除。
使用动态生存周期的资源:
1.程序不知道自己有多少的对象 //容器类
2.程序不知道自己做需要对象的准确类型
3.程序需要在多个对象间共享数据 //通过shared_ptr的计数器特性实现
直接管理内存:
通过new和delete来管理内存,与智能指针不同的是,直接管理内存无法依赖类对象拷贝、赋值和销毁操作的任何默认定义。
默认初始化和值初始化:
对于定义了构造函数的类类型来说两者都会调用构造函数初始化,没意义;但对于内置类型,值初始化有良好定义的值,而默认初始化
的值是未定义的。
int *p = new int; //默认初始化,值未定义
int *p = new int(); //值初始化为0,*p为0
所以在定义const对象时,如果有构造函数的类类型其const对象可以隐式初始化,但是其他类型就必须显示初始化了。
const int *pci = new const int(1024);
const string *s = new const string;
如果提供一个初始化器,就能使用auto判断我们想要的类型,但是只有当单一的初始化器时才能使用
auto p = new auto(obj);
auto p = new auto(a,b,c); //error
释放一块并非new分配的内存或者多次释放相同指针是未定义的行为。
动态对象的生命周期到被释放为止:
当离开作用域时,局部的指针被销毁了,但是所指内存并没有释放
void use_factory(T arg)
{
Foo *p = factory(arg);
//使用p但不delete
//即便离开作用于,所指内存没被释放
}
常见问题:忘记delete、使用已经释放了的内存,同一块内存释放了两次
shared_ptr和new的结合使用:
接受指针参数的智能指针的构造函数是explicit,new会返回一个指向该对象的指针
shared_ptr<int>p1 = new int(1024); //error 需要隐式转换成shared_ptr
shared_ptr<int>p2(new int(1024));
shared_ptr<int> clone(int p)
{
return new int(p); //error 需要隐式转换成shared_ptr
return shared_ptr<int>(new int(p));
}
默认情况下初始化智能指针的普通指针必需指向动态内存,因为智能指针默认使用delete释放。
否则需要自定义操作来替代delete
shared_ptr<T> p(q); //p管理q所指向的对象
shared_ptr<T> p(u); //p接管unique_ptr并将u置为空
shared_ptr<T> p(q,d); //p接管q,用d代替delete
shared_ptr<T> p(p2,d); //p2的一个拷贝
p.reset();
p.reset(q);
p.reset(q,d);
不要混用普通指针和智能指针:
当智能指针和普通指针指向了同一个内存,如果智能内存的计数减为0,那么内存会被释放
所以当同时用的时候,将内存管理交给shared_ptr
int *x = &q;
process(shared_ptr<int>(x) ); //只能指针会被销毁,内存释放
int j = *x; //空悬指针
可以通过get()向不能使用智能指针的传递一个内置指针,但get返回的指针不能delete
get最好用来传递访问权限,不能用get初始化另外一个智能指针或者为另一个智能指针赋值
无法将普通指针赋值给shared_ptr,所以需要reset
p = new int(42); //error 不能隐式转换?
p.reset(new int(42)); //correct
对于异常,智能指针无论怎样都能确保内存释放,但是直接管理内存时,如果在delete之前异常弹出,那么直接管理的内存
并不会释放。
如果类没有析构函数而且没有显示的关闭连接,那么会出现资源泄露。
可以考虑将其与一个shared_ptr绑定,然后指定delete该指针的方法,从而保证正常关闭
void end_connection(connection *p) {disconnect(p);}
void f(destination &d)
{
connection c = connect(&d);
shared_ptr<connection> p(&c,end_connection);
}
智能指针error:
1.不使用相同的内置指针(或者reset)初始化多个智能指针。 //计数器全是1?
shared_ptrp1(p);
shared_ptrp2(p);
cout << p1.use_count() << " " <<p2.use_count() <<endl;
//1 1
2.不delete get()返回的指针
3.不适用get初始化或者reset另一个智能指针
4.注意get()返回的指针会在智能指针销毁后失效
5.如果你管理的不是new分配的内存,指定一个删除器
unique_ptr:
没有类似make_shared的函数,只能绑定到一个new返回的指针上面。同样只能直接初始化
unique_ptr<string> p1(new string("123"));
unique_ptr<string> p2(p1.release());
//转移控制权,release放弃控制并返回指针
unique_ptr<string> p3(new string("abc"));
p2.reset(p3.release());
而且可以拷贝一个将要被销毁的unique_ptr,因为编译器知道它将被销毁所以执行一种特殊的拷贝
unique_ptr<int> clone(int p)
{
return unique_ptr<int>(new int(p));
}
unique_ptr可以自定义删除器:
connection c = connect(&d);
unique_ptr<connection , decltype(end_connection)*>
p(&c,end_connection);
weak_ptr:
是一种不控制所指对象生存期的只能指针,指向一个shared_ptr对象,但不会改变引用计数。
w.use_count(); //返回shared_ptr数量
w.expired(); //use_count为0返回true
w.lock(); //如果expired()为true返回一个空指针,否则返回一个shared_ptr
动态数组:
vector和string都是连续内存中保存元素,所以每次要分配大量内存。
可以进行默认初始化或者值初始化,由于不能在()中给出初始化器,意味着不能auto分配数组
int *p = new int[10]; //10个未初始化int
int *p = new int[10](); //10初始化为0的int
int *p = new int[10]{0,1,2,3,4}; //前几个用给定初始化器初始化
delete []p;
delete时[]告诉编译器指针指向对象数组的第一个元素,如果不加[]是未定义的行为。而且释放内存时逆序销毁
1.与unique_ptr:
unique_ptr<int[]> p(new int[10]);
p.release(); //自动调用delete
2.与shared_ptr:
和unique不同的是,shared不支持自动管理动态数组QAQ,所以需要定义删除器
如果没有删除器,那么产生问题和delete没加[]一样(即默认调用delete p)
shared_ptr<int> p(new int[10],[](int *p){delete[] p;});
p.reset(); //通过lambda表达式释放
shared_ptr没有下标运算符,而且只能指针不支持指针算术运算,所以访问数组中的元素。
必需要get()获取内置指针
*(p.get() + i) = i;
allocator
在用new分配的时候,不知道数量所以可能把数组分配过大。 而且每个使用的元素都要赋值两次,默认初始化 + 赋值。
如果没有默认构造函数,那么不能动态分配数组。
allocator<string> alloc;
auto p = alloc.allocate(5); //分配5个未构造原始内存
auto q = p;
alloc.construct(q++,"Orz");
cout << *p <<endl;
alloc.destroy(--q);
alloc.deallocate(p,5);
destory() 释放对象内的动态内存(如果有) deallocate是释放对象本身占有的内存
所以要先destory再进行deallocate
unintialized_copy(b,e,b2):从迭代器b和e指出的输入范围中拷贝元素到迭代器b2指定的未构造的原始内存中。b2指向的内存必须足够大,能容纳输入序列中元素的拷贝
uninitialized_copy_n(b,n,b2):从迭代器b指向的元素开始,拷贝n个元素到b2开始的内存
unintialized_fill(b,e,b2):在[b,e]创建b2的拷贝
uninitialized_fill_n(b,n,b2):在b开始创建b个
pair<string*,string*>
alloc_n_copy(const string* b,const string* e)
{
auto data = alloc.allocate(e-b);
return {data,uninitialized_copy(b,e,data)};
}
C++Primer学习——动态内存的更多相关文章
- C++ Primer 5th 第12章 动态内存
练习12.1:在此代码的结尾,b1 和 b2 各包含多少个元素? StrBlob b1; { StrBlob b2 = {"a", "an", "th ...
- 重拾c语言之动态内存分配
动态内存分配 传统数组的缺点: 1数组长度必须事先制定,且仅仅能是长整数不能是变量 2传统形式定义的数组该数组的内存程序无法手动释放 3数组一旦定义,系统就会为该数组分配的存储空间就会一直存在直到该函 ...
- C++动态内存new和delete(超详细)
C++动态内存new和delete C++动态内存是C++灵活.炫酷的一种操作.学好它,能让自己编程逼格上一个level. 在学习动态内存之前,我们先要了解C++是怎么划分内存的: 栈:在函数内部声明 ...
- C++ Primer : 第十二章 : 动态内存之shared_ptr与new的结合使用、智能指针异常
shared_ptr和new结合使用 一个shared_ptr默认初始化为一个空指针.我们也可以使用new返回的指针来初始化一个shared_ptr: shared_ptr<double> ...
- C++ Primer : 第十二章 : 动态内存之动态内存管理(new和delete)
C++语言定义了两个运算符来分配和释放动态内存:运算符new分配内存,运算符delete释放new分配的内存. 运算符new和delete 使用new动态分配和初始化对象 在自由空间分配的内存是无名的 ...
- C++ Primer : 第十二章 : 动态内存之shared_ptr类实例:StrBlob类
StrBlob是一个管理string的类,借助标准库容器vector,以及动态内存管理类shared_ptr,我们将vector保存在动态内存里,这样就能在多个对象之间共享内存. 定义StrBlob类 ...
- C++ Primer : 第十二章 : 动态内存之shared_ptr类
在C++中,动态内存是的管理是通过一对运算符来完成的:new ,在动态内存中为对象分配空间并返回一个指向该对象的指针,delete接受一个动态对象的指针,销毁该对象,并释放该对象关联的内存. 动态内 ...
- C++学习笔记(十一):void*指针、类型转换和动态内存分配
void*指针 void关键字表示“空类型”的概念.但是,这里的“空类型”不表示“任意类型”,而是表示不存在的意思,也就是说C/C++不允许你写语句void a,不存在类型为void的东西. void ...
- c++学习笔记—动态内存与智能指针浅析
我们的程序使用内存包含以下几种: 静态内存用来保存局部static对象.类static数据成员以及定义在任何函数之外的变量,在使用之前分配,在程序结束时销毁. 栈内存用来保存定义在函数内部的非stat ...
随机推荐
- C语言第八次作业
一.PTA实验作业 题目1:统计一行文本的单词个数 1.本题PTA提交列表 2.设计思路 // 一个非空格和一个空格代表一个单词 char str[1000]: 存放一行文本 定义 I,j=0:用作循 ...
- Alpha冲刺No.3
冲刺Day3 一.站立式会议 终于我们遇到了我们最艰难的时候,组员也反映每天做的事情越来越少,出现了问题越来越多. 人太少,时间太少,我们没有办法一个人花足够多的时间去钻研统一个问题,或许是所以组员的 ...
- 201621123060《JAVA程序设计》第九周学习总结
1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结集合与泛型相关内容. 2. 书面作业 本次作业题集集合 1. List中指定元素的删除(题集题目) 1.1 实验总结.并回答:列举至 ...
- 解决python中flask_sqlalchemy包安装失败的问题
在进行flask_sqlalchemy包的下载安装时出现以下问题: 由图片可看出是编码转换出了问题,找到pip\compat_init_.py文件,打开它并查看第73行,将代码做如下更改并保存: 问题 ...
- c++第0次作业
1.你认为大学的学习生活.同学关系.师生应该是怎样? 随着大学生活的慢慢到来,我开始领悟到大学并不是自由的天堂,相反,我们更加的走进社会这个牢笼.在这个牢笼中有着从前的我们并不需要在意和考虑的规则与问 ...
- Vue.js学习
<!DOCTYPE html> <html> <head> <title>xxx</title> </head> <bod ...
- 《Language Implementation Patterns》之 解释器
前面讲述了如何验证语句,这章讲述如何构建一个解释器来执行语句,解释器有两种,高级解释器直接执行语句源码或AST这样的中间结构,低级解释器执行执行字节码(更接近机器指令的形式). 高级解释器比较适合DS ...
- 团队作业4——第一次项目冲刺(Alpha版本)2017.11.16
第一次会议:2017-11-16 大家的任务完成的不错^_^,继续努力了. 上图: 忘记照了,额....... 会议主要内容: 1.登录功能的讨论 2. 代码统一 具体分工: 成员 计划任务 遇见难题 ...
- Trie树(转)
原文http://www.cnblogs.com/TheRoadToTheGold/p/6290732.html 一.引入 字典是干啥的?查找字的. 字典树自然也是起查找作用的.查找的是啥?单词. 看 ...
- C#系统服务安装
转载 http://blog.csdn.net/vvhesj/article/details/8349615 1.1创建WindowsService项目 导入需要的引用比如System.configu ...