Ter*_*ass 7 c++ shared-ptr unique-ptr c++11 c++14
std::unique_ptr有2个模板参数,第二个是要使用的删除器.由于这个事实,可以通过以下方式轻松地unique_ptr将a替换为类型,这需要自定义删除器(例如SDL_Texture):
using SDL_TexturePtr = unique_ptr<SDL_Texture, SDL2PtrDeleter>;
Run Code Online (Sandbox Code Playgroud)
...哪里SDL2PtrDeleter是一个用作删除器的仿函数.
鉴于这个别名,程序员能够构建和重置,SDL_TexturePtr而无需关心或甚至不知道自定义删除器:
SDL_TexturePtr ptexture(SDL_CreateTexture(/*args*/));
//...
ptexture.reset(SDL_CreateTexture(/*args*/));
Run Code Online (Sandbox Code Playgroud)
std::shared_ptr另一方面,没有模板参数,这将允许将删除器指定为类型的一部分,因此以下是非法的:
// error: wrong number of template arguments (2, should be 1)
using SDL_TextureSharedPtr = shared_ptr<SDL_Texture, SDL2PtrDeleter>;
Run Code Online (Sandbox Code Playgroud)
因此,对类型别名最好的方法是:
using SDL_TextureSharedPtr = shared_ptr<SDL_Texture>;
Run Code Online (Sandbox Code Playgroud)
但是与shared_ptr<SDL_Texture>明确使用相比,这几乎没有什么优势,因为用户必须知道要使用的删除函数,并在每次构造或重置时指定它SDL_TextureSharedPtr:
SDL_TextureSharedPtr ptexture(SDL_CreateTexture(/*args*/), SDL_DestroyTexture);
//...
ptexture.reset(SDL_CreateTexture(/*args*/), SDL_DestroyTexture);
Run Code Online (Sandbox Code Playgroud)
从上面的示例中可以看出,用户需要知道要删除的正确函数SDL_Texture(即SDL_DestroyTexture()),并且每次都将指针传递给它.除了不方便之外,这也会产生程序员通过将错误的函数指定为删除器而引入错误的可能性较小的概率.
我想以某种方式将删除器封装在共享指针本身的类型中.由于我无法通过使用类型别名来实现这一点,因此我考虑了3个选项:
创建一个包装类,std::shared_ptr<T>它将复制接口,shared_ptr但允许通过自己的模板参数指定删除器仿函数.然后,operator()当从其自己的构造函数或方法调用reset()其底层std::shared_ptr<T>实例的构造函数或reset()方法时,此包装器将提供指向其删除器实例的指针.当然,缺点是std::shared_ptr必须在这个包装类中重复整个相当大的接口,即WET.
创建一个子类std::shared_ptr<T>,允许通过自己的模板参数指定一个删除函子.假设public继承,这将帮助我们避免复制shared_ptr接口的需要,但会打开一堆自己的蠕虫.即使std::shared_ptr不是final,它似乎也没有被设计为子类,因为它具有非虚拟析构函数(尽管在这种特殊情况下这不是问题).更糟糕的是,reset()方法in shared_ptr不是虚拟的,所以不能被覆盖 - 只有阴影,这为不正确的使用打开了大门:通过public继承,用户可以将对我们子类的实例的引用传递给某个API,接受std::shared_ptr<T>&,其实现可能会调用reset(),完全绕过我们的方法.使用非公共继承,我们得到与选项#1相同的功能.
对于上述两个选项,最后SDL_TextureSharedPtr可以表示如下,假设MySharedPtr<T, Deleter>是我们的(子)类:
using SDL_TextureSharedPtr = MySharedPtr<SDL_Texture, SDL2PtrDeleter>;
Run Code Online (Sandbox Code Playgroud)
std::default_delete<T>.它是基于我是不正确的假设std::shared_ptr<T>使用std::default_delete<T>,如unique_ptr确实,如果没有缺失者已经作出明确规定.这是不是这种情况.感谢你@DieterLücking指出这一点!鉴于这些选项和上面的推理,这是我的问题.
我是否错过了一种更简单的方法来避免std::shared_ptr<T>每次构造实例时都必须指定删除器reset()?
如果没有,我的推理是否正确我列出的选项?还有其他客观原因偏好这些选择中的一种而不是另一种吗?
Run Code Online (Sandbox Code Playgroud)using SDL_TexturePtr = unique_ptr<SDL_Texture, SDL2PtrDeleter>;有了这个别名,程序员就可以构建和重置,而
SDL_TexturePtr无需关心甚至不知道自定义删除器:
嗯,这是一种(通常是致命的)过度简化。相反,如果默认构造的删除器适合构造,或者删除器的当前值适合重置指针,则不需要手动更改。
您对包装或扩展的缺点的看法是正确的shared_ptr,尽管有些人可能会说它允许您添加新的实例方法。
不过,您应该最大限度地减少耦合,这意味着更喜欢自由函数,因为您只需要现有的公共接口即可编写它们。
如果不指定删除器将导致使用std::default_delete(不幸的是没有),并且每种类型只需要一个删除器,或者标准删除表达式适合您的用例(它似乎不适合),第三个选项将是您可以选择的最佳选择。
因此,有一个不同的选择:使用构造函数来抽象(可能很复杂)构造和自定义删除器。
这样你就只能写一次,自由使用auto可以进一步减少你的头痛。
| 归档时间: |
|
| 查看次数: |
777 次 |
| 最近记录: |