序言
大家五一快乐呀!!有几天没有写了,今天刚好回学校有空,就写写东西吧。
今天学习的是动态内存和智能指针。
1、C++内存
我学习的资料中是这样定义C++内存分配的,可编程内存在基本上分为这样的几大部分:静态存储区、堆区和栈区。他们的功能不同,对他们使用方式也就不同。
静态内存
静态内存用来保存局部static对象、类static数据对象以及定义在任何函数外的变量。在使用之前分配,在程序结束之后销毁。
栈内存
在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。
堆内存(动态内存)
除了静态内存和栈内存,每个程序有个内存池。这部分叫做自由内存或堆。程序用堆来动态分配的对象。动态内存的生存期可以由我们决定,如果我们不释放内存,程序将在最后才释放掉动态内存。但是,良好的编程习惯是:如果某动态内存不再使用,需要将其释放掉,否则,我们认为发生了内存泄漏现象。
接下来介绍分两大类,1、动态内存管理(new和delete);2、智能指针
2、动态内存管理(new和delete)
C++定义两个运算符来分配和释放内存。运算符new分配内存,delete释放new分配的内存。(好像还有malloc和free,后面我在学习一下。)
new
在自由空间中分配的内存是无名的,所以new不能对其分配的内存命名,而是返回一个指向该对象的指针:
int *p = new int; //pi指向一个动态分配的、未初始化的内存的无名对象
*动态分配的对象是默认初始化的,所以对于内置类型或组合类型的对象的值将是未定义的;对于类 类型,将是默认构造函数进行初始化;同时,我们可以直接初始化或者列表初始化。
int *pi =new int(1024);
string *ps =new string()//初始化为空的string,因为是类类型。
delete
我们传递给delete的指针必须是指向动态分配的内存或者空指针,不允许释放并非new分配的内存或者释放多次。这些行为都是未定义的;
我们要记住动态对象的生存期直到被释放(delete)为止
delete之后要重置指针值,当我们delete一个指针之后,指针值变得无效。虽然指针失效了,但是很多机器上的指针仍然指向保存着动态内存的地址,就变成了空悬指针,即指向一块曾经保存数据对象但是现在已经无效的内存的指针,类似未初始化的指针
int *p(new int()42);
delete p;
p =nullptr;
3、智能指针
上述动态内存的使用很容易出问题,因为确保在正确的时间的释放内存是非常困难的。忘记释放内存就会产生内存泄漏。
为了更容易也更安全的使用动态内存,标准库提供了两种智能指针shared_ptr和unique_ptr,与常规指针的区别在于它负责自动释放所指向的对象。
智能指针都允许的操作:
- shared_ptr<T> sp
- unique_ptr<T> up
- p :这个一般作为条件判断,如果指向对象,则为true;
- *p:解引用
- p->mem:等价于(*p).mem
- p.get():返回p保存的指针.不过要小心使用,如果智能指针释放了其对象,返回的指针所指的对象就消失了
- swap(p,q)和p.swap(p):交换指针
shared_ptr
shared_ptr允许有多个指针指向一个对象,当最后一个指针被销毁的时候,指向的对象也会被销毁.
shared_ptr独有的操作:
- make_shared<T> (args):返回一个shared_ptr,指向动态分配的对象
- shared_ptr<T>p(q):拷贝初始化:这个操作会递增q中的计数器.
- p =q:p的计数会减1,q的计数会加1;p如果计数为0,销毁
- p.use_count():返回与p共享对象的指针数.
- p.unique():判断是否只有一个指针指向对象,即use_count为1
- p.reset(new int(1024)):我们可以用这个给shared_ptr赋予一个新指针
shared_ptr的拷贝和赋值都会引起引用计数的变化.
最安全分配动态内存就是用make_shared(),这个类似顺序容器的emplace成员
shared_ptr<T> p = make_shared<T>(args);通常用auto
shared_ptr<T> p (new T(args));
shared_ptr不仅会自动销毁所管理的对象,还会自动释放相关联的内存
unique_ptr
与shared_ptr不一样,某个时刻只能有一个unique_ptr指向一个给定的对象,unique_ptr没有类型make_shared的函数,所以定义一个unique_ptr需要绑定到new返回的指针上。且与shared_ptr一样只能直接初始化。
- unique_ptr的操作:
- unique_ptr<T> u1:空unique_ptr,u1会使用delete释放指针
- unique_ptr<T,D> u2:u2会使用可调用对象来释放指针
- u =nullptr:释放指向的对象,将u置空
- u.release() :u放弃对指针的控制权,返回指针,并将u置空
- u.reset(q):令u指向内置指针q
注:虽然我们不能拷贝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上,不会引起计数的变化。所以weak_ptr抓住了智能指针“弱”共享对象的特点。
weak_ptr的操作
- weak_ptr<T> w:空weak_ptr,指向类型为T的对象
- weak_ptr<T> w(sp):与shared_ptr绑定,指向同一个对象的weak_ptr
- w=p:p可以是weak_ptr,也可以是shared_ptr,赋值后共享对象
- w.reset()//w.reset(p):重置
- w.use_count():与w共享对象的shared_ptr的数量
- w.expired():如果use_count返回的是0,这个返回true
- w.lock():如果expired返回true,返回一个空的shared_ ptr;否则返回一个指向w的对象的shared_ptr
也就是说weak_ptr可以不影响shared_ptr所指对象的生存期,共享对象。