动态内存
- 动态分配对象的生存期和它们在无关,只有显式地被释放时,这些对象才会被释放;
- 为了解决动态对象能够被正确释放的问题,设置智能指针,当一个对象应该被释放时,指向它的智能指针可以确保自动地释放它;
- 内存分类:
- 静态内存:保存局部static变量、类的static数据成员及全局变量,由编译器自动创建和销毁;
- 栈内存:保存局部变量,由编译器自动创建和销毁;
- 程序的自由空间(堆,heap):存储动态分配(程序运行时分配)的对象。
动态内存和指针
-
直接动态内存管理的运算符:
new
,在动态内存中为对象分配一个空间并返回一个指向该对象的指针;delete
,接受一个动态对象的指针,销毁该对象,并释放与之关联的内存。
-
问题:
- 忘记释放,内存泄漏。
- 过早释放,使用非法的引用或者指针。
-
解决:两个智能指针,和一个伴随类,定义在头文件memory中
shares_ptr
,允许多个指针同时指向一个对象;unique_ptr
,独占一个对象;- 伴随类
weak_ptr
,它是一个弱引用。指向的是shared_ptr
所管理的对象。
-
shared_ptr类
-
智能指针实现的机制。依靠智能指针类里面的引用计数器成员,当进行拷贝时,引用计数增加;当一个局部ptr被销毁或者一个ptr被赋予新值时,引用计数器减少;当一个被引用对象的引用计数为0时,它的空间就会被自动释放。
-
智能指针是一个模板类,它的定义以及初始化方式如下:
{ shard_ptr<string> p1;//指向string对象的空指针 shared_ptr<list<int>> p2;//指向int类型的list的空指针。 }
-
make_shared函数:分配和使用动态内存的方法:
{ auto p1 = make_shared<int>(42);//p指向一个值为42的int类型。 shared_ptr<string> p2 = make_shared<string>(10,'2');//p2指向内容为10个2的string类型。 shared_ptr<int> p3 = make_shared<int>();//p3指向一个值初始化为0的int类型 }
-
shared_ptr和unique_ptr都支持的操作:
操作名称 相关介绍 shared_ptr sp; 空智能指针 unique up; p 将p作为一个条件,非0返回true *p 解引用 p->mem 等价于(*p).mem p.get() 返回与p指向同一个对象的内置指针。 swap(p,q) 交换指针的值 q.swap(q) -
shared_ptr独有的操作:
操作名称 相关描述 make_ptr(agrs) 返回一个指向动态分配的类型为T的对象的shared_ptr。该对象用args进行初始化。 shared_ptr p(q) 进行拷贝初始化。 q
中的指针必须可以转换为T*
,p
是shared_ptr
类型的指针。p = q p,q都是shared_ptr,并且它们所指向的类型必须可以进行相互转换。 p.use_count() 返回的是与p共享对象的智能指针的数量,可能很慢,主要用于调式。 p.unique() 如果p.use_count()的数量为1,返回的是true,否则返回的是false
-
-
直接内存管理
- 使用new关键字创建指针分配的空间是没有命名的,返回的是指针指向该对象;
- 可以选择是否值初始化,const对象必须进行初始化(若定义默认构造函数的类类型可以隐式初始化);
- delete销毁给定指向的对象,然后释放内存:
- 不可以对同一个空间进行多次释放;
- 只能对new关键字分配的内存或者空指针进行delete;
- 由内置指针管理的动态内存在被显示释放前会一直存在;
- 空悬指针:指向一块曾经保存数据对象但现在已经无效(指针被delete)的内存的指针。
-
shared_ptr和new的结合使用(智能指针及内置指针)
-
可以用new返回的指针初始化(必须是直接初始化)智能指针:
{ shared_ptr<int> p = new int(12); //错误,对于内置类型对shared_ptr的初始化,只能是直接初始化,不能是拷贝。 // 因为智能指针将会对该内置指针指向的空间进行接管。 shared_ptr<int> p(new int(12));//正确 //使用reset,用内置指针对智能指针赋值 p.reset(new int(1024)); // 使用显式地转换也可以。 p = shared_ptr<int>(new int(12)); }
-
定义和改变shared_ptr的其他方法
操作名称 相关描述 shared_ptr p(q); q是一个内置指针,p管理内置指针的空间。q必须是由new分配的,并且可以转换为T* shared_ptr p(u); p从unique_ptr中接管对象,将u置为空。也就是unique_ptr和shared_ptr之间的转换。 shared p(q,d); 同第一个操作,但使用可调用对象d代替delete shared_ptr p(p2,d) p2是shared_ptr p2类型的拷贝,区别就是使用d来代替 delete
p.reset() 如果p是唯一指向其对象的shared_ptr,reset会释放这个对象。并将p置为空。 p.reset(q) 此时指向的是内置指针q p.reset(q,d) 此时用d代替delete -
不要混合使用普通指针和智能指针,也不要使用get初始化另一个智能指针或为智能指针赋值;
-
推荐使用make_shared而不是new。
-
-
unique_ptr
-
一个unique_ptr“拥有”它所指向的对象,即某个时刻只能有一个unique_ptr指向一个给定对象,可以认为它的引用计数只能为1;
-
除了与shared_ptr相同的操作,还支持以下操作:
操作名称 相关描述 unique_ptr u 空的智能指针。使用delete来释放空间 unique_ptr<T,D> u u会调用D代替delete unique_ptr<T,D>u(d) 空的指针,hi用类型为D的d来重载delete u = nullptr 释放u指向的空间,并置为空指针 u.release() u放弃控制权,并返回一个内置指针。u置为空指针。没有释放空间 u.reset() **释放空间,并且u置为空。**如果u为空则无需释放。 u.reset(q) 内置指针q。u释放所指向的空间,并且指向新的q。 u.reset(nullptr) 效果同 u.reset()
-
unique_ptr不支持普通的拷贝或赋值,但可以通过调用release或reset将指针的所有权从一个非const的unique_ptr转移给另一个unique。
-
可以拷贝或赋值一个将要被销毁的unique_ptr,如函数的返回值。
-
-
weak_ptr
-
不会控制对象的生存期;
-
只能用shared_ptr初始化weak_ptr;
{ auto p = make_shared<int>(42); weak_ptr<int> q(p); }
-
由于是弱引用,它的引用不计入引用计数中,因此它可能是无效的,使用时需要进行判断,使用local:
{ if (shared_ptr q = wp.local()) { //保证q是有效的 } }
-
动态数组
- 解决一次为多个对象分配、释放内存的问题;
- 大多数应用应使用标准库容器而不是动态分配的数组,使用容器更为简单、更不容易出现内存管理错误并且可能有更好的性能;
- 具体参考动态数组。
使用标准库:文本查询程序
- 文本查询程序设计思路:
- 利用vector存储文本中的每一行。
- 利用istringstream对输入的每一行进行分解。
- 利用set存储每一个单词出现的所有行号。
- 利用map将每一个单词和它的set相互关联起来。
- 使用共享数据,shared_ptr(设计两个类)。