重新分配变量时,不会调用析构函数:
Object foo = Object(a,b);
foo = Object(c,d);
所以,析构函数只会在Object(c,d)范围的末尾被调用,这显然会导致问题。 现在,在这种特殊情况下,它并没有太多困扰我:它足以声明2个不同的对象:
Object foo1 = Object(a,b);
Object foo2 = Object(c,d);
这样就可以在最后调用两个对象的析构函数。
但是,有一种情况我必须重新分配变量,即在对象构造函数中,例如:
SuperObject(Point point1, Point point2) : delay_object_(DelayObject(0)) {
double distance = distance(point1, point2);
double delay = distance / speed;
delay_object_ = DelayObject(delay);
}
事实上,DelayObject参数不容易计算(在这个例子中我也省略了一些其他段落),我想避免在初始化列表中这样做。
我以为我可以通过将对象放在堆中并显式调用析构函数来强制删除:
SuperObject(Point point1, Point point2) : p_delay_object_(new DelayObject(0)) {
double distance = distance(point1, point2);
double delay = distance / speed;
delete p_delay_object_;
p_delay_object_ = new DelayObject(delay);
}
但这对我来说真的很难看,因为我更喜欢仅在严格必要时使用动态分配。我错过了什么吗?
干杯!
答案 0 :(得分:11)
“析构函数只会在Object(c,d)范围的末尾被调用”
假。 Object(c,d)
是一个临时的,它的析构函数在创建它的全表达式的末尾被调用。在这种情况下,这是foo = Object(c,d);
末尾的分号。 foo
的析构函数在作用域末尾调用。
Object
的赋值运算符应释放或重用foo
已拥有的资源,并复制临时资源。不一定按顺序排列(参见copy-and-swap)。
编辑:回应评论。
Object foo = Object(a,b);
要么
(a,b)
构建临时。foo
是使用copy-constructor构造的,传递临时参数。或的
foo
是使用任何双参数构造函数匹配(a,b)
构建的。实现可以自由执行 - “复制构造函数elision”允许这样做。
foo = Object(c,d);
(c,d)
构建临时。Object
上调用类foo
的赋值运算符,并传递临时参数。一段时间后,在范围的最后,foo
被销毁。
在C ++ 0x中,如果该类存在,则移动赋值将起作用。
答案 1 :(得分:8)
你应该重载赋值运算符,从概念上讲,它会复制构造现有对象并销毁旧的'this'。
class Object {
...
Object& operator= (const Object& other) {
if (this != &other) {
// copy 'other' into 'this'.
}
return *this;
}
...
};
然后foo = Object(c,d);
行应该达到预期效果。
(同样提到@Steve Jessop,临时对象Object(c,d)
也将在范围结束之后被破坏。)
答案 2 :(得分:1)
是的,您忘记了可以重载赋值运算符。编译器默认提供的那个只是盲目地复制对象的所有字段,但是你可以自己提供你想做的任何事情,包括处理赋值对象目标的资源。
请注意,分配操作符很容易出错(忘记极端情况,异常安全......),我建议您阅读运算符重载常见问题解答at least this part,然后重定向{{3这几乎总是实现赋值运算符的唯一合理方法。
<强> 修改 强>
另见@Steve Jessop的回答,它考虑了另一个根本的误解。
在你写的所有情况下
SomeClass sc = SomeClass(parameters);
您正在初始化一个新对象:您正在创建一个临时(SomeClass(parameters)
),然后使用它的副本(通过复制构造函数)初始化sc
;这是不必要的和低效的,在C ++中在堆栈上创建对象的语法只是
SomeClass sc(parameters);
答案 3 :(得分:0)
将DelayObject参数的计算委托给私有(可能是静态的)方法,并在初始化列表中构造DelayObject时使用它们。