std::unique_ptr 不是零成本

Posted

技术标签:

【中文标题】std::unique_ptr 不是零成本【英文标题】:std::unique_ptr is NOT zero cost 【发布时间】:2019-11-04 14:56:52 【问题描述】:

我已经设置好了,类似这样:

有类似于vector的类(使用std::vector实现)。

它包含指向 int 的指针。

我正在使用我自己的自定义分配器。

vector不会创建元素,但可以销毁元素。

为了销毁它需要调用非静态方法Allocator::deallocate(int *p)

如果我通过手动实时管理来完成,我可以手动调用Allocator::deallocate(int *p)。这有效,但不是 RAII。

或者,我可以将std::unique_ptr 与自定义删除器一起使用。但是,如果这样做,数组的大小会变成两倍,因为每个 std::unique_ptr 都必须包含指向分配器的指针。

有什么方法可以在不将向量大小加倍的情况下做到这一点?

请注意,我不想将课程模板化。

这是我想出的最好的 RAII 代码。

#include <functional>
#include <cstdlib>
#include <memory>



struct MallocAllocator
    template<class T>
    static T *allocate(size_t size = sizeof(T) ) noexcept
        return reinterpret_cast<T *>( malloc(size) );
    

    // this is deliberately not static method
    void deallocate(void *p) noexcept
        return ::free(p);
    

    // this is deliberately not static method
    auto getDeallocate() noexcept
        return [this](void *p)
            deallocate(p);
        ;
    
;



struct S
    std::function<void(void *)> fn;

    S(std::function<void(void *)> fn) : fn(fn)

    auto operator()() const
        auto f = [this](void *p)
            fn(p);
        ;

        return std::unique_ptr<int, decltype(f)> (int *) malloc(sizeof(int)), f ;
    
;



int main()
    MallocAllocator m;

    S s m.getDeallocate() ;

    auto x = s();

    printf("%zu\n", sizeof(x));

【问题讨论】:

你应该添加一些关于你正在尝试的原始问题的细节......你已经把问题逼入绝境,以至于要保持在你设定的范围内,不可能解决“大小”问题。 从某种意义上说,如果您试图自己模仿由此产生的行为,您将不得不这样做,这就是零成本。 @RinatVeliakhmedov 这不是真的,因为如果每个元素的删除器都是相同的,那么您可以包装容器并且只存储必要的机器一次。 这让我想知道将自定义分配器传递给 vector 是否是一个值得追求的目标。但是分配器不是我的强项。我也不确定向量适合给定示例的位置。 "如果我使用手动实时管理来执行此操作,我可以手动调用 Allocator::deallocate(int *p)。这可行,但不是 RAII。" 确定是。 RAII 是关于您的班级与外界的接口你的班级发生的事情并不一定是 RAII。重要的是你的类的用户可以期望内部资源的生命周期被限定为外部资源的生命周期。 【参考方案1】:

你做不到。如果您希望您的 unique_ptr 存储对非静态删除器的引用,则您无能为力,它必须将其存储在某个地方。

解决此问题的一些方法:

    如果您使用的是分配器感知数据结构,请将分配器传递给它,不要使用 unique_ptr 的,使用实际数据类型作为存储类型。 将分配的对象包装在某种管理器周围,该管理器会在需要时解除分配这些对象。您在其中丢失了 RAII,但对于外部代码,它仍然是 RAII。您甚至可以将某些对象的所有权从该管理器转移到外部代码,您只需要在那里使用自定义删除器。 (不推荐)使用一些可以从删除器访问的全局状态使其大小为 0。

【讨论】:

以上是关于std::unique_ptr 不是零成本的主要内容,如果未能解决你的问题,请参考以下文章

指向 std::unique_ptr 的内容

为啥 std::unique_lock 改变 std::unique_ptr?

为啥 std::unique_ptr 重置与赋值不同?

一些 std::unique_ptr 使用和“陷阱”

与 std::unique_ptr 相关的错误

`std::optional` 比 `std::shared_ptr` 和 `std::unique_ptr` 有啥优势?