【发布时间】:2011-11-08 21:55:48
【问题描述】:
如果我们有如下代码sn-p:
MyObject my_object = MyObject(0);
my_object = MyObject(1);
MyObject(0) 会发生什么?它被删除了吗?看看我读到的关于它的内容,只有在我们离开创建范围时才应该删除它,所以答案可能是否定的。如果是这种情况,除了使用指针之外,还有什么方法可以显式删除它?
【问题讨论】:
标签: c++ object new-operator
如果我们有如下代码sn-p:
MyObject my_object = MyObject(0);
my_object = MyObject(1);
MyObject(0) 会发生什么?它被删除了吗?看看我读到的关于它的内容,只有在我们离开创建范围时才应该删除它,所以答案可能是否定的。如果是这种情况,除了使用指针之外,还有什么方法可以显式删除它?
【问题讨论】:
标签: c++ object new-operator
MyObject my_object = MyObject(0);
这一行使用MyObject 的构造函数在堆栈上创建my_object,该构造函数可以接受int。
my_object = MyObject(1);
这一行再次使用与第一行相同的构造函数创建了一个临时 MyObject。然后通过调用赋值运算符将其分配给my_object。如果您没有提供此运算符,那么编译器将为您创建一个执行 shallow 副本的运算符。当此语句完成时,临时 MyObject 将超出范围并调用它的析构函数。
当您的my_object 超出范围时,它又会以同样的方式被销毁。您在任何时候都不需要手动删除它,因为所有内容都在堆栈上分配。
【讨论】:
my_object 正在创建,因此它可以避免创建额外的临时对象。基本上,第一行与您所做的相同:MyObject my_object(0);。
在谈论新创建的对象时,内存中有两个主要区域:堆栈和堆。堆包含使用 new 动态创建的所有对象。这些对象需要使用 delete 运算符显式删除。堆栈是特定于范围的,堆栈上定义的所有对象都将被自动删除。由于您不使用 new,因此您的所有对象都将在其作用域结束时被销毁。
假设没有编译器优化,您的代码大致转换为:
{
MyObject my_object;
MyObject tempObject0(0);
my_Object = tempObject0;
MyObject tempObject1(1);
my_Object = tempObject;
}//3 objects are deleted by this point (in theory)
还要注意两者的区别
MyObject myObject(0);
和
MyObject myObject = MyObject(0);
第二种情况会创建一个临时对象,因此效率会降低。当然,这一切都假设没有优化。根据编译器的不同,它可能会转换为相同的东西。
【讨论】:
// 3 Objects are deleted here 是错误的。临时对象在包含它们的完整表达式的末尾被销毁,而不是在包含块的出口处。因此,MyObject(0) 在MyObject(1) 被创建之前就被销毁了。
delete这个词在C++中有特殊的含义,所以deleted的使用是不幸的。
MyObject my_object = MyObject(0);
这一行声明一个MyObject 类型的对象使用自动存储持续时间创建(即,在堆栈上)。当作用域结束时,该对象将被销毁(即,将执行其析构函数)。标准中没有规定相关记忆的回忆(见后面的例子)。
这个MyObject 类型的对象将使用表达式MyObject(0) 构造。此构造函数将初始化已分开供其专用的内存。
注意:实际上,可以创建一个临时的,然后调用复制构造函数,但幸运的是,大多数编译器都避开了这个中间步骤,因为标准特别允许这样做。
my_object = MyObject(1);
这一行将由表达式MyObject(1) 确定的新值分配给已经存在的my_object 对象。为此,将创建一个具有自动存储持续时间的 MyObject 类型的临时文件。然后,执行赋值运算符;如果没有重载,它会将临时状态复制到my_object,擦除之前的状态。在表达式结束时,临时对象被破坏(再一次,没有为相关记忆的回忆做任何准备)。
注意:MyObject(0) 没有被“删除”,因为它不存在,而是将其写入状态的内存重新用于从MyObject(1) 复制状态。
正如所承诺的,既然这似乎是你的担心,那就讨论一下内存方面的问题。这是特定于编译器的,但大多数编译器的行为确实相似。
假设我们有以下函数:
void f() {
MyObject my_object = MyObject(0);
{
my_object = MyObject(1);
do_something(my_object);
}
{
my_object = MyObject(2);
do_something(my_object);
}
}
堆栈需要多少空间?
根据这些假设,它需要 3 个MyObject 的空间。
MyObject my_object = MyObject(0);:my_object需要活到函数结束my_object = MyObject(1);: 临时需要创建my_object = MyObject(2);: 临时创建需求堆栈空间在函数执行结束时被回收。
如果编译器足够聪明地执行堆栈着色,那么两个临时对象(永远不需要一起)可以使用相同的内存点,从而将空间需求降低到 2 MyObject。
智能优化器还可以直接将MyObject(1) 和MyObject(2) 直接构建到my_object 中(如果它可以证明效果与构建临时然后复制它相同),从而降低空间要求为 1 MyObject。
最后,如果do_something的定义是可见的,并且它没有使用它的参数,那么在某些条件下它可以(理论上)完全绕过my_object的构造。这种优化可以通过简单的程序来见证,例如:
int main() { int i = 0; for (; i < 1000; ++i); return i; }
经过简单优化:
int main() { return 1000; }
(注意i 是如何消失的)
您可能会注意到...实际上很难猜测编译器/优化器将能够做什么。如果您确实有严格的内存要求,那么(也许令人惊讶),您最好的选择可能是用函数替换块。
【讨论】:
第一行正在创建一个临时对象,并将该对象分配给 my_object。 在第 2 行中,创建了一个临时对象并将其分配给 my_object。
所以只有一个对象 my_object
我们不需要考虑临时对象。处理临时对象是编译器的责任。
【讨论】: