Is this C++ structure initialization trick safe?

前端 未结 16 1071
有刺的猬
有刺的猬 2020-12-23 17:56

Instead of having to remember to initialize a simple \'C\' structure, I might derive from it and zero it in the constructor like this:

struct MY_STRUCT
{
            


        
相关标签:
16条回答
  • 2020-12-23 18:30

    This is a perfect example of porting a C idiom to C++ (and why it might not always work...)

    The problem you will have with using memset is that in C++, a struct and a class are exactly the same thing except that by default, a struct has public visibility and a class has private visibility.

    Thus, what if later on, some well meaning programmer changes MY_STRUCT like so:

    
    struct MY_STRUCT
    {
        int n1;
        int n2;
    
       // Provide a default implementation...
       virtual int add() {return n1 + n2;}  
    };
    

    By adding that single function, your memset might now cause havoc. There is a detailed discussion in comp.lang.c+

    0 讨论(0)
  • 2020-12-23 18:30

    My opinion is no. I'm not sure what it gains either.

    As your definition of CMyStruct changes and you add/delete members, this can lead to bugs. Easily.

    Create a constructor for CMyStruct that takes a MyStruct has a parameter.

    CMyStruct::CMyStruct(MyStruct &)
    

    Or something of that sought. You can then initialize a public or private 'MyStruct' member.

    0 讨论(0)
  • 2020-12-23 18:31

    PREAMBLE:

    While my answer is still Ok, I find litb's answer quite superior to mine because:

    1. It teaches me a trick that I did not know (litb's answers usually have this effect, but this is the first time I write it down)
    2. It answers exactly the question (that is, initializing the original struct's part to zero)

    So please, consider litb's answer before mine. In fact, I suggest the question's author to consider litb's answer as the right one.

    Original answer

    Putting a true object (i.e. std::string) etc. inside will break, because the true object will be initialized before the memset, and then, overwritten by zeroes.

    Using the initialization list doesn't work for g++ (I'm surprised...). Initialize it instead in the CMyStruct constructor body. It will be C++ friendly:

    class CMyStruct : public MY_STRUCT
    {
    public:
        CMyStruct() { n1 = 0 ; n2 = 0 ; }
    };
    

    P.S.: I assumed you did have no control over MY_STRUCT, of course. With control, you would have added the constructor directly inside MY_STRUCT and forgotten about inheritance. Note that you can add non-virtual methods to a C-like struct, and still have it behave as a struct.

    EDIT: Added missing parenthesis, after Lou Franco's comment. Thanks!

    EDIT 2 : I tried the code on g++, and for some reason, using the initialization list does not work. I corrected the code using the body constructor. The solution is still valid, though.

    Please reevaluate my post, as the original code was changed (see changelog for more info).

    EDIT 3 : After reading Rob's comment, I guess he has a point worthy of discussion: "Agreed, but this could be an enormous Win32 structure which may change with a new SDK, so a memset is future proof."

    I disagree: Knowing Microsoft, it won't change because of their need for perfect backward compatibility. They will create instead an extended MY_STRUCTEx struct with the same initial layout as MY_STRUCT, with additionnal members at the end, and recognizable through a "size" member variable like the struct used for a RegisterWindow, IIRC.

    So the only valid point remaining from Rob's comment is the "enormous" struct. In this case, perhaps a memset is more convenient, but you will have to make MY_STRUCT a variable member of CMyStruct instead of inheriting from it.

    I see another hack, but I guess this would break because of possible struct alignment problem.

    EDIT 4: Please take a look at Frank Krueger's solution. I can't promise it's portable (I guess it is), but it is still interesting from a technical viewpoint because it shows one case where, in C++, the "this" pointer "address" moves from its base class to its inherited class.

    0 讨论(0)
  • 2020-12-23 18:32

    From an ISO C++ viewpoint, there are two issues:

    (1) Is the object a POD? The acronym stands for Plain Old Data, and the standard enumerates what you can't have in a POD (Wikipedia has a good summary). If it's not a POD, you can't memset it.

    (2) Are there members for which all-bits-zero is invalid ? On Windows and Unix, the NULL pointer is all bits zero; it need not be. Floating point 0 has all bits zero in IEEE754, which is quite common, and on x86.

    Frank Kruegers tip addresses your concerns by restricting the memset to the POD base of the non-POD class.

    0 讨论(0)
  • 2020-12-23 18:32

    Comment on litb's answer (seems I'm not yet allowed to comment directly):

    Even with this nice C++-style solution you have to be very careful that you don't apply this naively to a struct containing a non-POD member.

    Some compilers then don't initialize correctly anymore.

    See this answer to a similar question. I personally had the bad experience on VC2008 with an additional std::string.

    0 讨论(0)
  • 2020-12-23 18:37

    Much better than a memset, you can use this little trick instead:

    MY_STRUCT foo = { 0 };
    

    This will initialize all members to 0 (or their default value iirc), no need to specifiy a value for each.

    0 讨论(0)
提交回复
热议问题