在 C++ 中正确初始化一个结构,特别是 addrinfo

Properly initialise a struct in C++, specifically addrinfo

我想知道如何以“C++ 方式”正确初始化结构。

我正在用我的提示的相关信息填充 addrinfo-结构(用于 getaddrinfo())。

根据我初始化结构的方式,稍后我将能够获取所述地址信息并让网络事物执行其网络操作或因“未知错误”而失败。

我从中获取代码的教程(据我所知是用 C 编写的)使用了这个:

struct addrinfo hints;
memset(&hints, 0, sizeof hints);

这似乎是用全零初始化名称“hints”结构的有效方法。它使我的程序运行。

虽然看起来 memset(如果应用不正确)是邪恶的,但在本例中我们正在填充一个“POD”对象,所以我们应该是安全的。 ()

我尝试的替代方法是使用初始化程序(您可以将其称为构造函数吗?),如以下答案中所建议的:

struct addrinfo hints;
hints = addrinfo{};

(我知道我可以在声明时通过初始化来缩写两行,但是声明在我的基础 class 的头文件中,而初始化发生在每个继承 class 中)

这会产生错误(可能来自 getaddrinfo)。

因为我唯一改变的是 hints 的初始化,底层机制似乎是罪魁祸首。任何人都可以阐明这里发生了什么吗?

在 C++ 中,初始化堆栈。 class 成员和所有基 classes 在程序进入构造函数体之前被初始化,并将调用适当的构造函数来执行此操作。之后的一切,包括在构造函数体内,都是赋值。要初始化成员,您需要在 class 的构造函数中使用 default member initializer or a Member Initializer List

这对这个问题并不重要,但在成员初始化器列表中显式初始化通常优于默认初始化,然后在构造函数主体中赋值。如果默认初始化很昂贵,您将不想执行它,然后在以后通过赋值重复很多工作。

addrinfo 是一个 C 结构,我们以前称之为 Plain Old Data type,它们非常简单。它们不需要专门的初始化,也没有构造函数,它们包含的任何东西也没有。所以没有进行初始化。它们的初始值是未定义的,并且倾向于反映 addrinfo 现在所在的内存位置中发生的任何事情,基本上是垃圾。正如提问者所发现的,这个“垃圾”是有害的,需要清除。

简单代码示例:

class info_base
{
protected:
    addrinfo info = addrinfo(); // default member initializer
                                // will fall back on zero initialization in this case
};
class info_tcp:public info_base
{
public:
    info_tcp() // initialization includes default initialization of info_base
               // which will use the default member initializer
    {
        info.ai_socktype = SOCK_STREAM;
        info.ai_protocol = IPPROTO_TCP;
    }
};

如此简单的优点之一是它可以 aggregate initialized 并且从 C++20 开始,我们可以做这样简单的事情(请注意,这没有明显的性能优势——到目前为止我明白了——但知道你可以做到这一点以后会派上用场)

class info_base
{
public: // note public now. Necessary to be an aggregate
    addrinfo info = addrinfo();
};
class info_tcp:public info_base
{
public:
    info_tcp(): info_base({.ai_socktype = SOCK_STREAM, 
                           .ai_protocol = IPPROTO_TCP})
                           // Aggregate initialization of info_base via member 
                           // Initializer List taking advantage of designated 
                           // initializers added in C++20
    {
    }
};

因为 info_base 及其 info 成员被显式初始化,默认成员初始化程序被跳过。我不太喜欢这个,因为它制作了 info public,现在任何 shmuck 都可以弄乱它,但它真的很简单。

class info_base
{
protected:
    addrinfo info;
    info_base(int type,
              int protocol): info {.ai_socktype = SOCK_STREAM, 
                                   .ai_protocol = IPPROTO_TCP}
    {

    }
};
class info_tcp:public info_base
{
public:
    info_tcp(): info_base(SOCK_STREAM, IPPROTO_TCP)
    {
    }
};

现在info_base没有默认的构造函数,如果你喜欢可以加回去,但是调用者可以指定重要的参数用来初始化info而且没有人在外面家人可以与 addr.

互动

您只是在寻找 hints = {}; 不需要重复类型名称。如果你真的想要,那么 hints = (struct addrinfo){}; 也可以。

这两个例子都是赋值,不是初始化。初始化仅在对象创建期间发生,这意味着

(a) 在 non-static 数据成员的构造函数主体之前或

(b) 定义的一部分,对于所有其他变量或

(c) new-expression 的一部分,对于 dynamically-allocated 个对象


请注意,non-static 数据成员的初始化表达式可以在 ctor-initializer-list 中显式列出,或者由于存在brace-or-equal-initializer 在定义中。但是“在构造函数体执行之前”是它真正发生的时候。