删除班级的所有成员

时间:2009-01-31 15:36:35

标签: c++

昨天我读了一些同事的代码并发现了这个:

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?
 }

这是合法的(WTF线,而不是公共属性)?对我来说它闻起来非常非常糟糕...... 使用VC8编译时代码运行正常,但在调用a_instances[0].event_more_values.push_back(whatever)时使用VC9编译时会抛出“意外异常”,但访问任何其他成员时。任何见解?

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

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

8 个答案:

答案 0 :(得分:9)

这是一种广泛接受的C结构初始化方法 在C ++中它不起作用,因为你不能假设vector的内部结构。将其归零非常可能使其处于非法状态,这就是程序崩溃的原因。

答案 1 :(得分: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);
}

交换只会将o的向量与* this交换,并清除其他变量。交换矢量特别便宜。他当然需要编写一个交换函数,然后交换向量(even_more_values.swap(that.even_more_values))和其他变量。

答案 2 :(得分:4)

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

答案 3 :(得分:4)

当清零a_instances时,你也将std_vector归零。这可能在构造时分配缓冲区。现在,当您尝试push_back时,它会看到指向缓冲区的指针为NULL(或其他一些内部成员),因此它会引发异常。

如果你问,这是不合法的。那是因为你不能通过指针重载写入,因为你可以重载赋值运算符。

答案 4 :(得分:3)

你不应该对C ++对象做memset,因为它不会调用正确的构造函数或析构函数。

特别是在这种情况下,不会调用所有 a_instances 元素的 even_more_values 成员的析构函数。

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

答案 5 :(得分:3)

您应该在班级中实施'clear'方法

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

答案 6 :(得分:3)

最糟糕的部分是,如果向量中有任何内容,那么内存现在会丢失,因为没有调用构造函数。

永远不要覆盖C ++对象。 EVER。如果它是一个派生对象(我不知道std :: vector的细节),这段代码也会覆盖对象的vtable,使其崩溃和损坏。

写这篇文章的人并不了解对象是什么,需要你解释它们是什么以及它们是如何工作的,这样它们将来就不会犯这种错误。

答案 7 :(得分:2)

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

我会做两件事:

  1. a_classsome_function()中使用std :: vector作为存储空间。
  2. 为正确清理的a_class编写析构函数
  3. 如果这样做,编译器将自动为您管理存储。

    例如:

    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编写析构函数,编译器为你提供的析构函数就足够了。)< / p>