全局变量由结构中未见的析构函数更改

时间:2016-01-02 18:28:03

标签: c++

我在网站上看到以下程序,我无法理解输出。 http://codepad.org/T0qblfYg

#include <iostream>
using namespace std;

int i;

class A
{
public:
    ~A()
    {
        i=10;
    }
};

int foo()
{
    i=3;
    A ob;
    return i;
}

int main()
{
    cout <<"\n 1) Before calling i is "<<i;
    cout <<"\n 2) i = "<<i << " & the  function return value = " 
            << foo() << " and now i  is "   << i << endl;

    return 0;
}

输出是:

 1) Before calling i is 0
 2) i = 10 & the  function return value = 3 and now i  is 10

现在我是一个全局变量,在将调用返回到main之前,A的销毁应该将其更改为10。建议在调用返回main之后发生A的破坏,但是当我调用函数时作为调用者我总是期望结果是最终的。这里函数返回值不是10而是3。

我的问题是为什么我看到这个以及破坏的确切位置。

3 个答案:

答案 0 :(得分:3)

评估顺序不是您所期望的。调用函数时,可以按任何顺序计算函数的参数,甚至可以交错。所以,如果你打电话

f( g(), h(), i() );

调用g()h()i()的顺序取决于编译器。 这也适用于运算符。当您有表达式

expr1 << expr2 << expr3 << expr4;

表达式的计算顺序是任意的。您只知道,operator<<调用将从左到右进行计算,因为运算符是从左到右关联的,您可以将表达式重写为

((expr1 << expr2) << expr3) << expr4;

语法树看起来像这样:

              <<
            /    \
         <<     expr4
       /    \
    <<     expr3
  /    \
expr1 expr2

可以按任何顺序评估语法树的叶子,甚至是交错的。在评估了运算符或函数调用的所有叶子之后,将调用函数本身。

在你的情况下,foo()似乎在i第一次打印出来之前被调用,并且它是完全有效的(虽然是意外的)行为。

答案 1 :(得分:2)

析构函数运行 后评估return语句中的表达式。如果是你的另一种方式,你会陷入深深的麻烦:

std::string f() {
std::string res = "abcd";
return res;
}

此函数应返回一个包含"abcd"的字符串对象,而不是已经销毁的字符串对象的副本。

答案 2 :(得分:0)

  

为什么我会看到这个以及在哪里确切地召唤了破坏。

我相信当实例超出范围时会调用实例析构函数。您可以通过以下方式测试它(通过控制实例的范围 - 请参阅下面的bar())

注意 - 为了减少混淆,我1)重命名全局,并且当foo或bar被称为w.r.t时显式控制。随后的cout。

int g; // uninitialized!

class A
{
public:
   ~A()
      {
         g=10;
      }
};

int foo()
{
   g=3;
   A ob;
   return g;
} // <<<<<<<<<<< ob destructor called here

int bar()
{
   g=3;
   {
      A ob;
   }  // <<<<<<<<<<< ob destructor called here
   return g;
}

int t272(void)
{
   g = 0;  // force to 0
   std::cout << "\n 1) Before calling foo g is " << g;

   int fooRetVal = foo(); // control when called
                          // do not leave up to compiler

   std::cout << "\n 2) after foo, g = " << g
             << "\n 3)    fooRetVal = " << fooRetVal
             << "\n 4)    and now g = " << g
             << std::endl;


   g = 0;  // force to 0
   std::cout << "\n 1) Before calling bar g is " << g;

   int barRetVal = bar(); // control when called
                          // do not leave up to compiler

   std::cout << "\n 2) after bar, g = " << g
             << "\n 3)    barRetVal = " << barRetVal
             << "\n 4)    and now g = " << g
             << std::endl;

   return (0);
}

输出

1) Before calling foo g is 0
2) after foo, g = 10
3)    fooRetVal = 3     <<< dtor called after return of foo
4)    and now g = 10

1) Before calling bar g is 0
2) after bar, g = 10
3)    barRetVal = 10     <<< dtor was called before return of bar
4)    and now g = 10