局部变量作用域问题

发布于 2024-08-04 05:48:26 字数 1096 浏览 4 评论 0 原文

为什么下面的代码打印“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

Why is the following code prints "xxY"? Shouldn't local variables live in the scope of whole function? Can I use such behavior or this will be changed in future C++ standard?

I thought that according to C++ Standard 3.3.2 "A name declared in a block is local to that block. Its potential scope begins at its point of declaration and ends at the end of its declarative region."

#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;
}

Based on the responses I can assume that MyClass(12345); is the expression (and scope). That is make sense. So I expect that the following code will print "xYx" always:

MyClass (12345), cout << "Y" << endl;

And it is allowed to make such replacement:

// 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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

┈┾☆殇 2024-08-11 05:48:26

在您创建的对象

MyClass(12345);

是一个临时对象,仅在该表达式中存在;

MyClass m(12345);

是一个在整个块中都处于活动状态的对象。

The object created in your

MyClass(12345);

is a temporary object which is only alive in that expression;

MyClass m(12345);

is an object which is alive for the entire block.

半步萧音过轻尘 2024-08-11 05:48:26

您实际上是在创建一个对象,但没有将其保留在范围内,因此它在创建后立即被销毁。因此,您正在经历的行为。

您无法访问创建的对象,那么编译器为什么要保留它呢?

You're actually creating an object without keeping it in scope, so it is destroyed right after it is created. Hence the behavior you're experiencing.

You can't access the created object so why would the compiler keep it around?

橘寄 2024-08-11 05:48:26

回答你的其他问题。以下是逗号运算符的调用。它创建一个 MyClass 临时对象,其中包括调用其构造函数。然后它计算第二个表达式 cout << “Y”<< endl 它将打印出 Y。然后,在完整表达式的末尾,它将销毁临时变量,该临时变量将调用其析构函数。所以你的期望是正确的。

MyClass (12345), cout << "Y" << endl;

为了使以下内容正常工作,您应该添加括号,因为逗号在声明中具有预定义的含义。它将开始声明一个返回 int 且不带任何参数的函数 some_func ,并将 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 创建的对象没有作用域。范围是编译时属性。该术语在标准中经常被误用,请参阅此缺陷报告,所以要找到真正的含义是相当令人困惑的。

  • Lifetime 是一个运行时属性。这意味着对象已设置并可供使用。对于类类型对象,生命周期从构造函数执行结束时开始,在析构函数开始执行时结束。生命周期经常与范围混淆,尽管这两个东西完全不同。

    临时对象的生命周期是精确定义的。它们中的大多数在评估它们所包含的完整表达式(例如上面的逗号运算符或赋值表达式)后结束生命周期。临时对象可以绑定到 const 引用,这将延长它们的生命周期。抛出异常的对象也是临时对象,当不再有处理程序时,它们的生命周期就会结束。

To answer your other questions. The following is the invocation of the comma operator. It creates a MyClass temporary, which includes calling its constructor. It then evaluates the second expression cout << "Y" << endl which will print out the Y. It then, at the end of the full expression, will destroy the temporary, which will call its destructor. So your expectations were right.

MyClass (12345), cout << "Y" << endl;

For the following to work, you should add parentheses, because the comma has a predefined meaning in declarations. It would start declaring a function some_func returning an int and taking no parameters and would assign the scoped_lock object to x. Using parentheses, you say that the whole thing is a single comma operator expression instead.

int x = (boost::scoped_lock (my_mutex), some_func()); // still multi-thread safe

It should be noted that the following two lines are equivalent. The first does not create a temporary unnamed object using my_mutex as the constructor argument, but instead the parentheses around the name are redundant. Don't let the syntax confuse you.

boost::scoped_lock(my_mutex);
boost::scoped_lock my_mutex;

I've seen misuse of the terms scope and lifetime.

  • Scope is where you can refer to a name without qualifying its name. Names have scopes, and objects inherit the scope of the name used to define them (thus sometimes the Standard says "local object"). A temporary object has no scope, because it's got no name. Likewise, an object created by new has no scope. Scope is a compile time property. This term is frequently misused in the Standard, see this defect report, so it's quite confusing to find a real meaning.

  • Lifetime is a runtime property. It means when the object is set up and ready for use. For a class type object, the lifetime begins when the constructor ends execution, and it ends when the destructor begins execution. Lifetime is often confused with scope, although these two things are completely different.

    The lifetime of temporaries is precisely defined. Most of them end lifetime after evaluation of the full expression they are contained in (like, the comma operator of above, or an assignment expression). Temporaries can be bound to const references which will lengthen their lifetime. Objects being thrown in exceptions are temporaries too, and their lifetime ends when there is no handler for them anymore.

童话 2024-08-11 05:48:26

您正确引用了标准。我要强调一下:

块中名称声明是该块的本地名称。它的潜在范围从其声明点开始,到其声明区域的末尾结束。

实际上,您没有声明任何名称。您的

MyClass (12345);

甚至不包含声明!它包含一个表达式,该表达式创建 MyClass 的实例,计算该表达式(但是,在这种特殊情况下没有任何可计算的内容),并将其结果转换为 void,并销毁在那里创建的对象。

一个不那么令人困惑的事情听起来像是

call_a_function(MyClass(12345));

您已经看过很多次并且知道它是如何工作的,不是吗?

You quoted standard correctly. Let me emphasize:

A name declared in a block is local to that block. Its potential scope begins at its point of declaration and ends at the end of its declarative region.

You didn't declare any name, actually. Your line

MyClass (12345);

does not even contain a declaration! What it contains is an expression that creates an instance of MyClass, computes the expression (however, in this particular case there's nothing to compute), and casts its result to void, and destroys the objects created there.

A less confusing thing would sound like

call_a_function(MyClass(12345));

You saw it many times and know how it works, don't you?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文