gpt4 book ai didi

c++ - 如何避免每次构造或重置时都需要为 std::shared_ptr 指定删除器?

转载 作者:可可西里 更新时间:2023-11-01 18:27:57 25 4
gpt4 key购买 nike

std::unique_ptr有两个模板参数,第二个是要使用的删除器。由于这一事实,我们可以很容易地为 unique_ptr 添加别名。到需要自定义删除器的类型(例如 SDL_Texture ),方式如下:

using SDL_TexturePtr = unique_ptr<SDL_Texture, SDL2PtrDeleter>;

...哪里SDL2PtrDeleter是一个用作删除器的仿函数。

有了这个别名,程序员就可以构造和重置 SDL_TexturePtr不关心甚至不知道自定义删除器:

SDL_TexturePtr ptexture(SDL_CreateTexture(/*args*/));

//...

ptexture.reset(SDL_CreateTexture(/*args*/));

std::shared_ptr ,另一方面,没有模板参数,这将允许将删除器指定为类型的一部分,因此以下是非法的:

// error: wrong number of template arguments (2, should be 1)
using SDL_TextureSharedPtr = shared_ptr<SDL_Texture, SDL2PtrDeleter>;

因此,最好的类型别名是:

using SDL_TextureSharedPtr = shared_ptr<SDL_Texture>;

但这与使用 shared_ptr<SDL_Texture> 相比没有什么优势明确地,因为用户必须知道要使用的删除函数并在每次构造或重置 SDL_TextureSharedPtr 时指定它无论如何:

SDL_TextureSharedPtr ptexture(SDL_CreateTexture(/*args*/), SDL_DestroyTexture);

//...

ptexture.reset(SDL_CreateTexture(/*args*/), SDL_DestroyTexture);

从上面的例子可以看出,用户需要知道删除SDL_Texture的正确函数。 (即 SDL_DestroyTexture() )并每次都传递一个指向它的指针。除了不方便之外,这还导致程序员通过将不正确的函数指定为删除器而引入错误的可能性很小。


我想以某种方式将删除器封装在共享指针本身的类型中。据我所知,由于无法仅通过使用类型别名来实现这一点,因此我考虑了 3 个选项:

  1. 创建一个类,包装std::shared_ptr<T> , 这将复制 shared_ptr 的界面但允许通过它自己的模板参数指定删除仿函数。然后这个包装器将提供一个指向其删除器实例的 operator() 的指针。调用构造函数或 reset() 时其底层方法std::shared_ptr<T>来自其自己的构造函数或 reset() 的实例方法,分别。当然,缺点是 std::shared_ptr 的整个相当大的界面。必须在这个 WET 包装类中复制。

  2. 创建 std::shared_ptr<T> 的子类,这将允许通过它自己的模板参数指定删除仿函数。这将假设 public继承,帮助我们避免重复shared_ptr的界面,但会打开它自己的一 jar 蠕虫。尽管std::shared_ptr不是 final ,它似乎并没有被设计成子类化,因为它有一个非虚析构函数(尽管在这种特殊情况下这不是问题)。更糟糕的是,reset() shared_ptr 中的方法不是虚拟的,因此不能被覆盖 - 仅被遮蔽,这为不正确的使用打开了大门:使用 public继承,用户可能会将对我们子类实例的引用传递给某些 API,接受 std::shared_ptr<T>& ,其实现可能会调用 reset() ,完全绕过我们的方法。通过非公共(public)继承,我们得到与选项 #1 相同的结果。

对于以上两个选项,最后,SDL_TextureSharedPtr可以表示如下,假设MySharedPtr<T, Deleter>是我们的(子)类:

using SDL_TextureSharedPtr = MySharedPtr<SDL_Texture, SDL2PtrDeleter>;
  1. 第三个​​选项曾经在这里,它涉及特化 std::default_delete<T> .这是基于我错误的假设 std::shared_ptr<T>使用 std::default_delete<T> , 比如 unique_ptr会,如果没有明确提供删除器。 不是这种情况。感谢@DieterLücking指出这一点!

鉴于这些选项和上述推理,这是我的问题。

我是否错过了一种更简单的方法来避免必须为 std::shared_ptr<T> 指定删除器?每次构造它的实例或reset() ?

如果不是,我列出的选项的推理是否正确?是否有其他客观原因使您更喜欢这些选项中的一个而不是另一个?

最佳答案

using SDL_TexturePtr = unique_ptr<SDL_Texture, SDL2PtrDeleter>;

Given this alias, programmers are able to construct and reset SDL_TexturePtr without caring or even knowing about custom deleter:

好吧,这是(通常是致命的)过度简化。而是当且仅当默认构造的删除器适合构造,分别删除器的当前值适合重置指针,而不需要手动更改。

关于包装或扩展 shared_ptr 的缺点,您是对的,尽管有些人可能会说它允许您添加新的实例方法。
不过,您应该尽量减少耦合,这意味着更喜欢自由函数,因为您只需要现有的公共(public)接口(interface)即可编写它们。

如果不指定删除器将导致使用 std::default_delete(不幸的是它没有),并且每种类型只需要一个删除器,或者标准的 delete-expression 将适合您的使用-case(似乎不是),第三个选项是您可以选择的最佳选择。

因此,一个不同的选择:使用构造函数抽象出(可能复杂的)构造和自定义删除器。
这样你只能写一次,自由使用 auto 可以进一步减少你的头痛。

关于c++ - 如何避免每次构造或重置时都需要为 std::shared_ptr 指定删除器?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37907758/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com