唯一指针没有“空操作删除”

9

如何以最简洁的方式向unique_ptr传递一个什么都不做的自定义删除器?我需要为我正在编写的JNI函数提供支持,其中C++端期望一个unique_ptr,但是我不希望在退出JNI函数时删除unique_ptr所持有的对象 - 我稍后会处理删除。因此,我想做类似以下的事情:

std::unique_ptr<MyClass, noop_delete> ptr;

一行代码实现 - 不需要单独的函数定义 :-)

1
你基本上只需要传递一个空函数指针给它。 - Cory Kramer
7
“什么是自定义 deleter,它做的事情是什么?”那你为什么还需要使用std::unique_ptr呢? - 101010
1
你说过,“C++端期望一个unique_ptr”,这个是否在你的控制范围内?你能否更改C++端,使其不再期望一个unique_ptr - Sam Estep
@Jeffrey - 谢谢!所以我将不得不使用多行代码来实现这个。可惜。 - Frank
将'MyClass'包装在一些管理生命周期的'YourClass'中 - user2249683
显示剩余4条评论
3个回答

16
如@101010所指出,拥有一个没有删除器的非常奇怪,因为唯一有价值的东西实际上就是删除器。此外,你说“C++端期望一个unique_ptr”,但是一个不同删除器的将是另一种类型,这可能无法工作。
尽管如此,以下是实现的方法:
struct nop
{
    template <typename T>
    void operator() (T const &) const noexcept { }
};

template <typename T>
using nop_unique_ptr = std::unique_ptr<T, nop>;

请注意,这种nop类型可以在任何地方替代一个带有一个参数的函数对象作为无操作使用。

1
如果您需要相同类型,那么可以使用std::shared_ptr而无需删除。 - Gelldur
1
@lisyarus 我同意这很奇怪,但我确实有一个使用案例。在单元测试中,我需要模拟一个保存在unique ptr中的对象。我使用了fakeit框架,它可以仅根据ABC接口自动创建模拟对象。一旦fakeit创建了模拟对象,你就可以获得指向它的指针。问题是fakeit管理模拟对象的生命周期。当然,解决方法是使用一个带有no-op删除器的unique ptr。 - Loss Mentality
@lisyarus(续)否则,这个解决方案本来会更难,需要更多的#ifdef,但我们只需要几个。虽然这个解决方案从测试覆盖范围中删除了一些生产代码行,但是能够模拟指针所指向的内容将整个类带入了测试覆盖范围,这弥补了这一点。 - Loss Mentality
1
std::unique_ptr 具有“独一无二”的含义,同时您也不会因为错误而删除 std::unique_ptr。这两个属性可能是需要的,而不需要删除指向的对象。 - Fabio A.
2
当尝试管理对整个程序生命周期内保持活动状态的全局内存映射资源的访问时,这在嵌入式设备中非常有用。在这种情况下,具有noop删除器的unique_ptr具有访问所有权语义,模块能够明确地授予和收回对共享资源的访问权限。 - Liarokapis Alexandros
显示剩余4条评论

0

关于 std::unique_ptr::release 怎么样?就像这样

void JNIfunc (T*) {};
std::make_unique( new T) t;
JNIfunc( t.release);

0

在@lisyarus的回答评论中,我的回答促使我想出一个比我之前提供的更好的解决方案。这个解决方案涉及到@lisyarus已经提到的事实:一个no-op删除器unique_ptr与有一个delete删除器的unique_ptr是不同类型的。

我将其作为一个单独的答案发布,因为它可能与其他人相关(此外,这无法适应单个评论)。

上下文:对于单元测试,FakeIt模拟框架管理模拟对象的生命周期,因此当需要模拟通过unique_ptr指向的对象时,我们需要一个具有no-op删除器的unique_ptr。

// As in @lisyarus's answer...
struct nop
{
    template <typename T>
    void operator() (T const &) const noexcept { }
};

// NOTE: We have no use for a pointer that doesn't delete unless we're mocking, so 
// the types below are named to indicate that.

#ifndef WE_ARE_BUILDING_UNIT_TESTS
// Production build - we want a unique_ptr that deletes.
template <typename T>
using mockable_unique_ptr = std::unique_ptr<T>;
#else
// Unit test build - we want unique_ptr that doesn't delete.
template <typename T>
using mockable_unique_ptr = std::unique_ptr<T, nop>;
#endif

现在,mockable_unique_ptr将根据构建类型自动切换类型,这意味着您不必在代码中到处添加#ifdef。当然,仍然会有一些需要#ifdef / #else的地方,并且在单元测试版本中可能需要使用略微不同的代码(通常在指针初始化位置),但如果您的mock也在那里创建,您无论如何都需要这样做。不过,其余代码保持不变,因为unique_ptr的接口不会改变。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接