How to do the equivalent of memset(this, …) without clobbering the vtbl?

后端 未结 7 1076
粉色の甜心
粉色の甜心 2020-11-29 10:39

I know that memset is frowned upon for class initialization. For example, something like the following:

class X { public: 
X() { memset( this,          


        
相关标签:
7条回答
  • 2020-11-29 11:03

    Leverage the fact that a static instance is initialised to zero: https://ideone.com/GEFKG0

    template <class T>
    struct clearable
    {
        void clear()
        {
            static T _clear;
            *((T*)this) = _clear;
        };
    };
    
    class test : public clearable<test>
    {
        public:
            int a;
    };
    
    int main()
    {
        test _test;
        _test.a=3;
        _test.clear();
    
        printf("%d", _test.a);
    
        return 0;
    }
    

    However the above will cause the constructor (of the templatised class) to be called a second time.

    For a solution that causes no ctor call this can be used instead: https://ideone.com/qTO6ka

    template <class T>
    struct clearable
    {
        void *cleared;
        clearable():cleared(calloc(sizeof(T), 1)) {}
    
        void clear()
        {
            *((T*)this) = *((T*)cleared);
        };
    };
    

    ...and if you're using C++11 onwards the following can be used: https://ideone.com/S1ae8G

    template <class T>
    struct clearable
    {
        void clear()
        {
            *((T*)this) = {};
        };
    };
    
    0 讨论(0)
  • For each class where you find a memset call, add a memset member function which ignores the pointer and size arguments and does assignments to all the data members.

    edit: Actually, it shouldn't ignore the pointer, it should compare it to this. On a match, do the right thing for the object, on a mismatch, reroute to the global function.

    0 讨论(0)
  • 2020-11-29 11:07

    This is hideous, but you could overload operator new/delete for these objects (or in a common base class), and have the implementation provide zero'd out buffers. Something like this :

    class HideousBaseClass
    {
    public:
        void* operator new( size_t nSize )
        {
            void* p = malloc( nSize );
            memset( p, 0, nSize );
            return p;
        }
        void operator delete( void* p )
        {
            if( p )
                free( p );
        }
    };
    

    One could also override the global new/delete operators, but this could have negative perf implications.

    Edit: I just realized that this approach won't work for stack allocated objects.

    0 讨论(0)
  • 2020-11-29 11:19

    Try this:

    template <class T>
    void reset(T& t)
    {
       t = T();
    }
    

    This will zeroed your object - no matter it is POD or not.

    But do not do this:

       A::A() { reset(*this); }
    

    This will invoke A::A in infinite recursion!!!

    Try this:

      struct AData { ... all A members };
      class  A { 
       public: 
          A() { reset(data); } 
       private: 
          AData data; 
       };
    
    0 讨论(0)
  • 2020-11-29 11:23

    You could always add constructors to these embedded structures, so they clear themselves so to speak.

    0 讨论(0)
  • 2020-11-29 11:23

    The better solution I could find is to create a separated struct where you will put the members that must be memsetted to zero. Not sure if this design is suitable for you.

    This struct got no vtable and extends nothings. It will be just a chunk of data. This way memsetting the struct is safe.

    I have made an example:

    #include <iostream>
    #include <cstring>
    
    struct X_c_stuff {
        X_c_stuff() {
            memset(this,0,sizeof(this));
        }
        int cMember;
    };
    class X : private X_c_stuff{
    public:
        X() 
        : normalMember(3)
        {
            std::cout << cMember << normalMember << std::endl;
        }
    private:
        int normalMember;
    };
    
    int main() {
        X a;
        return 0;
    }
    
    0 讨论(0)
提交回复
热议问题