【问题标题】:Smart pointers with addrinfo struct带有 addrinfo 结构的智能指针
【发布时间】:2014-03-23 10:20:36
【问题描述】:

我需要处理两个 struct addrinfo 指针。由于我使用 C++(11) 进行编码,因此我必须使我的代码异常安全。事实上,我的 costructors 可能会抛出一个runtime_error。 当您不再需要这种结构时,您应该调用freeaddrinfo 以释放结构内的列表。请考虑以下代码:

#include <memory>
#include <netdb.h>

class SomeOtherClass
{
  public:
    SomeOtherClass() : hints(new addrinfo), result(new addrinfo) { /*stuff*/ }
    ~SomeOtherClass() { freeaddrinfo(result.get()); } // bad things will happen

private:
    std::unique_ptr<addrinfo> hints, result;
};

class MyClass : public SomeOtherClass
{
public:
    MyClass() { /* hints initialization, call to getaddrinfo, etc. */ }

private:
    // ...
};

我的问题是:

  1. addrinfo 是一个“旧”的 C 结构,没有要调用的 ctor/dtor:使用新的安全吗?
  2. getaddrinfo 需要一个指向 addrinfo 结构的指针的指针:我应该如何通过智能指针传递它?
  3. 打电话给freeaddrinfo怎么样?删除(或者更好的free)智能指针持有的指针被认为是不安全的。

hints 没有问题,因为它的生命周期更短。

【问题讨论】:

  • 你自己并没有真正分配结果地址信息结构,是吗?它们由getaddrinfo 函数分配。并且可以为智能指针设置自定义删除器,可以调用freeaddrinfo。并且提示不必动态分配,即使您要多次使用相同的结构。只需在普通(非指针)结构变量上使用地址运算符即可。
  • 这个构造函数是not safe,如果你的编译器支持它,你应该使用std::make_unique,或者如果它不支持,你自己做一个等效的。您真的真的不想处理部分构造的内存泄漏。此外,如果你的基类要保存资源,你的基类析构函数应该是virtual
  • @Mgetz 这只是一个让你找出问题所在的 sn-p。但是,我找不到virtual dtor 的位置,因为基类不是抽象类。
  • @black 声明析构函数virtual 并不意味着纯虚拟,只是以virtual 关键字为前缀,这样如果有人同时持有基类指针,则基类和派生类析构函数被调用
  • @Mgetz 这对 ctor 来说是安全的,因为成员按照声明的顺序进行初始化,其余成员可以使用

标签: sockets c++11 smart-pointers


【解决方案1】:

对于您自己分配的任何addrinfo,使用newdelete 是安全的,因此您可以使用unique_ptr 的默认实现来处理。

对于getaddrinfo() 分配的任何addrinfo,您必须使用freeaddrinfo() 来释放它。您仍然可以为此使用unique_ptr,但您必须将freeaddrinfo() 指定为自定义Deleter,例如:

class SomeOtherClass
{
  public:
    SomeOtherClass() : hints(new addrinfo), result(nullptr, &freeaddrinfo) { /*stuff*/ }

private:
    std::unique_ptr<addrinfo> hints;
    std::unique_ptr<addrinfo, void(__stdcall*)(addrinfo*)> result;
};

那么你可以这样做:

getaddrinfo(..., &result);

或者这样,如果std::unique_ptr 没有覆盖&amp; 运算符:

addrinfo *temp;
getaddrinfo(..., &temp);
result.reset(temp);

更新:更好的选择是使用decltype 并让编译器为您推断Deleter 的函数类型:

std::unique_ptr<addrinfo, decltype(&freeaddrinfo)> result;

【讨论】:

  • 这是不安全的,有部分建设的风险,请参阅我对 OP 的评论中的链接。
  • AFAIK make_unique 不允许您指定自定义 Deleter
  • 就我个人而言,我不会在一开始就在堆上分配hints,所以我只会让它成为类的本地成员而根本没有动态分配。那么就没有部分构造的机会(在这个例子中,无论如何)。您无法选择result 的分配方式。
  • @RemyLebeau 看来你是对的......在这种情况下,唯一安全的异常选择是委托给默认构造函数以使对象脱离构造阶段,因此将调用析构函数.
  • “您无法选择如何分配结果”是什么意思?
【解决方案2】:

除其他答案外:C++23 引入了智能指针与 C 样式输出参数交互的新功能。现在,我们可以使用std::out_ptr(),而不是用临时值替换托管对象:

getaddrinfo(..., std::out_ptr(result));

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-03-01
    • 1970-01-01
    • 2019-02-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-23
    相关资源
    最近更新 更多