在c++的内存上我碰到很多很多坑~,特此来记一笔。
程序中所使用的对象都有着严格的生存期,全局对象在程序启动时分配,在程序结束时销毁;局部对象在进入其定义所在的程序块时被创建,在离开块时销毁。局部的static对象只在第一次使用前进行分配,在程序结束时销毁。
但是除了自动和static对象之外,C++还支持动态分配对象。比较特殊的是,动态分配的对象的生存期与它在哪里创建是无关的,只有在显示地被释放时,这些动态分配的对象才会被销毁。
静态内存: 保存局部static对象,类static数据成员和任何定义在函数之外的变量。
栈内存: 保存定义在函数内的非static对象。
动态内存:保存在堆上,除了静态内存和栈内存, 每个程序都有一个内存池,这个内存池被称为自由空间(free store)或者堆(heap).
new : 在动态内存中为对象分配空间并返回一个指向该对象的指针,可以在分配空间的同时对对象进行初始化。
delete : 接受一个动态对象的指针,销毁该对象,并释放与之关联的内存。
动态内存的使用非常容易出现问题,比较常见的问题是忘记释放内存,产生内存泄漏的问题;另外是有指针引用内存的情况下释放了内存,会产生引用非法内存的指针的问题。
智能指针负责自动释放所指向的对象,这里主要介绍两种指针,一种是share_ptr, 一种是unique_ptr, 下表是这两种指针都支持的操作。
share_ptr 允许多个指针指向同一个对象。在创建一个智能指针时,必须提供额外的信息,也就是指针指向的类型。如下:
下表是share_ptr独有的操作。
make_shared函数
此函数在内存分配中分配一个对象并初始化它,返回指向此对象的share_ptr, 该函数定义在memory中。调用make_shared传递的参数必须与某个构造函数匹配,如果不传递任何参数,对象则会进行值初始化。
由于share_ptr允许多个指针指向同一个对象,显而易见的是,每个shared_ptr都会记录有多少个其他shared_ptr指向相同的对象。尤其是在进行拷贝或者是赋值的操作时,这就更为必要了。
每一个shared_ptr都有一个与之关联的计数器,被称为引用计数,在需要的情况下会加1或者是减1.
加1的情况:
1. 当用一个shared_ptr初始化另外一个shared_ptr时
2.当作为实参传入到一个函数时
3. 当作为函数的返回值时。
减1的情况:
1. 当给shared_ptr赋予一个新值
2. shared_ptr被销毁,局部变量shared_ptr离开其作用域时会被销毁。
需要注意的是,当一个shared_ptr的计数器变为0时,它会自动释放自己所管理的对象。
当指向一个对象的最后一个shared_ptr被销毁是,shared_ptr类会自动销毁此对象,主要是通过析构函数完成销毁工作的。析构函数一般用来释放对象所分配的资源。销毁对象,释放内存。尤其是使用智能指针能够在动态对象不再使用时释放动态对象的特性,使得内存的使用变得非常容易。
除了采用make_shared创建和初始化智能指针,我们还可以用new返回的指针来初始化智能指针。
我们推荐使用make_shared而不是采用new的方式,这是因为采用make_shared的方式能在分配对象的同时就讲shared_ptr与内存进行绑定,避免了无意中将同一块内存绑定到多个独立创建的shared_ptr上。
一个unique_ptr拥有它所指向的对象。当unique_ptr被销毁时,它所指向的对象也会被销毁。下表是unique_ptr独有的操作。
unique_ptr没有类似的make_shared函数,当我们定义一个unique_ptr时需要将其绑定到一个new返回的指针上,初始化unique_ptr必须采用直接初始化形式。
由于一个unique_ptr拥有它指向的对象, 因此unique_ptr不支持普通的拷贝和赋值操作
虽然不能进行拷贝或者是赋值操作,但是可以通过调用release或reset将指针的所有权从一个非const unique_ptr转移到另一个unique_ptr
调用release会切断的unique_ptr和它原来管理对象的联系。release返回的指针通常被用来初始化另一个智能指针或给另一个智能指针赋值。如果我们不用 另一个智能指针来保存release返回的指针,我们得程序就要负责资源的释放。
不能拷贝unique_ptr的规则但是有一个例外,可以拷贝或赋值一个将要被销毁的unique_ptr,主要是传递unique_ptr和返回unique_ptr。
基本上shared_ptr和unique_ptr就已经讲解完了,除了采用智能指针管理动态内存,我们还可以直接管理动态内存。
用new 动态分配和初始化对象
释放动态内存
我们传递给delete的指针必须是指向动态分配的内存。或者是一个空指针,释放一块的并非new分配的内存或者是相同的指针被释放多次,其行为都是未定义的。