C++开发初级


639 浏览 5 years, 4 months

99.5 智能指针

版权声明: 转载请注明出处 http://www.codingsoho.com/

智能指针

new和delete的问题

C++中使用new和delete动态管理内存

  • 使用过程非常容易出问题,确保在正确的时间释放内存非常困难
  • 忘记释放内存,导致内存泄漏;
  • 过早释放内存,如果还有其他指针访问该内存而导致非法访问错误
  • 动态请求的内存释放多次也会导致程序异常
  • 加入异常处理机制后,导致内存的有效管理非常困难

unique_ptr

unique_ptr的必要性

传统的内存管理模式

void fun()
{
    int *p=new int(42);
     //访问数据
    delete p;
}

传统内存管理,强调谁使用,谁申请,谁释放。
问题:若果函数有多个分支,很难保证数据正确释放; 访问数据过程中抛出异常,delete被跳过!

void fun()
{
    int *p=new int(42);
    try{
         //访问数据
    }
    catch()
    { 
        delete p; 
        throw; 
    }
    delete p;
}
unique_ptr独占管理内存

unique_ptr以独占方式管理底层的内存对象,智能指针是局部对象,超过作用域后会自动析构,并自动释放所绑定的内存对象。
每块内存对象只能对应1个智能指针。

void f()
{
    unique_ptr<int> p(new int(42));
     //使用数据
}

函数返回后,自动释放p所对应的内存。
使用智能指针就如同使用普通指针,*p解析所指向的 数据,->可以访问对象方法。
不支持智能指针的算术运算,如++、+等

释放内存的时机

智能指针unique_ptr超出作用域,被释放后引起内存的释放。

将智能指针置为nullptr

unique_ptr<string> p(new string("hello"));
p=nullptr;

调用reset方法

unique_ptr<string> p(new string("hello"));
p.reset();
unique_ptr为空

空的unique_ptr没有控制底层的内存对象。

unique_ptr<string> p;

初始没有分配内存

unique_ptr<string> p(new string(Hello));
p=nullptr;

强行释放所分配的内存

string *sp=p.release();

释放并归还所分配的内存

if(p) {...}
if(p!=nullptr) {...}

判断智能指针是否为空。

unique_ptr主权的转移
  • 1块内存只能由1个智能指针unique_ptr管理
  • unique_ptr指针不能拷贝
  • unique_ptr指针不能赋值
  • unique_ptr的主权可以转移
unique_ptr<string> p1(new string);
unique_ptr<string> p2(p1); X
unique_ptr<string> p3;
p3=p1; X
unique_ptr<string> p4(move::p1);
p3=move(p4);

share_ptr

share_ptr管理共享内存

share_ptr允许多个智能指针共享底层的内存对象,通过引用计数控制内存的释放,引用计数大于等于1时保留内存,引用计数为0释放内存。

指针析构、拷贝构造和赋值都可改变引用计数。

void f()
{
    share_ptr<int> p(new int(42));
     //使用数据
}
share_ptr引用计数

新建指针后,引用计数为1,指针析构后引用计数为减1,拷贝构造或赋值后引用计数加1

auto p1=make_shared<int>(42); //引用计数为1
p1=make_share<int>(43);

p1原先所指内存对象计数 为0,内存被释放

share_ptr<int> p2(p1);

引用计数增加为2

share_ptr<int> p3;
p3=p2;

引用计数增加为3

直接分配内存

make_shared,可直接分配内存并返回智能指针

void fun()
{
    share_ptr<int> p1=make_shared<int>(42);
    auto p2=make_shared<double>(4.2);

}

make_share分配内存,并返回包装该内存智能指针。

p1包装int型内存,初始值为42,p2包装double型内存, 初始值为4.2

fun返回时,p1和p2包装的内存都将自动释放。

share_ptr的参数传递
int ff()
{
    share_ptr<int> p1=make_share<int>(42); //计数为1
    fun(p1);
    return 0;
} //返回时,销毁p1,引用计数为0,释放内存
void fun(share_ptr<int>& p)
{
    auto p2(p); //计数变为2
} //返回时p2销毁,引用计数变为1,不能释放内存

管理动态数组

unique_ptr指针管理动态数组
unique_ptr<int[]> p1(new int [10]);
p1.release(); //释放内存
for(int i=0;i<10;++i)
    p1[i]=i;
// > Fix markdown bug

unique_ptr支持对动态分配数组的包装,支持相应的下 标运算符,以访问不同的数据元素。

share_ptr指针管理动态数组
share_ptr<int> sp(new int[10],
    [ ](int *p) { delete[] p } );
sp.reset(); //释放内存
for(int i=0;i<10;++i)
    *(sp.get()+i)=i;
// >

share_ptr正常情况下只能包装单个内存数据,若要管理 动态数组,需要提供删除器。

调用sp.reset时,释放内存,调用Lambda表达式按自定义 方式释放内存。

share_ptr不支持动态数组的下标运算符。

参考代码 SmartPointer.cpp