前言
- 静态/全局
- 用来保存局部static对象,类static数据成员以及定义在任何函数之外的变量。
- 由编译器自动创建和销毁
- static对象在使用之前分配,在程序结束时销毁
- 栈内存
- 用来保存定义在函数内的非static对象
- 由编译器自动创建和销毁
- 仅在其定义的程序块运行时才存在
- 堆
- 用堆来存储动态分配的对象——那些在程序运行时分配的对象。
- 就是由malloc分配的内存块
- 动态对象的生存期由程序来控制,也就是说,当动态对象不再使用时,我们的代码必须显式的销毁它们
- 常量存储区
- 用来存放常量,程序结束时自动释放
- 自由存储区
- new。与堆类似.。一般意义上,new都是由malloc为底层的,有时候自由存储区和堆并没有什么区别
动态内存与智能指针
- 动态内存的管理是通过一对运算符来完成的:new和delete
- 忘记释放内存,就会导致内存泄露
- 提前释放内存,就会导致产生非法指针
智能指针 | 操作/意义 |
---|
shared_ptr | 允许多个指针指向同一个对象 |
unique_ptr | “独占”所指向的对象 |
代码 | 操作/意义 |
---|
shared_ptr< type > sp ,unique_ptr< type > up | 空智能指针,可以指向类型为type的对象 |
p | 将p作为一个条件判断,if(p),若p指向一个对象,则true |
*p | 解引用 |
p.get() | 返回p中保存的指针。要小心使用。若智能指针释放了其对象,返回的指针所指向的对象也就消失了 |
swap(p,q)/p.swap(q) | 交换p, q |
- shared_ptr 类
- 类似vector,当我们创建智能指针时,必须指出指针可以指向的类型
shared_ptr < string > p1; //可以指向string
shared_ptr< list<int> > p2; //可以指向,int为元素的list
shared_ptr独有的操作
代码 | 操作 |
---|
make_shared< T >(args) | 返回一个shared_ptr,指向一个动态分配的类型为T的对象,并用arg初始化此对象 |
shared_ptr< T >p (q) | p是shared_ptr q的拷贝,此操作会递增q中的计数器 |
p = q | 两者都是shared_ptr,所保存的指针必须能相互转换。此操作会递减p的引用计数,递增q的引用计数 |
p.unique() | 若p.use_count()为1,则ture |
p.use_count() | 返回与p共享的智能指针数量,可能很耗时 |
make_shared函数
- 函数在动态内存中分配一个对象,并初始化它,返回指向此对象的shared_ptr
//指向一个值为42的int的shared_ptr
shared_ptr<int> p1 = make_shared<int>(42);
//指向“9999999999”的string
shared_ptr<string> p2 = make_shared<string>(10, '9');
//指向一个值初始化的int,即值为0
shared_ptr<int> p3 = make_shared<int>();
----------------------------------------------------------------
//通常,我们使用auto来保存make_shared的结果
auto p4 = make_shared<vector<string>>();
shared_ptr的拷贝和赋值(计数器)
- 每一个shared_ptr都有一个关联的计数器,通常称其为引用计数
- 无论我们何时拷贝一个shared_ptr,计数器都会递增:比如当用一个shared_ptr初始化拎一个shared_ptr时,或者将它作为参数传递给一个函数以及作为函数的返回值时,它所关联的计数器都会加1
- 当给shared_ptr赋予一个新值,或是shared_ptr被销毁时,计数器就会递减
- 一旦一个shared_ptr的计数器变为0,它就会自动释放自己所管理的对象
- shared_ptr自动销毁所管理的对象
- 通过析构函数完成该操作
直接管理内存
- new就是动态分配内存的!!!
- 在自由空间分配的内存是无名的,返回的是一个指向该对象的指针。
int *pi1 = new int;//默认初始化,值未定义
int *pi2 = new int();//值初始化,为0
//指向的对象的值为1024
int *pi = new int(1024);
//指向十个9的字符串
string *ps = new string (10,'9');
auto p1 = new auto(obj);
//p指向个与obj类型相同的对象,该对象用obj进行初始化
auto p2 = nwe auto{a,b,c};
//错误,括号中只能有单个的初始化器。即只能有一个参数
动态分配const对象
分配并初始化一个const int
const int *pci = new const int(1024);
内存耗尽
- 如果内存分配失败,new抛出std::bad_alloc
- int *p2 = new int;
释放内存delete
- shared_ptr智能指针可以自动销毁释放内存
- 但是通过new创建的动态内存,直到使用delete销毁前,它都是存在的
- 不能重复对指针进行delete
- 如果调用的函数返回值是一个动态内存分配的指针
type* func(type arg)
{
return new type(arg);
}
void use_func(type arg)
{
type* p = func(arg);
//delete p;
}
//这时,需要调用者来销毁动态内存
//func内不能销毁,不然就无法传参了
- 向shared_ptr传递删除器


Foo* factory(T arg)
{
return new Foo (arg);//函数factory使用new创建了动态内存
}
void use_factory(T arg)
{
Foo *p = factory(arg);//调用了函数factory
//此时没有进行delete
}
//那么当跳出该函数之后,我们就失去了指向该内存块的指针
//我们也就再也无法释放该内存
//长此以往,就会不断使得我们的可用内存减少
- 智能指针也可能导致内存泄露
- 如果申请了一个vector<shared_ptr > vec;
- 如果因为一些原因,对vec进行了重排序,从而不需要了一些vec中的元素
- 那么这样,不需要的那些元素就会被遗忘
- 其相对应的内存就会泄漏
坚持使用智能指针,就能避免这三点
new 和 delete | 易出错的三点 |
---|
1 | 忘记delete内存,导致内存泄露 |
2 | 使用了已经释放掉的对象 |
3 | 同一块内存释放两次 |
shared_ptr 和 new 结合使用
shared_ptr<int> p(new int(42));//p是一个智能指针,指向一个int 42
- 记住上面那种初始化就行了
- 虽然new 和智能指针可以混合变换使用
- 但是既然能够使用智能指针,那为什么还要用new呢?
//不能将内置指针转换为智能指针
`shared_ptr<int> p(new int(42));
`shared_ptr<int> p = new int(42); //错误,必须直接初始化
- get() 返回的指针,我们只用于向一个函数传递内置指针这一种情况
- 并且,delete这个指针是不能删除指针对应的动态内存的;
- 更不要用get()的值,去给一个智能指针赋值

智能指针与异常

- unique_ptr
- 由于unique_ptr是独占的,因此不支持普通的拷贝和赋值操作
unique_ptr<string> p1(new string("Stegosaurus"));
unique_ptr<string> p2(p1); //错误: unique_ptr不支持拷贝
unique_ptr<string> p3;
p3 = p1; //错误: unique_ptr不支持赋值
unique_ptr | 操作 |
---|
unique_ptr< type > u1 | 空的unique_ptr,指向类型为type的对象。u1会调用delete |
unique_ptr< type, D> u2 | 来释放它的指针;u2会使用一个类型为D的可调用对象来释放它的指针 |
unique_ptr< type, D> u(d) | 空的unique_ptr,指向类型type的对象,用类型为D的对象d代替delete |
u = nullptr | 释放u所指向的对象,将u置为空 |
u.release() | u释放对指针的控制权,返回指针,并将u置为空 |
u.reset() | 释放u指向的对象 |
u.reset(q) | 如果提供了内置指针q,令u指向这个对象,否则将u置为空。如果u原先指向有对象,则释放。 |
- 虽然我们不能拷贝或赋值unique_ptr,但是可以通过调用release或reset将指针的所有权从一个(非const)unique_ptr转移给另一个unique:
unique_ptr<string> p1(new string("Stegosaurus"));
unique_ptr< string> p2(p1.release()); //所有权从p1转移给p2,release将p1置为空
unique_ptr< string> p3(new string("Trex"));
p2.reset(p3.release()); //p2现在指向的是"Trex"
- 不能拷贝unique_ptr有一个例外
- 我们可以拷贝或赋值一个将要被销毁的unique_ptr
- 最常见的例子就是函数返回以个unique_ptr;
unique_ptr< int> clone(int p)
{
return unique_ptr<int> (new int(p));
}
- 向unique_ptr传递删除器
- 与shared_ptr有些许的不同,这里有两个参数

- weak_ptr
- 这是一种不控制所指对象生存期的智能指针,它指向一个由shared_ptr管理的对象
- 将一个weak_ptr绑定到shared_ptr并不会改变shared_ptr的引用次数
- 一旦最后一个指向对象的shared_ptr被销毁,对象就会被释放
- 即使有weak_ptr指向对象,对象还是会被释放
- 因此weak_ptr的名字就是弱!!!
我们创建一个weak_ptr时,要用shared_ptr来初始化它
auto p = make_shared<int> (42);
weak_ptr<int> wp(p); //弱共享,p的计数并未改变

动态数组
int *pia = new int[get_size()]; //pia指向第一个int.//方括号中大小必须是整形,但不必是常量
//也可以这样
typedef int arrt[42]; //arrt表示42个int的整数类型
int *p = new arrt;
- 动态数组并不是数组,我们并没有得到一个数组类型的对象,只是得到一个数组元素类型的指针
- 也不能使用范围for语句来访问所谓的动态数组中的元素
- 初始化
- int *pia = new int[10]; //10个未初始化的int
- int *pia2 = new int [10] (); //值初始化为0的int
- string *psa = new string[10]; //10个空string
- string *psa = new string [10] (); //10个空string
- 还可以直接初始化:
- int *pia3 = new int[10] {0,1,2,3,4,5,6,7,8,9};
- 如果列出的值个数少于初始化器所需的数目,则不足的部分将进行值初始化
- 如果超出了所需的个数,则抛出bad_array_new_length的异常
- 释放动态数组
- delete p; //p必须指向一个动态分配的对象或空
- delete [ ] pa; //pa必须指向一个动态分配的数组或空
- allocator 类
- new和allocator的区别:
- new将内存分配和对象构造组合在了一起
- 类似的,delete将对象析构和内存释放组合在了一起
- allocator,先分配内存,在需要时在进行真正对象的创建操作
allocator<string> alloc; //可以分配string的allocator对象
suto const p = alloc.allocate(n); //分配n个未初始化的string
