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

3、weak_ptr

        在C++中还有一个与shared_ptr相关的智能指针叫做weak_ptr。weak_ptr可以包含一个被shared_ptr管理的资源的引用。weak_ptr自身不拥有资源,所以shared_ptr不被禁止释放资源。当weak_ptr被破坏时(如当其不在活动范围内),weak_ptr不破坏指向的资源;然而,它可以用于决定资源是否被相关shared_ptr释放。weak_ptr的构造函数要求shared_ptr或者另一个weak_ptr作为参数。要访问存储在weak_ptr中的指针,需要将其转化为shared_ptr。有两种方式:

  • 在weak_ptr实例上使用lock()成员函数,它返回一个shared_ptr。如果weak_ptr同时被释放的话它就会返回一个nullptr。
  • 生成一个新的shared_ptr实例,将weak_ptr作为参数传递给shared_ptr构造函数。如果weak_ptr与之相联的shared_ptr被释放的话就会抛出一个std::bad_weak_ptr的例外。

以下的示例展示的weak_ptr的用法:

import std;
using namespace std;

class Simple
{
public:
	Simple() { println("Simple constructor called!"); }
	~Simple() { println("Simple destructor called!"); }
};

void useResource(weak_ptr<Simple>& weakSimple)
{
	auto resource{ weakSimple.lock() };
	if (resource) {
		println("Resource still alive.");
	}
	else {
		println("Resource has been freed!");
	}
}

int main()
{
	auto sharedSimple{ make_shared<Simple>() };
	weak_ptr<Simple> weakSimple{ sharedSimple };

	// Try to use the weak_ptr.
	useResource(weakSimple);

	// Reset the shared_ptr.
	// Since there is only 1 shared_ptr to the Simple resource, this will
	// free the resource, even though there is still a weak_ptr alive.
	sharedSimple.reset();

	// Try to use the weak_ptr a second time.
	useResource(weakSimple);
}

结果如下:

Simple constructor called!
Resource still alive.
Simple destructor called!
Resource has been freed!

weak_ptr也支持C风格的数组,与shared_ptr一样。

4、传参给函数

        只有在传递属主或者属主共享存在的时候,能够接受指针作为其参数也应该接受智能指针。为了共享shard_ptr的属主,只接受通过值的shared_ptr作为参数。同样的,为了传递unique_ptr的属主,只接受通过值的unique_ptr作为参数。后者要求使用move的语法,我们以后再讨论。

        如果既没有属主传递也没有属主共享介入,函数只有reference-­to-­non-­const或者 reference-­to-­const参数指向其背后的资源,或者是一个原始指南指向它,如果nullptr是有效参数的话。像const shared_ptr<T>& 或者const unique_ptr<T>&这样的参数类型是没有什么道理的。

5、函数返回值

        标准智能指针shared_ptr,unique_ptr,以及weak_ptr通过值可以容易且有效地从函数返回,得益于强制与非强制的拷贝省略以及移动的语法。移动的语法目前并不重要。重要的是这些方式从函数中返回一个智能指针都是高效的。例如,可以写出如下的create()函数,在main()中如示例使用:

import std;
using namespace std;

class Simple
{
public:
	Simple() { println("Simple constructor called!"); }
	~Simple() { println("Simple destructor called!"); }
};

unique_ptr<Simple> create()
{
	auto ptr{ make_unique<Simple>() };
	// Do something with ptr...
	return ptr;
}

int main()
{
	unique_ptr<Simple> mySmartPtr1{ create() };
	auto mySmartPtr2{ create() };
}

6、enable_shared_from_this

        从std::enable_shared_from_this继承一个类允许成员函数被在一个安全返回shared_ptr或者weak_ptr给自身的对象调用。没有这个基类,一种方式就是返回一个有效的shared_ptr或者weak_ptr给this,通过给类加一个weak_ptr作为成员,返回其复本或者返回由其构造的shared_ptr。enable_shared_from_this类添加了如下的两个成员函数给其继承的类:

  • shared_from_this():返回一个共享对象属主的shared_ptr。
  • weak_from_this():返回一个跟踪对象属主的weak_ptr。

        这个高级属性我们不进行详细讨论,但是下面的代码展示了其用途。share_from_this()与weak_from_this()为公共成员函数。然而,你可能会发现from_this部分在公共接口时有点令人搞不懂,只是作为一个展示,下面的Foo类定义了它自身的成员函数getPointer():

import std;
using namespace std;

class Foo : public enable_shared_from_this<Foo>
{
public:
	shared_ptr<Foo> getPointer() {
		return shared_from_this();
	}
};

int main()
{
	auto ptr1{ make_shared<Foo>() };
	auto ptr2{ ptr1->getPointer() };
}

        注意:只有在指针已经被保存在shared_ptr时才可以在对象上使用shared_from_this();否则的话,就会抛出bad_weak_ptr的例外。在上面的例子中,make_shared()在main()中用于生成一个叫做ptr1的shared_ptr,它包含一个Foo的实例。shared_ptr生成之后,允许在Foo实例上调用shared_from_this()。另一方面,调用weak_from_this()总是可以的,只是如果在一个没有存储在shared_ptr的指针上的对象进行调用就可能会返回空的weak_ptr。

下面的代码是对getPointer()成员函数的彻头彻尾的错误应用:

class Foo
{
public:
    shared_ptr<Foo> getPointer() {
        return shared_ptr<Foo>(this);
    }
};

        如果像刚展示的那样在main()中使用同样的代码,对Foo的应用就会产生双重删除。两个完全独立的shared_ptr(ptr1与ptr2)指向了同一个对象,都会在其不在活动范围时尝试去删除该对象。

7、智能指针与C风格的函数的互操作性(c++23特性)

        通常情况下,C风格的函数使用返回类型来显示函数是否正确执行或者是否有错误。既然返回类型早就用于报告错误,那另外的输出参数就用于函数返回其他的数据。例如:

using errorcode = int;

errorcode my_alloc(int value, int** data)
{
	*data = new int{ value };
	println("Allocated");
	return 0;
}

errorcode my_free(int* data)
{
	delete data;
	println("Freed");
	return 0;
}

        这种C风格的API,my_alloc()函数返回了一个errorcode,在输出的data参数中返回了分配的数据。在c++23之前,不能在my_alloc()中直接使用智能指针,比如unique_ptr。那你可以这样做:

		unique_ptr<int, decltype(&my_free)> myIntSmartPtr(nullptr, my_free);
		int* data{ nullptr };
		my_alloc(42, &data);
		myIntSmartPtr.reset(data);

        这个也比较容易吧。c++23引入了std::out_ptr与inout_ptr()函数来帮助做这些,定义在<memory>中,使用该函数,上面的代码片断就可以写得更优雅:

		unique_ptr<int, decltype(&my_free)> myIntSmartPtr(nullptr, my_free);
		my_alloc(42, inout_ptr(myIntSmartPtr));

        如果你确信传递给inout_ptr的指针为nullptr,也可以使用out_ptr。

8、旧的已经移除的auto_ptr

        旧的,c++11之前的标准库包含了一个智能指针的基本实现,叫做auto_ptr。很不幸,auto_ptr有很严重的缺点。其中一个就是当在比如vector这样的标准库构造函数中使用时不能正确工作。c++官方废除了auto_ptr,从c++17开始,从标准库中完全移除掉了。它被unique_ptr与shared_ptr替换掉了。这里提到auto_ptr是为了确认你了解这一点并且 确认不会用到它。

        不要使用旧的auto_ptr智能指针!要使用unique_ptr,如果需要共享属主就使用shared_ptr。

  • 16
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

王俊山IT

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

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

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

打赏作者

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

抵扣说明:

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

余额充值