为什么以下代码打印“xxY”?局部变量不应该存在于整个函数的范围内吗?我可以使用这种行为,或者在将来的C ++标准中会改变这种行为吗?
我认为根据C ++标准3.3.2“块中声明的名称是该块的本地名称。它的潜在范围从其声明点开始,并在其声明区域的末尾结束。“
#include <iostream>
using namespace std;
class MyClass
{
public:
MyClass( int ) { cout << "x" << endl; };
~MyClass() { cout << "x" << endl; };
};
int main(int argc,char* argv[])
{
MyClass (12345);
// changing it to the following will change the behavior
//MyClass m(12345);
cout << "Y" << endl;
return 0;
}
根据回答,我可以假设MyClass(12345);
是表达式(和范围)。这是有道理的。所以我希望以下代码始终打印“xYx”:
MyClass (12345), cout << "Y" << endl;
允许进行此类替换:
// this much strings with explicit scope
{
boost::scoped_lock lock(my_mutex);
int x = some_func(); // should be protected in multi-threaded program
}
// mutex released here
//
// I can replace with the following one string:
int x = boost::scoped_lock (my_mutex), some_func(); // still multi-thread safe
// mutex released here
答案 0 :(得分:16)
在
中创建的对象MyClass(12345);
是一个临时对象,只在该表达式中 ;
MyClass m(12345);
是一个对整个块都有效的对象。
答案 1 :(得分:8)
您实际上是在创建对象而不将其保留在范围内,因此它在创建后立即被销毁。因此,您正在经历的行为。
您无法访问创建的对象,为什么编译器会保留它?
答案 2 :(得分:5)
回答您的其他问题。以下是逗号运算符的调用。它创建了一个MyClass
临时表,包括调用它的构造函数。然后它会计算第二个表达式cout << "Y" << endl
,它将打印出Y.然后,在完整表达式结束时,将销毁临时表,它将调用它的析构函数。所以你的期望是对的。
MyClass (12345), cout << "Y" << endl;
要使以下工作,您应该添加括号,因为逗号在声明中具有预定义含义。它将开始声明函数some_func
返回int
并且不带参数,并将scoped_lock
对象分配给x
。使用括号,您可以说整个事件只是一个逗号运算符表达式。
int x = (boost::scoped_lock (my_mutex), some_func()); // still multi-thread safe
应该注意,以下两行是等效的。第一个使用my_mutex
作为构造函数参数,不创建一个临时的未命名对象,而是名称周围的括号是多余的。不要让语法混淆你。
boost::scoped_lock(my_mutex);
boost::scoped_lock my_mutex;
我看到滥用术语范围和生命周期。
Scope
是您可以在不限定名称的情况下引用名称的地方。名称具有范围,对象继承用于定义它们的名称范围(因此有时标准称为“本地对象”)。临时对象没有范围,因为它没有名称。同样,new
创建的对象没有范围。 Scope是一个编译时属性。这个术语在标准中经常被滥用,请参阅this defect report,因此找到真正的含义会让人感到困惑。
Lifetime
是一个运行时属性。这意味着何时设置对象并准备使用。对于类类型对象,生命周期在构造函数结束执行时开始,并在析构函数开始执行时结束。终身常常与范围混淆,尽管这两件事完全不同。
临时工作的生命周期是精确定义的。它们中的大多数在评估它们包含的完整表达式后终止生命周期(例如,上面的逗号运算符或赋值表达式)。临时工具可以绑定到const引用,这将延长它们的寿命。在异常中抛出的对象也是临时对象,并且当它们不再存在处理程序时它们的生命周期结束。
答案 3 :(得分:4)
您正确引用了标准。让我强调一下:
块中的 名称 声明是该块的本地。它的潜在范围始于其声明点,并在其声明区域结束时结束。
实际上,您没有声明任何名称。您的行
MyClass (12345);
甚至不包含声明!它包含的是一个创建MyClass实例的表达式,计算表达式(但是,在这种特殊情况下,没有任何东西可以计算),并将其结果转换为void
,并销毁在那里创建的对象。
一个不那么令人困惑的事情听起来像是
call_a_function(MyClass(12345));
你多次看到它,知道它是如何工作的,不是吗?