C memset seems to not write to every member

后端 未结 7 2122
感动是毒
感动是毒 2021-01-18 09:01

I wrote a small coordinate class to handle both int and float coordinates.

template 
class vector2
{
public:
    vector2() { memset(this, 0, s         


        
相关标签:
7条回答
  • 2021-01-18 09:12

    As others are saying, memset() is not the right way to do this. There are some subtleties, however, about why not.

    First, your attempt to use memset() is only clearing sizeof(void *) bytes. For your sample case, that apparently is coincidentally the bytes occupied by the x member.

    The simple fix would be to write memset(this, 0, sizeof(*this)), which in this case would set both x and y.

    However, if your vector2 class has any virtual methods and the usual mechanism is used to represent them by your compiler, then that memset will destroy the vtable and break the instance by setting the vtable pointer to NULL. Which is bad.

    Another problem is that if the type T requires some constructor action more complex than just settings its bits to 0, then the constructors for the members are not called, but their effect is ruined by overwriting the content of the members with memset().

    The only correct action is to write your default constructor as

    vector2(): x(0), y(0), {}
    

    and to just forget about trying to use memset() for this at all.

    Edit: D.Shawley pointed out in a comment that the default constructors for x and y were actually called before the memset() in the original code as presented. While technically true, calling memset() overwrites the members, which is at best really, really bad form, and at worst invokes the demons of Undefined Behavior.

    As written, the vector2 class is POD, as long as the type T is also plain old data as would be the case if T were int or float.

    However, all it would take is for T to be some sort of bignum value class to cause problems that could be really hard to diagnose. If you were lucky, they would manifest early through access violations from dereferencing the NULL pointers created by memset(). But Lady Luck is a fickle mistress, and the more likely outcome is that some memory is leaked, and the application gets "shaky". Or more likely, "shakier".

    The OP asked in a comment on another answer "...Isn't there a way to make memset work?"

    The answer there is simply, "No."

    Having chosen the C++ language, and chosen to take full advantage of templates, you have to pay for those advantages by using the language correctly. It simply isn't correct to bypass the constructor (in the general case). While there are circumstances under which it is legal, safe, and sensible to call memset() in a C++ program, this just isn't one of them.

    0 讨论(0)
  • 2021-01-18 09:17

    No don't use memset -- it zeroes out the size of a pointer (4 bytes on my x86 Intel machine) bytes starting at the location pointed by this. This is a bad habit: you will also zero out virtual pointers and pointers to virtual bases when using memset with a complex class. Instead do:

    template <class T>
    class vector2
    {
    public:
        // use initializer lists
        vector2() : x(0), y(0) {}
        T x;
        T y;
    };
    
    0 讨论(0)
  • 2021-01-18 09:17

    The problem is this is a Pointer type, which is 4 bytes (on 32bit systems), and ints are 4 bytes (on 32bit systems). Try:

    sizeof(*this)
    

    Edit: Though I agree with others that initializer lists in the constructor are probably the correct solution here.

    0 讨论(0)
  • 2021-01-18 09:17

    Don't try to be smarter than the compiler. Use the initializer lists as intended by the language. The compiler knows how to efficiently initialize basic types.

    If you would try your memset hack on a class with virtual functions you would most likely overwrite the vtable ending up in a disaster. Don't use hack like that, they are a maintenance nightmare.

    0 讨论(0)
  • 2021-01-18 09:23

    dirkgently is correct. However rather that constructing x and y with 0, an explicit call to the default constructor will set intrinsic types to 0 and allow the template to be used for structs and classes with a default constructor.

    template <class T>
    class vector2
    {
    public:
        // use initializer lists
        vector2() : x(), y() {}
        T x;
        T y;
    };
    
    0 讨论(0)
  • 2021-01-18 09:24

    Don't use memset. It'll break horribly on non-POD types (and won't necessarily be easy to debug), and in this case, it's likely to be much slower than simply initializing both members to zero (two assignments versus a function call).

    Moreover, you do not usually want to zero out all members of a class. You want to zero out the ones for which zero is a meaningful default value. And you should get into the habit of initializing your members to a meaningful value in any case. Blanket zeroing everything and pretending the problem doesn't exist just guarantees a lot of headaches later. If you add a member to a class, decide whether that member should be initialized, and how.

    If and when you do want memset-like functionality, at least use std::fill, which is compatible with non-POD types.

    If you're programming in C++, use the tools C++ makes available. Otherwise, call it C.

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