在 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
在定义中。但是“在构造函数体执行之前”是它真正发生的时候。
我想知道如何以“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
在定义中。但是“在构造函数体执行之前”是它真正发生的时候。