English 中文(简体)
删除班级所有成员
原标题:
  • 时间:2009-01-31 15:36:34
  •  标签:

昨天我看了同事的一些代码,发现了这个。

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

Is that legal (the WTF line, not the public attributes)? To me it smells really, really bad... The code ran fine when compiled with VC8, but it throws an "unexpected exception" when compiled with VC9 when calling a_instances[0].event_more_values.push_back(whatever), but when accessing any of the other members. Any insights?

EDIT: Changed the memset from memset(&a_instances... to memset(a_instances.... Thanks for pointing it out Eduard.
EDIT2: Removed the ctor s return type. Thanks litb.

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

最佳回答

This is a widely accepted method for initialization for C structs.
In C++ it doesn t work ofcourse because you can t assume anything about vectors internal structure. Zeroing it out is very likely to leave it in an illegal state which is why your program crashes.

问题回答

他在一个非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))和其他变量。

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

将a_instances归零时,也会将其中的std_vector设为零。这可能会在构建时分配一个缓冲区。现在,当你尝试push_back时,它会看到缓冲区的指针为NULL(或其他内部成员),所以它会抛出异常。

如果您询问,那将不是合法的。这是因为您无法通过指针重载写入,就像您可以重载赋值运算符一样。

最糟糕的部分是,如果向量中有任何内容,由于构造函数未被调用,因此现在已经丢失了该内存。

永远不要覆盖一个C++对象。如果它是一个派生对象(我不知道std::vector的具体情况),这个代码也会覆盖对象的虚函数表,使它变得不稳定且损坏。

谁写的不理解什么是物体,需要你解释什么是物体以及它们是如何工作的,这样他们就不会在将来犯这种错误了。

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

具体而言,在这种情况下,没有调用所有a_instances元素的even_more_values成员的析构函数。

实际上,在列出的成员中,您不需要调用memset或创建任何特殊的析构函数或clear()函数。所有这些成员都会被默认的析构函数自动删除。

你应该在你的类中实现一个清除方法 clear。

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

你这里可能不会崩溃,但也可能不能做你想要的!将向量归零不会调用每个 a_class 实例的析构函数。它还将覆盖 a_class.even_more_values 的内部数据(所以如果你的 push_back()memset() 之后,你可能会得到访问冲突)。

我会做两件不同的事情:

  1. Use std::vector for your storage both in a_class and in some_function().
  2. Write a destructor for a_class that cleans up properly

如果您这样做,存储将由编译器自动管理。

例如:

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编写析构函数,编译器为您提供的那个可能已经足够)。





相关问题
热门标签