I am wondering how I can properly initialise a struct in a "C -way".
I am filling an addrinfo
-structure with the relevant information for my hints (to use in getaddrinfo()
).
Depending on how I initilise the struct, I will later on be able to get said addrinfo and let the network thing do its networky thing or fail with "unknown error".
The tutorial I took the code from (which was in C as far as I can tell) used this:
struct addrinfo hints;
memset(&hints, 0, sizeof hints);
This seems to be a valid way to initialise the struct of name "hints" with all zeroes. It makes my program work.
Although it appears that memset (if applied incorrectly) is evil, in this case we are filling a "POD"-object, so we should be safe. (https://stackoverflow.com/a/1976024/1968308)
The alternative I tried was using a initialiser (Can you call it a constructor?) as suggested in this answer: https://stackoverflow.com/a/54019987
struct addrinfo hints;
hints = addrinfo{};
(I am aware that I can abbreviate above two lines by initialising while declaring, but the declaration is in the header file for my base class whereas the initialisation happens in each inheriting class)
This produces an error (probably from getaddrinfo).
Since the only thing I changed was the initialisation of hints
, the underlying mechanism seems to be the culprit. Can anyone please shed light on what is going on here?
CodePudding user response:
In C the initialization stacks. A class member and all base classes are initialized before the program enters the body of the constructor and will call the appropriate constructors to do so. Everything afterward, including inside the constructor's body, is assignment. To initialize a member you need to use either a default member initializer or a Member Initializer List in the class's constructor.
It's not important to this question, but explicitly initializing in the member initializer list is often superior to default initialization followed by assignment in the constructor body. If the default initialization is expensive, you won't want to perform it and then repeat a lot of the work later with the assignment.
addrinfo
is a C structure, what we used to call a Plain Old Data type, and they are as simple as it gets. They require no specialized initialization and have no constructors and nothing they contain does either. So no initialization is performed. Their initial values are undefined and tend to reflect whatever happened to be in the memory location where the addrinfo
now sits, basically garbage. As the asker found, this "garbage" is detrimental and needs to be cleared.
Simple code example:
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;
}
};
One of the advantages of being so simple is it can be aggregate initialized and as of C 20 we can do simple stuff like this (Note there is no noticeable performance advantage to this--far as I can see--but knowing you can do this could come in handy later)
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
{
}
};
Because info_base
and its info
member are being explicitly initialized, the default member initializer is skipped. I don't much like this one because it made info
public
and now any shmuck can mess with it, but it is really simple.
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)
{
}
};
Now info_base
doesn't have a default constructor, you can add it back if you like, but the caller can specify the important parameters to use to initialize info
and no one outside of the family can interact with addr
.
CodePudding user response:
You are looking for simply hints = {};
No repetition of the type name is needed. If you really want to, then hints = (struct addrinfo){};
works fine as well.
Both of these examples are assignment, not initialization. Initialization only happens during object creation, that means either
(a) prior to the constructor body for non-static data members or
(b) part of the definition, for all other variables or
(c) part of the new-expression
, for dynamically-allocated objects
Note that the intialization expression for a non-static data member can be explicitly listed in the ctor-initializer-list
or implicitly done there due to the presence of a brace-or-equal-initializer
in the definition. But "before execution of the constructor body" is when it actually happens.