C++ std::需要免费的C函数的唯一\u ptr

C++ std::需要免费的C函数的唯一\u ptr,c++,c++11,C++,C++11,考虑一个C函数,它返回的东西必须是免费的d,例如POSIX的strdup()。我想在C++11中使用该函数并避免任何泄漏的机会,这是正确的方法吗 #include <memory> #include <iostream> #include <string.h> int main() { char const* t { "Hi stackoverflow!" }; std::unique_ptr<char, void(*)(void*)&

考虑一个C函数,它返回的东西必须是
免费的
d,例如POSIX的
strdup()
。我想在C++11中使用该函数并避免任何泄漏的机会,这是正确的方法吗

#include <memory>
#include <iostream>
#include <string.h>

int main() {
    char const* t { "Hi stackoverflow!" };
    std::unique_ptr<char, void(*)(void*)>
        t_copy { strdup(t), std::free };

    std::cout << t_copy.get() << " <- this is the copy!" <<std::endl;
}
#包括
#包括
#包括
int main(){
char const*t{“Hi stackoverflow!”};
std::unique\u ptr
t_copy{strdup(t),std::free};

std::cout您所拥有的极有可能在实践中发挥作用,但并非严格正确。您可以通过以下方式使其更有可能发挥作用:

std::unique_ptr<char, decltype(std::free) *>
    t_copy { strdup(t), std::free };
std::unique\u ptr
t_copy{strdup(t),std::free};
原因是
std::free
的函数类型不能保证为
void(void*)。当通过<代码> Value*/Cuff>时,它是可调用的,在这种情况下返回“代码>空格”,但至少有两种函数类型符合该规范:一个具有C链接,一个具有C++链接。大多数编译器不注意这一点,但是为了正确,您应该避免对它做出假设。p>

但是,即使是这样,也不是严格的。正如C++中的PTESOSOM指出的那样,C++允许实现例如“代码> STD::/FoeX>一个重载函数,在这种情况下,您和我对<代码> STD::免费< /C> >的使用将是模糊的。这不是对 STD::Field的许可,它是一个PiMIS。几乎所有标准库函数都可以使用sion。为了避免这个问题,需要一个自定义函数或函子(如他的答案所示)

假设它是有意义的,那么有可能对非指针使用类似的模式吗

不使用
unique\u ptr
,这实际上是针对指针的。但是您可以创建自己的类,类似于
unique\u ptr
,但不需要对要包装的对象进行假设

假设这是有意义的,那么可以使用类似的模式 非指针?例如,返回的POSIX函数open 整数

当然,使用Howard的Hinnant,我们可以看到一个激励性的例子:

// For open
#include <sys/stat.h>
#include <fcntl.h>

// For close
#include <unistd.h>

// For unique_ptr
#include <memory>

int main()
{
    auto handle_deleter = [] (int* handle) {
        close(*handle);
    };

    int handle = open("main.cpp", O_RDONLY);
    std::unique_ptr<int, decltype(handle_deleter)> uptr
        { &handle, handle_deleter };
}
//用于打开
#包括
#包括
//接近
#包括
//用于唯一的\u ptr
#包括
int main()
{
自动句柄\u deleter=[](int*handle){
关闭(*手柄);
};
int handle=open(“main.cpp”,仅限ordu);
std::唯一的ptr uptr
{&handle,handle\u deleter};
}
或者,您可以使用函子而不是lambda:

struct close_handler
{
    void operator()(int* handle) const
    {
        close(*handle);
    }
};

int main()
{
    int handle = open("main.cpp", O_RDONLY);
    std::unique_ptr<int, close_handler> uptr
        { &handle };
}
struct close\u处理程序
{
void运算符()(int*句柄)常量
{
关闭(*手柄);
}
};
int main()
{
int handle=open(“main.cpp”,仅限ordu);
std::唯一的ptr uptr
{&handle};
}
如果我们使用typedef和“factory”函数,这个例子可以进一步简化

using handle = int;
using handle_ptr = std::unique_ptr<handle, close_handler>;

template <typename... T>
handle_ptr get_file_handle(T&&... args)
{
    return handle_ptr(new handle{open(std::forward<T>(args)...)});
}

int main()
{
    handle_ptr hp = get_file_handle("main.cpp", O_RDONLY);
}
使用handle=int;
使用handle\u ptr=std::unique\u ptr;
模板
句柄\u ptr获取文件\u句柄(T&&…args)
{
返回句柄(新句柄{open(std::forward(args)…)});
}
int main()
{
handle\u ptr hp=get\u file\u handle(“main.cpp”,O\RDONLY);
}
假设这是有意义的,那么可以使用类似的模式 非指针?例如,返回的POSIX函数open 整数

是的,可以这样做。您需要满足以下条件的“指针”类型:

请注意,我们在这里使用-1作为空句柄值,因为这是
open()
在失败时返回的值。对该代码进行模板化也很容易,这样它就可以接受其他句柄类型和/或无效值

然后

struct posix_close
{
使用指针=句柄\包装器;
void运算符()(指针fd)常量
{
关闭(*fd);
}
};
int
main()
{
std::unique_ptr p(handle_wrapper(打开(“测试”),O_CREAT));
int fd=*p.get();
}
.

原始问题(和)引入了每指针开销,因此这样的
唯一\u ptr
的大小是使用
std::make_unique
导出的大小的两倍。此外,我将直接制定decltype:

std::unique_ptr<char, decltype(&std::free)>
    t_copy { strdup(t), &std::free };
现在的例子是

unique_C_ptr<char const> t_copy { strdup(t) };
unique_C_ptr t_copy{strdup(t)};

<>我建议在C++核心指导支持库中(可能有更好的命名),可以使用该机制,这样就可以用于转换到现代C++的代码库(成功打开)。真正强调你想要使用的删除器,我喜欢它,也许它值得再问一个问题。然而,
extern“C”{void f(void*)}
void f(void*)的类型是什么
不同?我认为区别只是符号名称。至少你可以使用相同类型的函数指针。@Paolo.Bolzoni:是的,它们是不同的类型,你不能(可移植地)使用相同的指针类型。@Paolo.Bolzoni一些实现确实抱怨
外部“C”{void f();}void(*fp)()=f;
带有一条关于不兼容指针类型赋值的消息。我想我记得读到HP的一个实现完全拒绝了它,但我无法验证这一点。Sun的(现在是Oracle的)是另一个关注它的实现,但它允许这种带有警告的隐式转换(我也看到了我自己)先生,你是对的。在我的系统中,代码> STD::ISSUME相同<代码>返回两个代码> true。DeCype < /Calp> S,但是我检查了标准,是的,不同的链接意味着不同的类型。总是有一些东西需要学习。@ CMASTER他们可以合法地有不同的调用约定,C约定的兼容性,C++约定A。更好的方法。为了避免
decltype
:在
extern“C”中创建typedefs
阻塞。但您会惊讶地发现它所需的资源是如此之少。因为您现在存储的是指向句柄的指针,而不是句柄本身,如果
句柄在
唯一\u ptr
之前被销毁,则对象的生存期可能会出现问题。您可以尝试使用
新句柄来解决此问题
std::unique_ptr<char, decltype(&std::free)>
    t_copy { strdup(t), &std::free };
struct free_deleter{
    template <typename T>
    void operator()(T *p) const {
        std::free(const_cast<std::remove_const_t<T>*>(p));
    }
};
template <typename T>
using unique_C_ptr=std::unique_ptr<T,free_deleter>;
static_assert(sizeof(char *)==
              sizeof(unique_C_ptr<char>),""); // ensure no overhead
unique_C_ptr<char const> t_copy { strdup(t) };