C++学习笔记----6、内存管理(五)---- 智能指针(1)

        就像前面讨论到的,C++中的内存管理是错误与bug的罪恶之源。这许多的bug由于使用动态内存与指针遭遇不断上升。当你在程序中大量使用动态内存分配,在对象之间传递许多指针时,就很难记住对每一个指针只在正确的时间进行一次delete调用。弄错了的后果是很严重的:当你将动态分配的内存释放了多次,或者使用指向已经释放了内存的指针时,就会造成内存崩溃或者严重的运行时错误;而当你忘记对动态分配的内存进行释放时,又会造成内存渗露。

        智能指针会帮助你管理动态分配内存,是推荐的避免内存渗露的技巧。从概念上来说,智能指针可以吼住像内存这样的动态分配资源。当智能指针不在范围内或者被重置时,就会自动释放吼住的资源。智能指针可以被用于在函数范围内,或者在类的成员函数范围内管理动态分配的资源。也可以用于在函数参数中传递动态分配资源的属主。

        C++提供了山积 使动态指针吸引人的几个语言属性。首先,你可以写一个使用模板的任何指针类型的类型安全的智能指针类。其次,可以提供一个智能指针的接口,使用重载操作符,允许 代码使用智能指针对象,就像原始的傻瓜指针一样。需要特别指出的是,可以重载*,->,以及[]操作符,客户端代码可以像间接引用正常指针一样对智能指针进行同样的操作。

        有好几种类型的智能指针。最简单的类型就是取得了资源的主要/独一无二的所有权。作为资源的唯一属主,智能指针可以在指针不在活动范围或者被重置时自动释放所指向的资源 。标准库提供了std::unique_ptr,这是一个带有唯一属主语法的智能指针。

        更高级一点的智能指针允许有共享属主;也就说,几个智能指针指向同一个资源。当这样的一个智能指针不在活动范围或者被重置时,那就会在只有它是指向该资源的最后一个智能指针时才会释放指向的资源。标准库提供了std::shared_ptr支持共享属主。

        unique_ptr与shared_ptr两个标准智能指针,都定义在了<memory>中,我们会进行详细讨论。

        缺省情况下要用unique_ptr。只有在需要共享资源时才会乃至shared_ptr。

        千万不要将资源分配的结果赋值会原始指针!不管你用什么样的资源分配方法,都要立刻马上把资源指针放到智能指针当中,不管是unique_ptr还是shared_ptr,或者是使用RAII(资源获取即初始化)类。RAII类对特定资源取得所有权,在适当的时间处理释放问题。这是一个设计技巧,我们以后再讨论。

1、unique_ptr

        unique_ptr对资源拥有独一无二的所有权。当unique_ptr被破坏或者被重置时,资源会自动释放。一个优点是内存与资源总是会被释放,即使是return语句被执行或者抛出例外。这样的话,举个例子,当一个函数拥有多个return语句时就会简化编码,因为不必要记住在每个return语句前面释放资源了。

        作为一个经验法则,在unique_ptr实例中总要把动态分配的资源归属到一个单独的属主中。

1.1、生成unique_ptr

        考虑下面的函数,它公然地通过在自由内存空间上分配一个Simple对象而没有释放它来制造了一个内存渗露:

void leaky()
{
	Simple* mySimplePtr{ new Simple{} }; //BUG! Memory is never released!
	mySimplePtr->go();
}

        有时候你可能会想你的代码已经正确地释放了动态分配的内存。但不幸的是,极大可能是并不是在所有情况下都是正确的。看一下下面的函数:

void couldBeLeaky()
{
	Simple* mySimplePtr{ new Simple{} };
	mySimplePtr->go();
	delete mySimplePtr;
}

        这个函数动态分配了一个Simple对象,使用了这个对象,然后正确调用了delete。然而,在这个例子中仍然可能会有内存渗露!如果go()成员函数抛出例外,对delete的调用永远不会被执行,造成内存渗露。

        那怎么办呢?你应该使用unique_ptr,用std::make_unique()类的协助函数来生成。unique_ptr是一个通用智能指针,可以指向任何类型的内存。这就是为什么它是一个类模板,而make_unique()是一个函数模板。两者都要求中括号之间的模板参数,<>,指定了要将unique_ptr指向的内存类型。模板会在以后的博文中讨论,其细节对于理解如何使用智能指针并不重要。

        下面的函数使用了unique_ptr而不是原始指针。Simple对象并没有显式地被删除;但当unique_ptr实例不在活动范围(函数的结尾,或者抛出例外),它就会调用析构函数自动释放Simple对象。

void notLeaky()
{
	auto mySimpleSmartPtr{ make_unique<Simple>() };
	mySimpleSmartPtr->go();
}

        这段代码使用了make_unique(),与auto关键字相结合,这样的话只需要指定指针的类型一次,在这个例子中,就是Simple。这是生成unique_ptr的推荐方式。如果Simple构造函数需要参数,要把它们作为参数传递给make_unique()。

  • 11
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
05-30
指针是C语言中非常重要的概念,它是一种变量类型,用来存储内存地址。通过指针,我们可以直接访问和修改内存中的数据,因此在C语言中使用指针可以实现很多高级的操作。 下面是一些关于指针的基本知识点: 1.指针的定义和初始化: 指针的定义需要指定指针类型和指针名称,例如: ``` int *p; // 定义一个指向整数类型的指针p ``` 指针的初始化可以使用取地址运算符`&`取得变量的地址,或者直接赋值为NULL表示空指针,例如: ``` int a = 10; int *p = &a; // 将指针p初始化为变量a的地址 int *q = NULL; // 将指针q初始化为空指针 ``` 2.指针的解引用: 指针的解引用可以使用`*`运算符,表示取出指针所指向的内存地址处的值。例如: ``` int a = 10; int *p = &a; printf("%d\n", *p); // 输出变量a的值10 ``` 3.指针的运算: 指针可以进行加法和减法运算,例如: ``` int a[10]; int *p = a; p++; // 指针p指向a[1] p--; // 指针p指向a[0] ``` 指针的加法和减法运算不是简单的数值相加减,而是根据指针类型计算出偏移量并加上指针当前指向的地址。 4.指向指针的指针: 指向指针的指针也是C语言中常见的概念,例如: ``` int a = 10; int *p = &a; int **q = &p; // 定义一个指向指针p的指针q ``` 指向指针的指针可以用来实现多级指针的操作,例如链表的遍历等。 希望以上内容能够帮助你更好地理解指针的概念。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

王俊山IT

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值