静态内存:用来保存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学习——动态内存的更多相关文章

  1. C++ Primer 5th 第12章 动态内存

    练习12.1:在此代码的结尾,b1 和 b2 各包含多少个元素? StrBlob b1; { StrBlob b2 = {"a", "an", "th ...

  2. 重拾c语言之动态内存分配

    动态内存分配 传统数组的缺点: 1数组长度必须事先制定,且仅仅能是长整数不能是变量 2传统形式定义的数组该数组的内存程序无法手动释放 3数组一旦定义,系统就会为该数组分配的存储空间就会一直存在直到该函 ...

  3. C++动态内存new和delete(超详细)

    C++动态内存new和delete C++动态内存是C++灵活.炫酷的一种操作.学好它,能让自己编程逼格上一个level. 在学习动态内存之前,我们先要了解C++是怎么划分内存的: 栈:在函数内部声明 ...

  4. C++ Primer : 第十二章 : 动态内存之shared_ptr与new的结合使用、智能指针异常

    shared_ptr和new结合使用 一个shared_ptr默认初始化为一个空指针.我们也可以使用new返回的指针来初始化一个shared_ptr: shared_ptr<double> ...

  5. C++ Primer : 第十二章 : 动态内存之动态内存管理(new和delete)

    C++语言定义了两个运算符来分配和释放动态内存:运算符new分配内存,运算符delete释放new分配的内存. 运算符new和delete 使用new动态分配和初始化对象 在自由空间分配的内存是无名的 ...

  6. C++ Primer : 第十二章 : 动态内存之shared_ptr类实例:StrBlob类

    StrBlob是一个管理string的类,借助标准库容器vector,以及动态内存管理类shared_ptr,我们将vector保存在动态内存里,这样就能在多个对象之间共享内存. 定义StrBlob类 ...

  7. C++ Primer : 第十二章 : 动态内存之shared_ptr类

    在C++中,动态内存是的管理是通过一对运算符来完成的:new  ,在动态内存中为对象分配空间并返回一个指向该对象的指针,delete接受一个动态对象的指针,销毁该对象,并释放该对象关联的内存. 动态内 ...

  8. C++学习笔记(十一):void*指针、类型转换和动态内存分配

    void*指针 void关键字表示“空类型”的概念.但是,这里的“空类型”不表示“任意类型”,而是表示不存在的意思,也就是说C/C++不允许你写语句void a,不存在类型为void的东西. void ...

  9. c++学习笔记—动态内存与智能指针浅析

    我们的程序使用内存包含以下几种: 静态内存用来保存局部static对象.类static数据成员以及定义在任何函数之外的变量,在使用之前分配,在程序结束时销毁. 栈内存用来保存定义在函数内部的非stat ...

随机推荐

  1. 201621123040《Java程序设计》第九周学习总结

    1.本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结集合与泛型相关内容 泛型部分思维导图 集合部分学习总结 java.util.Collection 是一个集合接口;java.util. ...

  2. 201621123031 《Java程序设计》第13周学习总结

    作业13-网络 1.本周学习总结 以你喜欢的方式(思维导图.OneNote或其他)归纳总结多网络相关内容. 2. 为你的系统增加网络功能(购物车.图书馆管理.斗地主等)-分组完成 为了让你的系统可以被 ...

  3. 200行Python代码实现2048

    200行Python代码实现2048 一.实验说明 1. 环境登录 无需密码自动登录,系统用户名shiyanlou 2. 环境介绍 本实验环境采用带桌面的Ubuntu Linux环境,实验中会用到桌面 ...

  4. Python 图片转字符画

    Python 图片转字符画 一.课程介绍 1. 课程来源 原创 2. 内容简介 本课程讲述怎样使用 Python 将图片转为字符画 3. 前置课程 Python编程语言 Linux 基础入门(新版) ...

  5. 201621123043 《Java程序设计》第2周学习总结

    1.本周学习总结 使用jdk文档查阅函数功能及代码 用switch语句是在每个case中可能在第一行是sc.nextLine;来给回车赋值: 在使用循环的时候要注意循环返回的条件,否则陷入死循环可能会 ...

  6. OO第一次阶段性总结

    经过三次作业的历练之后终于来到了写博客这一周.回顾开学来的这一个月,令我印象最深刻也是最累的一门课就是OO了.虽然上学期学过一部分Java,但这学期开学就来的OO作业还是让我在第二周就开始熬夜了.不过 ...

  7. DES加密实现的思想及代码

    感谢: http://blog.csdn.net/yxstars/article/details/38424021 上面的日志非常清晰的写出了这个DES加密的过程,主要存在初始IP置换,然后中间存在8 ...

  8. Python内置函数(59)——open

    英文文档: open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True, ope ...

  9. Python内置函数(36)——reversed

    英文文档: reversed(seq) Return a reverse iterator. seq must be an object which has a __reversed__() meth ...

  10. emqtt 试用(六)系统主题

    $SYS-系统主题 EMQ 消息服务器周期性发布自身运行状态.MQTT 协议统计.客户端上下线状态到 $SYS/ 开头系统主题. $SYS 主题路径以 "$SYS/brokers/{node ...