smartptr讲义

为什么会有智能指针?

在我们现实生活中有这样一个使用场景,每次我们new或者malloc了一块空间,完了必须需要我们手动delete或者free释放这块空间,否则会出现内存泄漏。但是,实际应用中程序未必都会按照顺序执行到释放空间那一步,有时候会在中间部分抛出异常使执行流发生跳转或者发生其他状况,总之代码未按照我们期望的那样去执行,就有可能导致内存泄漏。这时候我们就引入智能指针(自动化管理指针所指向的内存资源的释放),主要是利用RAII机制,RAII它是在面向对象语言中的一种惯用法。资源分配以及初始化,定义一个类来封装资源的分配和释放,在构造函数完成资源的分配和初始化,在析构函数完成资源的清理,可以保证资源的正确初始化和释放。

smartptr讲义目录


18721079-8a0ded548bd506be.png

18721079-4e7d457a0e30e56c.png

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;

}

18721079-f4f84cd9ddd267cf.png
18721079-8826afd662a21b42.png

可以看到,就算发生了异常,也能够保证析构函数成功执行!这里的例子是这个资源只有一个人使用,我不用了就将它释放掉。但还有种情况,一份资源被很多人共同使用,要等到所有人都不再使用的时候才能释放掉,对于这种问题,就需要对上面的SafeIntPointer 增加一个引用计数,如下:

18721079-42d1ce75a61b2d00.png
18721079-50cedd4911a9ce11.png

可以看到每一次赋值,引用计数都加一,最后每次析构一次后引用计数减一,知道引用计数为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文档;

想要获取完整资料的可以私信 关键词【资料】+资料名进行获取。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值