我在网站上看到以下程序,我无法理解输出。 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。
我的问题是为什么我看到这个以及破坏的确切位置。
答案 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