为什么会有智能指针?
在我们现实生活中有这样一个使用场景,每次我们new或者malloc了一块空间,完了必须需要我们手动delete或者free释放这块空间,否则会出现内存泄漏。但是,实际应用中程序未必都会按照顺序执行到释放空间那一步,有时候会在中间部分抛出异常使执行流发生跳转或者发生其他状况,总之代码未按照我们期望的那样去执行,就有可能导致内存泄漏。这时候我们就引入智能指针(自动化管理指针所指向的内存资源的释放),主要是利用RAII机制,RAII它是在面向对象语言中的一种惯用法。资源分配以及初始化,定义一个类来封装资源的分配和释放,在构造函数完成资源的分配和初始化,在析构函数完成资源的清理,可以保证资源的正确初始化和释放。
smartptr讲义目录
1. 智能指针
Java、python 和 go 等语言中都有垃圾自动回收机制,在对象失去引用的时候自动回收,而且基本上没有指针的概念,而 C++语言不一样,C++充分信任程序员,让程序员自己去分配和管理堆内存,如果管理的不好,就会很容易的发生内存泄漏问题,而 C++11 增加了智能指针(Smart Pointer)。主要分为 shared_ptr、unique_ptr 和 weak_ptr 三种,使用时需要引用头文件<memory>。c++98 中还有 auto_ptr,基本被淘汰了,不推荐使用。而 c++11 中 shared_ptr 和 weak_ptr 都是参考的 boost 库中实现的。
1.1. 裸指针
c 语言中最常使用的是 malloc()函数分配内存,free()函数释放内存,而 c++中对应的是new、delete 关键字。malloc()只是分配了内存,而 new 则更进一步,不仅分配了内存,还调用了构造函数进行初始化。使用示例:
rawptr.cpp //
#include <iostream>
int main()
{
// malloc 返回值是 void*
int* argC = (int*)malloc(sizeof(int)); free(argC);
char*c = (char*)malloc(100); free(c);
int *age = new int(25); //做了两件事情 1.分配内存 2.初始化 int* height = new int(160);
delete height; delete age;
char* arr = new char[100]; delete[] arr;
/*delete 数组需要使用 delete[],事实上,c++原始支持的数据结构组成的数组不需要[]也可以,但 自定义的数据类型组成的数组必须使用 delete[]*/
}
new 和 delete 必须成对出现,有时候是不小心忘记了 delete,有时候则是很难判断在这个地方自己是不是该 delete,这个和资源的生命周期有关,这个资源是属于我这个类管理的还是由另外一个类管理的,如果是我管理的,就由我来 delete,由别人管理的就由别人delete,我就算析构了也不影响该资源的生命周期。例如:
// 情况 1: 需要自己 delete
const char* getName() {
char *valueGroup = new char[1000]; // do something
return valueGroup;
}
// 情况 2: 不需要自己 delete
const char* getName2() { static char valueGroup[1000]; // do something
return valueGroup;
}
只通过函数签名来看,这两个函数没有什么区别,但是由于实现的不同,有时候需要自己管理内存,有时候不需要,这个时候就需要看文档说明了。这就是使用一个"裸"指针不好的地方。
一点改进是,如果需要自己管理内存的话,最好显示的将自己的资源传递进去,这样的话,就能知道是该资源确实应该由自己来管理。
char *getName(char* v, size_t bufferSize) {
//do something
return v;
}
上面还是小问题,自己小心一点,再仔细看看文档,还是有机会避免这些情况的。但是在c++引入异常的概念之后,程序的控制流就发生了根本性的改变,在写了 delete 的时候还是有可能发生内存泄漏。如下例:
// memoryleak.cpp
void badThing(){
throw 1;// 抛出一个异常
}
void test() {
char* a = new char[1000];
badThing();
// do something delete[] a;
}
int main(int argc, char* argv[]) { try {
test();
}
catch (int i){
cout << "error happened " << i << endl;
}
return 0;
}
可以看到,就算发生了异常,也能够保证析构函数成功执行!这里的例子是这个资源只有一个人使用,我不用了就将它释放掉。但还有种情况,一份资源被很多人共同使用,要等到所有人都不再使用的时候才能释放掉,对于这种问题,就需要对上面的SafeIntPointer 增加一个引用计数,如下:
可以看到每一次赋值,引用计数都加一,最后每次析构一次后引用计数减一,知道引用计数为0,才真正释放资源。要写出一个正确的管理资源的包装类还是蛮难的,比如上面那个上面例子就不是线程安全的,只能属于一个玩具,在实际工程中简直没法用。而到了C++11,终于提供了一个共享的智能指针解决这个问题。
1.2.shared_ptr 共享的智能指针
1.1.1.简介
shared_pr是一个标准的共享所有权的智能指针,允许多个指针指向同一个对象,定义在
memory文件中,命名空间为std。shared_ptr最初实现于Boost库中,后由C++11引入
到C++STL.shared_ptr利用引用计数的方式实现了对所管理的对象的所有权的分享,即
允许多个shared_ptr共同管理同一个对象。像shared_ptr这种智能指针,《Effective
C++》称之为“引用计数型智能指针”(reference-counting smart pointer,RCSP).
shared_ptr 是为了解决auto_ptt在对象所有板上的局限性(auto_pr是独占的),在使用引
用计数的机制上提供了可以共享所有权的智能指针,当然这需要额外的开销:
(1)shared_ptr对象除了包括一个所拥有对象的指针外,还必须包括一个引用计数代理
对象的指针;
(2)时间上的开销主要在初始化和拷贝操作上,*和->操作符重载的开销跟auto_ptr是
一样;
(3)开销并不是我们不使用shared_ptr的理由,,永远不要进行不成熟的优化,直到性能
分析器告诉你这一点。
因文章过长不能一一的上传了,文章已整理为PDF文档;
想要获取完整资料的可以私信 关键词【资料】+资料名进行获取。
网友评论