如何在不破坏vtbl的情况下完成相当于memset(this,...)的操作?

时间:2012-10-15 19:28:15

标签: c++ initialization

我知道memset对class初始化不满意。例如,类似以下内容:

class X { public: 
X() { memset( this, 0, sizeof(*this) ) ; }
...
} ;
如果混合中有vtbl函数,

会破坏virtual

我正在开发一个(巨大的)遗留代码库,它是C-ish但是用C ++编译,所以所讨论的所有成员通常都是POD,不需要传统的C ++构造函数。 C ++的使用逐渐深入(就像虚函数一样),这就咬了那些没有意识到memset有这些额外的C ++牙齿的开发人员。

我想知道是否有一种C ++安全的方法来进行初始的全部零初始化,然后可能会进行特定的成员初始化,其中零初始化不合适?

我发现了类似的问题memset for initialization in C++zeroing derived struct using memset。这两个都有“不使用memset()”的答案,但没有好的选择(特别是对于可能包含许多成员的大型结构)。

7 个答案:

答案 0 :(得分:4)

对于找到memset调用的每个类,添加一个memset成员函数,该函数忽略指针和大小参数,并为所有数据成员分配。

编辑: 实际上,它不应该忽略指针,它应该将它与this进行比较。在匹配中,为对象做正确的事情,在不匹配的情况下,重新路由到全局函数。

答案 1 :(得分:1)

你总是可以在这些嵌入式结构中添加构造函数,因此它们可以说清楚了。

答案 2 :(得分:1)

这很可怕,但是你可以重载这些对象(或公共基类)的过载operator new/delete,并让实现提供零输出缓冲区。像这样:

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 );
    }
};

还可以覆盖全局 new/delete运算符,但这可能会产生负面影响。

编辑:我刚刚意识到这种方法对堆栈分配的对象不起作用。

答案 3 :(得分:1)

试试这个:

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

这会将你的对象归零 - 无论它是不是POD。

但不要这样做:

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

这将在无限递归中调用A::A !!!

试试这个:

  struct AData { ... all A members };
  class  A { 
   public: 
      A() { reset(data); } 
   private: 
      AData data; 
   };

答案 4 :(得分:1)

利用静态实例初始化为零的事实: 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;
}

但是上面的内容会导致构造函数(templatised类)被第二次调用。

对于不会导致ctor调用的解决方案,可以使用此代码:https://ideone.com/qTO6ka

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

    void clear()
    {
        *((T*)this) = *((T*)cleared);
    };
};

...如果您使用的是C ++ 11,则可以使用以下内容:https://ideone.com/S1ae8G

template <class T>
struct clearable
{
    void clear()
    {
        *((T*)this) = {};
    };
};

答案 5 :(得分:0)

您可以使用指针算法查找要清零的字节范围:

class Thing {
public:
    Thing() {
        memset(&data1, 0, (char*)&lastdata - (char*)&data1 + sizeof(lastdata));
    }
private:
    int data1;
    int data2;
    int data3;
    // ...
    int lastdata;
};

(编辑:我最初使用offsetof(),但是评论指出这只能用于POD,然后我意识到你可以直接使用成员地址。)

答案 6 :(得分:0)

我能找到的更好的解决方案是创建一个单独的结构,您将把必须被memset的成员放到零。不确定这种设计是否适合您。

这个结构没有vtable并且扩展了nothings。它只是一大块数据。这样设置结构是安全的。

我举了一个例子:

#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;
}