指向堆内存的指针,很容易忘了释放:
int foo()
{
int* p = new int(9);
cout << *p << endl;
return *p;
}
为什么要用指针
使用堆数据有以下几个目的(也可称为作用)如表7-18所列
使用堆数据的目的 | 作用说明示例 | 附加说明 |
数据需要拥有超出当前代码块的生命周期 | 多线程时传递给其他线程 | 还未学习线程,暂不说明 |
在特定时机下初始化全局指针,例如: int* p//全局变量 void foo() { p = new int(9); } | 通常不推荐使用全局数据 | |
作为全局容器的指针元素,例如: list <int*> l; //全局容器 void foo() { int* p = new int(9); l.push_back(p); } | 同上,并且通常也不太推荐在容器中存储基本类型数据的指针 | |
需要传递到其他函数,由其他函数删除它 | 在单线程的情况下,这种设计不太好,违背谁创建谁释放的原则 | |
数据的状态表达,需要有“空”或“无”这个状态 | 比如让一个人心里默想一个整数,然后还要有一个状态,表示他大脑防控,什么数都没想。光使用int无法表达这个状态,如果使用"int* p",可以让p为nullptr时表示放空状态 | 指针为空的状态很常见,但通常不会仅仅为了多一个状态表达,而改为使用指针 |
所要使用的内存很大,必须使用堆内存 | 通常一个程序可用堆空间,要大于可用占空间。 例如下面这行代码,可能会让程序挂掉: int a[665536] 改为,使用对空间则问题不大: int* pa = new [665536]; | 推荐使用标准库容器,如array,vector, list等 |
需要在运行期才能决定所要分配的内存大小 | int n; cin >> n; int* pa = new [n]; | 同上 |
直接给出的结论:
使用C++编写程序,如果没有用“面向对象”,则很大程度上不必要也不应该通过裸指针手工分配堆内存。
两个可怕的错误:
int foo()
{
int* p = new int(9);
cout << *p << endl;
delete p; //我释放了哦
return *p;
}
return时访问已经被释放的“*p”
int foo()
{
int* p = new int;
if(...)
{
*p = 10;
return *p;
}
else
{
switch(...)
{
case 1:
return 20;
default:
...
}
}
delete p;
return 0;
}
函数中有一堆return,每个都可以结束函数,但除了最后一个,其他几个函数结束之前,都没有释放p。
怎么办?最简单的办法是,将例子中堆数据,都应该改成栈数据。
但是以后学习面向对象时,许多时候对象必须使用new创建,一不小心忘了释放,又该怎么办?有没有办法让一个对象在堆中创建,然后又会自动释放呢?
方法就是在堆中创建一个对象(称为po),然后在栈中也创建一个对象(称为Killer),然后把po交给killer来管理。
po当然可以就是“ int* ”这样简单的指针,
为了更清楚地观察对象的释放,我们为它定制下面这样的结构
然后,“O”类型的对象,交给另一个类Killer管理
对象释放时,会自动调用析构函数。定义一个Killer的栈变量,栈变量会自动释放,释放时会自动调用其析构函数,我们让析构函数负责删除po:
这个版本的“foo()”函数,创建了名为po的堆对象,后面却没有delete po的显式代码,但内存也没有泄露。因为k是栈对象,会自动释放,并会在临死之前,无情地将po干掉。
foo()运行效果
Killer的缺点:
killer管理的是“O*” ,如果要管理“int *”或其他,就得重写一个管理者
解决方案:
第一,C++有模板技术,可以只写一份代码,就能管理各种类型指针;第二,万一代码很长,程序员用着指针po,用着用着,就忘了之前将它托管给某个Killer的是,于是程序员很负责任地手工删除它:比如:
运行效果:可以看到内存被释放两次
怎么办?解决办法是:干脆不要po这个对象,在构建Killer对象是,直接new出一个无名对象作为入参:
运行效果:
至此还不能满意,因为好好的“po->HaHa()”不得不写成“k.po->HaHa”,有没有办法直接写作“k->HaHa()”呢?在C++中可以通过为Killer重载“->”这个操作符来实现。
重载“->”的Killer例子:
事实上,C++已经为我们提供了很好的实现,术语上当然不是血腥的“Killer”,而是智能指针“smart pointer(智能指针)”,不管叫什么,一定要记得它是“管理者”,被它管着的,称为“raw pointer(裸指针)”
智能指针分两种:
根据管理方式,C++智能指针又分成两种:一种是独占式智能指针。即一个裸指针,在同一个时间点,只允许有一个管理者。另一种是共享式智能指针,允许一个裸指针被多个智能指针同时管理。
标准库智能指针包含在“<memory>”头文件中。