7

memset が初期化のために眉をひそめていることは知っていclassます。たとえば、次のようなものです。

class X { public: 
X() { memset( this, 0, sizeof(*this) ) ; }
...
} ;

ミックスに関数 vtblがある場合は上書きします。virtual

私は、C っぽいが C++ でコンパイルされた (巨大な) レガシー コードベースに取り組んでいるため、問題のすべてのメンバーは通常 POD であり、従来の C++ コンストラクターは必要ありません。C++ の使用は (仮想関数のように) 徐々に忍び寄ってきており、memset にこれらの追加の C++ の知識があることに気付いていない開発者はこれに悩まされています。

最初のキャッチオールゼロ初期化を行うC++の安全な方法があり、その後にゼロ初期化が適切でない特定のby-member初期化が続く可能性があるかどうか疑問に思っていますか?

C++での初期化と memsetを使用した派生構造体のゼロ化について、同様の質問 memset を見つけました。これらの両方に「memset() を使用しない」という回答がありますが、適切な代替手段はありません (特に、多くのメンバーを潜在的に含む大きな構造の場合)。

4

7 に答える 7

4

呼び出しが見つかったクラスごとに、ポインターとサイズの引数を無視し、すべてのデータ メンバーに割り当てを行うメンバー関数をmemset追加します。memset

編集: 実際には、ポインターを無視するべきではなく、 と比較する必要がありthisます。一致する場合はオブジェクトに対して正しいことを行い、不一致の場合はグローバル関数に再ルーティングします。

于 2012-10-15T19:39:38.537 に答える
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が、これはパフォーマンスに悪影響を与える可能性があります。

編集:このアプローチは、スタックに割り当てられたオブジェクトでは機能しないことに気づきました。

于 2012-10-15T19:38:43.310 に答える
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; 
   };
于 2012-10-15T19:42:08.143 に答える
1

これらの埋め込み構造にはいつでもコンストラクターを追加できるため、いわば自分自身をクリアします。

于 2012-10-15T19:34:00.330 に答える
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;
}

ただし、上記の場合、(テンプレート化されたクラスの) コンストラクターが 2 回呼び出されます。

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) = {};
    };
};
于 2016-06-29T14:57:00.707 に答える
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 でのみ機能するはずであるとコメントが指摘していたので、メンバーのアドレスを直接使用できることに気付きました。)

于 2012-10-15T20:05:37.093 に答える
0

私が見つけることができるより良い解決策は、memsetted をゼロにする必要があるメンバーを配置する分離された構造体を作成することです。このデザインがあなたに適しているかどうかはわかりません。

この構造体には vtable がなく、何も拡張されていません。単なるデータの塊になります。このようにして、構造体の memsetting は安全です。

私は例を作りました:

#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;
}
于 2012-10-15T20:57:33.490 に答える