6

int 座標と float 座標の両方を処理する小さな座標クラスを作成しました。

template <class T>
class vector2
{
public:
    vector2() { memset(this, 0, sizeof(this)); }
    T x;
    T y;
};

次に、 main() で次のことを行います。

vector2<int> v;

しかし、私の MSVC デバッガーによると、x 値のみが 0 に設定され、y 値はそのままです。これまでテンプレート クラスで sizeof() を使用したことがありませんでしたが、それが原因でしょうか?

4

7 に答える 7

25

いいえ、使用しないmemsetでください - が指す位置から始まるポインターのサイズ (私の x86 Intel マシンでは 4 バイト) バイトをゼロにしますthismemsetこれは悪い習慣です。複雑なクラスで使用する場合、仮想ポインターと仮想ベースへのポインターもゼロにします。代わりに次のようにします。

template <class T>
class vector2
{
public:
    // use initializer lists
    vector2() : x(0), y(0) {}
    T x;
    T y;
};
于 2009-04-12T20:59:54.907 に答える
16

他の人が言ってmemset()いるように、これを行う正しい方法ではありません。ただし、そうでない理由については、いくつかの微妙な点があります。

まず、使用しようとしているのはバイトmemset()をクリアすることだけです。sizeof(void *)あなたのサンプルケースでは、それは偶然にもxメンバーが占有するバイトです。

簡単な修正はmemset(this, 0, sizeof(*this))、この場合は と の両方xを設定する を書き込むことyです。

ただし、vector2クラスに仮想メソッドがあり、通常のメカニズムを使用してコンパイラがそれらを表す場合、ポインターを NULLに設定することで、インスタンスmemsetが破棄され、中断されます。これは悪いことです。vtablevtable

もう 1 つの問題はT、ビットを 0 に設定するよりも複雑なコンストラクター アクションが型に必要な場合、メンバーのコンストラクターは呼び出されませんmemset()が、メンバーの内容を.

唯一の正しいアクションは、デフォルト コンストラクターを次のように記述することです。

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

そして、これを使用しようとすることをまったく忘れてくださいmemset()

編集:x D.Shawley はコメントで、デフォルトのコンストラクターがとyの前に実際に呼び出され たことを指摘しましたmemset()。技術的には正しいのですが、呼び出しmemset()はメンバーを上書きします。これはせいぜい非常に悪い形式であり、最悪の場合、未定義の動作の悪魔を呼び出します。

書かれているように、クラスはvector2PODTです。Tintfloat

ただし、T何らかのbignum値クラスであることが原因で、診断が非常に困難な問題が発生する可能性があります。運が良ければ、 によって作成された NULL ポインターの逆参照によるアクセス違反によって、早期に発生する可能性がありますmemset()。しかし、Lady Luck は気まぐれな愛人であり、より可能性の高い結果は、一部のメモリがリークされ、アプリケーションが "不安定" になることです。または、「揺れる」可能性が高いです。

OPは、別の回答に対するコメントで「...memsetを機能させる方法はありませんか?」と尋ねました。

答えは単純に「いいえ」です。

C++ 言語を選択し、テンプレートを最大限に活用することを選択した場合、言語を正しく使用することによって、それらの利点を支払う必要があります。コンストラクターをバイパスするのは正しくありません (一般的な場合)。memset()C++ プログラムで呼び出すことが合法で、安全で、賢明な状況もありますが、これはその 1 つではありません。

于 2009-04-12T21:09:08.753 に答える
5

問題は、これが Pointer 型であり、4 バイト (32 ビット システムの場合) であり、int が 4 バイト (32 ビット システムの場合) であることです。試す:

sizeof(*this)

編集:コンストラクターのイニシャライザーリストがおそらく正しい解決策であるという他の人に同意します。

于 2009-04-12T20:58:16.940 に答える
4

memset を使用しないでください。POD 以外の型ではひどく壊れます (また、必ずしもデバッグが容易であるとは限りません)。この場合、単純に両方のメンバーをゼロに初期化するよりもはるかに遅くなる可能性があります (2 つの代入と関数呼び出し)。

さらに、通常、クラスのすべてのメンバーをゼロにすることは望ましくありません。ゼロが意味のあるデフォルト値であるものをゼロにします。いずれにせよ、メンバーを意味のある値に初期化する習慣を身に付ける必要があります。すべてをゼロにして、問題が存在しないふりをすることは、後で多くの頭痛の種を保証するだけです. クラスにメンバーを追加する場合は、そのメンバーを初期化する必要があるかどうか、およびその方法を決定します。

memset のような機能が必要な場合は、少なくとも POD 以外の型と互換性のある std::fill を使用してください。

C++ でプログラミングしている場合は、C++ で利用できるツールを使用してください。それ以外の場合は、C とします。

于 2009-04-12T21:15:02.657 に答える
3

コンパイラより賢くなろうとしないでください。言語の意図どおりに初期化子リストを使用します。コンパイラは、基本型を効率的に初期化する方法を知っています。

仮想関数を持つクラスで memset ハックを試みると、vtable が上書きされて大惨事になる可能性が高くなります。そのようなハックを使用しないでください。メンテナンスの悪夢です。

于 2009-04-12T21:08:09.427 に答える