C++11新特性(2) ——动态内存和智能指针从入门到入坑

动态内存与智能指针

动态内存的使用十分容易出现问题(内存泄漏/非法内存),而智能指针能更安全、容易的使用动态内存,因为他负责自动释放所指向的对象,并且在出现异常时,也会自动释放。
两种智能指针,区别是管理底层指针的方式:
shared_ptr :允许多指针指向同一个对象
unique_ptr独占所指向的对象
weak_ptr:弱引用,指向shared_ptr所管理的对象。
auto_ptr:和weak相比,主要差异是支持拷贝构造与赋值操作

shared_ptr p1; ///< 指向string
shared_ptr <list> p2; ///< 指向int的list
默认初始化的智能指针中保存一个空指针,使用方法与指针类似

shared_ptr和unique_ptr共有操作

shared_ptr sp unique_ptr up 默认初始化空智能指针
使用智能指针:p *p p->mem 与指针类似
p.get() 返回p中保存的指针。(若智能指针释放,则返回指针所指向对象也释放了)
swap(p,q) p.swap(q)交换p和q的指针

shared_ptr独有操作

make_shared(args) 返回shared_ptr,指向一个动态分配,以args初始化类型为T的对象
shared_ptrp(q)p是q的拷贝,此操作会递增q中计数器
shared_ptrp(q,d)p是q的拷贝,此操作会递增q中计数器,d用于代替delete函数
p=q递减p引用计数,递增q引用计数,引用为0时释放内存
p.use_count() 返回和p共享对象的智能指针数量
p.unique 若count 为1 返回true

关于new分配内存的操作
shared_ptr p(q) q指向new分配的T*类型数据,p管理该对象
shared_ptr p(u) 从unique_ptr对象u接管所有权,u置为空
shared_ptr p(q,d) p接管new出来的q,并且使用d函数代替delete函数
p.reset()、p.reset(q)、p.reset(q,d) 若有d,则使用d代替delete,若有q,则令p指向q,若都无,则代表释放此对象。

unique_ptr操作


/// release 放弃控制权,返回指针并置为空(不会释放之前指向的对象)
/// reset(q)   释放指向的对象,并指向新对象q,若为空,则只是释放操作

int main(int argc, char **argv)
{
    unique_ptr<int> uni1(new int(1));

    unique_ptr<int> uni2(uni1.release());   /// uni1放弃对指针的控制权,返回指针,并将uni1置为空

    unique_ptr<int> uni3(new int(3));

    uni1.reset(new int(4));                 /// 释放uni1指向的对象,并让uni1指向新的对象
    uni1.reset(nullptr);                    /// 释放uni1指向的对象,并让uni1指向null         

    cout << uni1.get() <<endl;
    if (uni1.get() != NULL)
    {
        cout << *uni1 <<endl;
    }

    cout << uni2.get() <<endl;
    if (uni2.get() != NULL)
    {
        cout << *uni2 <<endl;
    }
}

智能指针使用

智能指针管理对象原理每个shared_ptr都有一个关联的计数器,称为引用计数,当引用计数为0时,会自动释放所管理的对象

make_shared初始化

使用动态内存最安全的方法就是使用make_shared
shared_ptr p = make_shared(42); /// 指向int的shared_ptr
shared_ptr p = make_shared(42); ///指向string “9999999999”
auto p = make_shared<vector>
p = q; p内存自动释放 q引用递增
/// p的作用域结束时,调用析构函数,指向的内存自动释放,而return p时,引用计数发生了递增。

使用类实现数据共享

智能指针加容器


initializer_list v 代表是初始化列表传递

shared_ptr 和 new 结合使用

shared_ptr p = new int(40); /// 错误。不能将普通指针隐式初始化为智能指针
shared_ptr p(new int (42)); /// 正确,直接初始化
当shared_ptr绑定到一个普通指针时,内存管理的责任将交给shared_ptr,不应该再使用内置指针访问被管理的内存,因为此时内置指针访问时,智能指针是不知道的。所以建议在定义智能指针时直接初始化动态内存。

智能指针的异常兜底

void func()
{
    int *p = new int(42);
    /// 若此时抛出异常,且未进行捕获操作,则发生内存泄漏
    delete p;
}

void func()
{
    shared_ptr <int> sp(new int (42));
    /// 抛出异常且未捕获
    /// 函数结束时自动释放内存
}

unique_ptr

与shared_ptr的区别是,它是独占式,并且没有make_shared操作,需要绑定到new 返回的指针上。

unique_ptr p1; /// 正确,指向double
unique_ptr p2(new int(42)) ///正确,指向值为42的int

因为unique_ptr是独占式,所以不支持普通的拷贝或赋值,但有例外,可以拷贝或赋值一个将要被销毁的unique_ptr,最常见的就是函数中返回一个unique_ptr。

重写unique_ptr的删除器代替delete

decltype关键字

auto和decltype都用于自动类型推导,但用法有差别。
auto valname = value;
decltype(exp) varname = value;
decltype(exp) varname;
///auto根据右边的初始值value,推导变量类型,而decltype根据exp表达式,推导出变量类型
///,auto必须要初始化
如果 exp 是一个表达式,或者是一个单独的变量,那么 decltype(exp) 的类型就和 exp 一致。
如果 exp 是函数调用,那么 decltype(exp) 的类型就和函数返回值的类型一致。所以不能返回void类型
如果 exp 是一个左值,或者被括号( )包围,那么 decltype(exp) 的类型就是 exp 的引用;假设 exp 的类型为 T,那么 decltype(exp) 的类型就是 T&。
如果exp是函数类型,那么返回也是函数类型。加上*后变为函数指针

weak_ptr

弱引用智能指针:不控制所指向对象的生存周期,指向由shared_ptr管理的对象,使用前必须调用检查对象是否还存在。

weak_ptr绑定到shared_ptr之后,不会改变它的引用计数,实现“弱”共享对象。

auto p = make_shared(42);
if (shared_ptr np = wp.lock()) { ///检查对象是否还存在,存在才能访问,保证安全
weak_ptr wp§; ///弱共享p,不改变p的引用计数。
}

注意事项

1、get 用来将指针的访问权限传递给代码,仅限于某些不能传递智能指针的场合。因为get到指针地址后,如果用该普通指针初始化智能指针,并不会增加引用计数,之后可能发生double free。(不能用于初始化或reset另一个智能指针,不能delete)
2、不使用相同的内置指针初始化(或reset)多个智能指针。(多个智能指针管理同一片内存,并且互不感知)
3、如果智能指针管理的资源,不是new出来的内存,需要传递一个删除器代替delete操作
4、智能指针的内存泄漏问题:当两个shared_ptr互相指向,则会造成循环引用,引用计数始终不为0,导致内存泄漏。解决办法:将其中一个share_ptr改为弱引用指针weak_ptr,弱引用指针不修改引用计数的值,从而不会对对象的内存进行管理。并且它可以检测到所管理对象是否已释放,避免非法访问
5、引用计数并不是原子操作,第一步复制内容的ptr指针,第二步引用计数+1,所以多线程需要加锁

动态数组

.。。。。。。。。。。。。。待完善

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值