2009-01-31 35 views
2

昨天我看了同事的一些代码和跨越这来了:擦除类的所有成员

class a_class 
{ 
public: 
    a_class() {...} 
    int some_method(int some_param) {...} 

    int value_1; 
    int value_2; 
    float value_3; 
    std::vector<some_other_class*> even_more_values; 
    /* and so on */ 
} 

a_class a_instances[10]; 

void some_function() 
{ 
    do_stuff(); 
    do_more_stuff(); 

    memset(a_instances, 0, 10 * sizeof(a_class)); // <===== WTF? 
} 

那是合法的(世跆联线,而不是公共属性)?对我来说,它闻起来真的很糟糕...... 使用VC8编译时代码运行良好,但当调用时,但在访问任何其他成员时,它会引发VC9编译时的“意外异常”。任何见解?

编辑:将存储从memset(&a_instances...更改为memset(a_instances...。感谢您指出Eduard。
EDIT2:删除了ctor的返回类型。谢谢你。

结论:谢谢你们,你们证实了我的怀疑。

回答

9

这是初始化C的结构被广泛接受的方法实施“清除”的方法。
在C++中它不适用,因为你不能假设任何关于vector的内部结构。清零它很可能会使其处于非法状态,这就是为什么您的程序崩溃。

4

我不确定,但我认为memset会擦除向量的内部数据。

4

当清零a_instances时,还可以将std_vector清零。构建时可能会分配一个缓冲区。现在,当您尝试push_back时,它将缓冲区的指针视为NULL(或其他内部成员),因此会引发异常。

如果你问,这是不合法的。这是因为你不能通过指针重载书写,因为你可以重载赋值操作符。

+0

好吧,你让我在那里。当然,在原始代码中不是&a_instances,而是a_instances。相应地更改了代码。谢谢。 – EricSchaefer 2009-01-31 16:05:45

+0

好吧,我正在改变我的答案。 – 2009-01-31 16:07:01

3

您不应该在C++对象上执行memset,因为它不会调用正确的构造函数或析构函数。

具体地,在这种情况下,even_more_values所有a_instances的元件构件析构函数不被调用。

实际上,至少对于你列出的成员(在/ *之前等* /),你不需要调用memset或创建任何特殊的析构函数或clear()函数。所有这些成员都会被默认的析构函数自动删除。

+0

那么,你*可以*,但... – dmckee 2009-01-31 15:54:42

+0

固定为“不应该”:-) – 2009-01-31 15:56:08

+0

::耸耸肩::我明白了。只是在一个慵懒的周六早上感觉相反。 – dmckee 2009-01-31 15:57:17

3

你应该在你的类

void clear() 
    { 
    value1=0; 
    value2=0; 
    value_3=0f; 
    even_more_values.clear(); 
    } 
5

他在非POD类类型上使用memset。这是无效的,因为C++只允许它用于最简单的情况:如果一个类没有用户声明的构造函数,析构函数,没有虚函数和其他几个限制。它的一系列对象不会改变这个事实。

如果他删除了矢量,但使用memset就可以了。一个音符虽然。即使它不是C++,它对于他的编译器可能仍然有效 - 因为如果标准说某事有未定义的行为,实现可以做他们想要的一切 - 包括祝福这种行为并说出会发生什么。在他的情况下,会发生什么情况可能是你应用了memset,它会默默地清除向量中的任何成员。可能的指针指向已分配的内存,现在只会包含零,但不知道这一点。

你可以建议他将其清除出使用这样的事情:

... 
for(size_t i=0; i < 10; i++) 
    objects[i].clear(); 

而且使用像写清楚:

void clear() { 
    a_object o; 
    o.swap(*this); 
} 

交换实现只想与一个交换邻矢量*这一点,并清除其他变量。交换矢量特别便宜。他当然需要编写交换函数,然后交换矢量(even_more_values.swap(that.even_more_values))和其他变量。

3

其中最糟糕的部分是,如果矢量中有任何东西,那么现在内存丢失了,因为构造函数没有被调用。

绝不会覆盖C++对象。 EVER。如果它是一个派生对象(并且我不知道std :: vector的细节),则此代码还会覆盖该对象的vtable,使其崩溃并损坏。

无论谁写这篇文章都不明白对象是什么,需要你解释他们是什么以及他们如何工作,以便他们在将来不会犯这种错误。

2

你在这里有什么可能不会崩溃,但它可能不会做你想要的!清零矢量将不会调用每个a_class实例的析构函数。它也将覆盖a_class.even_more_values的内部数据(因此,如果您的push_back()memset()之后,您可能会遇到访问冲突)。

我会做出不同的两件事情:

  1. 使用std :: vector的为您的存储无论在a_classsome_function()
  2. 写出a_class析构函数是由编译器自动清理正确

如果你这样做,存储进行管理你。

例如:

class a_class 
{ 
public: 
    a_class() {...} 
    ~a_class() { /* make sure that even_more_values gets cleaned up properly */ } 

    int some_method(int some_param) {...} 

    int value_1; 
    int value_2; 
    float value_3; 
    std::vector<some_other_class*> even_more_values; 
    /* and so on */ 
} 

void some_function() 
{ 
    std::vector<a_class> a_instances(10); 

    // Pass a_instances into these functions by reference rather than by using 
    // a global. This is re-entrant and more likely to be thread-safe. 
    do_stuff(a_instances); 
    do_more_stuff(a_instances); 

    // a_instances will be cleaned up automatically here. This also allows you some 
    // weak exception safety. 
} 

请记住,如果even_more_values包含指针到其他对象,则需要在a_class析构函数删除这些对象。如果可能的话,even_more_values应该包含对象本身而不是指向那些对象的指针(这样你就不必为a_class编写析构函数,编译器为你提供的那个就足够了)。