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 个字节)从this. memset这是一个坏习惯:在使用复杂类时,您还会将虚拟指针和指向虚拟基的指针归零。而是这样做:

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)),在这种情况下会同时设置xy

但是,如果您的vector2类有任何虚方法,并且编译器使用通常的机制来表示它们,那么这将通过将指针设置为 NULLmemset来破坏vtable并破坏实例。vtable哪个不好。

另一个问题是,如果该类型T需要一些比将其位设置为 0 更复杂的构造函数操作,则不会调用成员的构造函数,但它们的效果会因用 覆盖成员的内容而被破坏memset()

唯一正确的操作是将默认构造函数编写为

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

并且完全忘记尝试使用memset()它。

编辑: D.Shawley 在评论中指出,在提供的原始代码中,默认构造函数实际上是在xy之前调用的。memset()虽然在技术上是正确memset()的,但调用会覆盖成员,这充其量是非常非常糟糕的形式,最坏的情况是调用未定义行为的恶魔。

正如所写,vector2该类是 POD,只要类型T也是普通的旧数据,如果Tintfloat.

然而,所需要的只是T成为某种bignum价值类别来导致可能很难诊断的问题。如果幸运的话,它们会通过取消引用由memset(). 但幸运女神是一个善变的情妇,更有可能的结果是一些内存泄漏,应用程序变得“不稳定”。或者更有可能,“摇摇欲坠”。

OP 在对另一个答案的评论中问道“......难道没有办法让 memset 工作吗?”

答案很简单,“不”。

选择了 C++ 语言并选择充分利用模板后,您必须通过正确使用该语言来为这些优势付出代价。绕过构造函数(在一般情况下)根本不正确。虽然在某些情况下调用memset()C++ 程序是合法、安全和明智的,但这并不是其中之一。

于 2009-04-12T21:09:08.753 回答
5

问题是这是一个指针类型,它是 4 个字节(在 32 位系统上),而整数是 4 个字节(在 32 位系统上)。尝试:

sizeof(*this)

编辑:虽然我同意其他人的观点,构造函数中的初始化列表可能是这里的正确解决方案。

于 2009-04-12T20:58:16.940 回答
4

不要使用 memset。它会在非 POD 类型上严重中断(并且不一定很容易调试),在这种情况下,它可能比简单地将两个成员初始化为零(两个赋值与函数调用)要慢得多。

此外,您通常不希望将类的所有成员归零。您希望将零是有意义的默认值的那些清零。并且您应该养成在任何情况下将您的成员初始化为有意义的值的习惯。将所有内容归零并假装问题不存在只会保证以后会让人头疼。如果将成员添加到类中,请决定是否应该初始化该成员以及如何初始化。

如果您确实需要类似 memset 的功能,请至少使用与非 POD 类型兼容的 std::fill。

如果您使用 C++ 编程,请使用 C++ 提供的工具。否则,称它为 C。

于 2009-04-12T21:15:02.657 回答
4

迫切地是正确的。然而,与使用 0 构造 x 和 y 不同,显式调用默认构造函数会将内部类型设置为 0,并允许模板用于具有默认构造函数的结构和类。

template <class T>
class vector2
{
public:
    // use initializer lists
    vector2() : x(), y() {}
    T x;
    T y;
};
于 2009-04-13T01:07:30.147 回答
3

不要试图比编译器更聪明。按照语言的意图使用初始值设定项列表。编译器知道如何有效地初始化基本类型。

如果您尝试对具有虚拟功能的类进行 memset hack,您很可能会覆盖 vtable,最终导致灾难。不要像那样使用 hack,它们是维护的噩梦。

于 2009-04-12T21:08:09.427 回答
1

这可能会起作用:


char buffer[sizeof(vector2)];
memset(buffer, 0, sizeof(buffer));
vector2 *v2 = new (buffer) vector2();

..或替换/覆盖 vector2::new 来做类似的事情。不过对我来说仍然很奇怪。

一定要搭配


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

于 2009-04-18T06:24:30.683 回答