昨天我读了一些同事的代码并发现了这个:
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的返回类型。谢谢你。
结论:谢谢大家,你证实了我的怀疑。
答案 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()
之后,您可能会遇到访问冲突。)
我会做两件事:
a_class
和some_function()
中使用std :: vector作为存储空间。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
编写析构函数,编译器为你提供的析构函数就足够了。)< / p>