RAII, unique_ptr, and out parameters

RAII, unique_ptr, and out parameters

本文关键字:out parameters and unique RAII ptr      更新时间:2023-10-16

我是一名c#开发人员,正在学习c++ 11。我正在尝试使用wins .h.

查询DNS。

我从DnsQuery()开始,读到我需要用DnsRecordListFree()释放结果记录输出参数。c#的方法可能是使用try-finally块来确保无论如何都可以释放资源。

但是我了解到没有finally块,并且wind .h确实应该与时俱进并实现符合raii的接口(正如我所理解的典型建议)。而不是等待这种情况发生,我试图做一个RAII包装类,其析构函数调用DnsRecordListFree()和操作符重载强制转换,以获得原始指针。

但是我对如何适当地使用这个句柄或指针来获得一个输出参数感到困惑。当我研究的时候,我学会了unique_ptr,我已经学了一点,可以与自定义删除器一起使用。

这是我到目前为止的简单代码。可能有比这更多的错误,但我想我可以声明另一个PDNS_RECORD *presult,并使用它作为输出参数,然后复制或移动或以其他方式将其值分配到unique_ptr,但这听起来太麻烦了。

在我看来,unique_ptr的内部指针应该初始化为NULL,我应该以某种方式能够将指针的地址传递给out参数,DNSQuery将更新原始值,当unique_ptr在我的函数中超出范围时,DnsRecordListFree()调用将自动进行。我不知道的东西太多了,要找出正确的组合才能达到最低限度的正确/安全的使用。

#include <iostream>
#include <fstream>
#include <memory>
#include <Windows.h>
#include <WinDNS.h>
using namespace std;
auto pdnsDeleter = [&](PDNS_RECORD *ptr){ if (ptr) DnsRecordListFree(ptr); };
int main(int argc, char **argv)
{
    cout << "Hello Worldn";
    std::unique_ptr<PDNS_RECORD*, decltype(pdnsDeleter)> results(0, pdnsDeleter);
    if (DnsQuery(L"google.com", DNS_TYPE_A, DNS_QUERY_STANDARD, NULL, ??results??, NULL))
    {
        cout << "google.com -> " << ??results??;
    }
    cout << "Donen";
    getchar();
    return 0;
}

谢谢!

您可以花一整天的时间尝试适应标准的智能指针,或者您可以自己编写。这并不难做到,特别是如果你愿意欺骗并允许访问原始指针本身。

struct DnsRAII
{
    PDNS_RECORD p;
    DnsRAII() : p(NULL) { }
    ~DnsRAII() { if (p != NULL) DnsRecordListFree(p, DnsFreeRecordList); }
};
DnsRAII results;
if (DnsQuery(L"google.com", DNS_TYPE_A, DNS_QUERY_STANDARD, NULL, &results.p, NULL))
// ...

除非我错过了什么(我没有一个Windows盒子方便编译这个,所以原谅我,如果我是)你的代码是不正确的。就我个人而言,我不会用智能指针这样做,因为你基本上使用它是一个自定义的看门人类(你可以写一个很容易)。

无论如何,首先,你的删除器应该是:

auto pdnsDeleter = [&](PDNS_RECORD ptr){ if (ptr) DnsRecordListFree(ptr, DnsFreeRecordList); };
接下来,你的智能指针的类型应该是:
std::unique_ptr<DNS_RECORD, decltype(pdnsDeleter)> results;

最后,我相信你的加载这个智能指针应该在确定函数成功后:

PDNS_RECORD pdnsrec;
if (DnsQuery(L"google.com", DNS_TYPE_A, DNS_QUERY_STANDARD, NULL, &pdnsrec, NULL))
{
    results.reset(pdnsrec);
}

如果我得到的是正确的,你的删除将被正确地触发的范围退出在获取链。但是,对于您可以使用自己的janitor类有效地简化的工作来说,这似乎工作量太大了。