将unique_ptr用于原始指针的正确方法是什么?

时间:2016-03-05 13:41:22

标签: c++ unique-ptr

有两个用于分配和释放原始指针的函数,我想使用C ++轻松完成脏工作。我找到了两个让unique_ptr处理它的选项,它们对我来说都不合适:

char *raw_alloc();
void raw_free(char *ptr);

int main (int argc, char *argv[])
{
    //First option:
    {
        std::unique_ptr<char, decltype(&raw_free)> ptr (raw_alloc(), raw_free);
        printf ("size1: %lu\n", sizeof(ptr) / sizeof(char *));
    }

    //Second option:
    {
        struct deleter { void operator()(char *ptr) { raw_free(ptr); } };
        std::unique_ptr<char, deleter> ptr (raw_alloc());
        printf ("size2: %lu\n", sizeof(ptr) / sizeof(char *));
    }
}

输出表示第一个指针是第二个指针的两倍;自然地需要空间来保持指向释放功能的指针。

同时第二个选项要求我为我的类型创建存根删除器。当然,我可以编写一个功能模板,为我做这个:

template <typename T, void (*D)(T*)>
auto my_unique (T *ptr) {
    struct deleter { void operator()(T *ptr) { D(ptr); } };
    return unique_ptr<T,deleter>(ptr);
};

但为什么不能unique_ptr为我做这件事而只接受删除函数作为第二个模板参数?熟练的C ++人员如何处理原始指针?

3 个答案:

答案 0 :(得分:1)

我通常使用第二种方法,但我会将Deleter类保留在raw_allocraw_free声明附近的全局范围内。定义raw_unique_ptr简写:

可能会有所帮助
char *raw_alloc();
void raw_free(char *ptr);

struct raw_releter
{ void operator()(char * const ptr) const noexcept { raw_free(ptr); } };

using raw_unique_ptr = std::unique_ptr<char, raw_releter>;

答案 1 :(得分:0)

您可以使用重载的特定于类的new和delete运算符将原始指针包装到类中:

char* raw_alloc()
{
    // Don't allocate anything, just a demonstration of approach
    return (char*)"Hello, World!";
}

void raw_free(char* ptr)
{
    cout << ptr << endl;
}

class WrappedRawPointer
{
public:
    static void* operator new(std::size_t)
    {
        return raw_alloc();
    }

    static void operator delete(void* ptr, std::size_t)
    {
        raw_free((char*)ptr);
    }
};

int main() {
    // Will print "Hello, World!" twice: once for explicit
    // printing and once for deleter.
    auto ptr = unique_ptr<WrappedRawPointer>(new WrappedRawPointer());
    cout << (const char*) ptr.get() << endl;
    return 0;
}

我认为这种方法更好,因为它解除了分配/解除分配逻辑,但同时也没有造成任何重大的性能损失。

答案 2 :(得分:0)

默认情况下,

std::unique_ptr包含一个不同的实现,用于何时不包含删除操作,而不包含删除操作。由于带有deletor的版本将删除器存储为传递的模板类型,实际问题归结为从构造函数中推导出类模板参数。 (见this thread

正如您所提到的,第一个选项比第二个选项占用更多空间,但是我不会认为这是一个问题,因为编译器可以进行相当多的优化。

使用my_unique()的实现是一种减小std::unique_ptr大小的正确方法,因为它不会将ptr存储到函数中,但是它假设一个真正的函数指针,而它并没有使用lambdas作为删除者。 (这不是问题,尽管它是这种方法的限制)

另一种方法是使用std::shared_ptr,它不需要知道析构函数的外观,但是它总是存储删除器并且会占用大量内存,而不需要知道删除器的实际实现。

在我看来,当std::unique_ptr在本地时,我会使用类似于你的选项1或2的东西。如果这是API的一部分,那么选择一个实现会更加困难,而且所有三个选项(选项1,2或std :: shared_ptr)都很有趣。我会选择std::shared_ptr,因为您可以将它与其他具有自己删除功能的API结合使用。 (考虑一个工厂,它以JSON或XML转换数据,这两个函数都将char *清理为生成的流)

std::shared_ptr类似的不同方法是创建std::unique_ptr<T, std::function<void(T*)>>,其中包含std::function的性能开销,并且与原始选项相比,很可能是大量内存开销