返回值的理解
class A
{
……..
};
一直以来搞不清“返回值”这个概念,今天总算搞清楚了。
我们知道返回值有三种方式:
第一种:“普通”方式,例如,
A fun()
{
A a;
...
return a;
}
第二种:const方式,例如
const A fun()
{
A a;
...
return a;
}
第三种:const+引用方式,例如
const A& fun(const A& a)
{
...
return a;
}
首先我们得弄清楚返回值的机制,我们做一下测试:
#include <iostream.h>
class A
{
public:
int a;
int b;
static int cnt;//调用次数
A();
A(const A &rhs);
};
int A::cnt = 1;
A::A()
{
a = 0;
b = 0;
cout<<this<<endl;
cout<<"Construct be performanced "<<cnt<<" times."<<endl;
cnt++;
}
A::A(const A &rhs)
{
cout<<this<<endl;
cout<<"Copy Construct be performanced"<<endl;
a = rhs.a;
b = rhs.b;
}
const A Fun()
{
A aa;
return aa;
}
int main()
{
A bb = Fun();
return 0;
}
(1)
程序的运行结果是,构造函树调用了1次、拷贝构造函数被调用了2次,输出了三个不同的this指针的地址,因此我们可以得出结论,整个程序在运行过程钟产生了三个对象。其中的两个对象是显而易见的,一个是Fun()中的aa,一个是main()中的bb,那么还有一个是哪里来的呢。是返回值,以前对我返回值的理解“fun ()的返回值 == aa”,现在看来是错误的。在bb被func()赋值之前,编译器生成了一个返回值。
为了进一步证明我们把main()函数改为:
int main()
{
cout<<Fun().a<<endl;
return 0;
}
运行结果:
第二个地址值就是返回值(对象)所在的地址。
第三中方式因为传回的是引用,“fun ()的返回值 == aa”在这种情况是成立的。我们把fun()改为:
const A& Fun()
{
A aa;
return aa;
}
这时的运行结果
不过这样做存在一个巨大的隐患,如果把A bb = Fun();改为
A &bb = Fun();
因为aa是一个局部变量,它的生命周期在A &bb = Fun();语句子后就结束了。这时如果编译程序将报错。
结论:
1.当函数带返回值时,有一个看不见的返回值变量
2,返回值的第一种方式和第二种方式的区别是第二种方式中返回值是const型的,在直接用返回值不可改变其值
3.对于A bb = fun ()这样的语句(fun()的返回值采用的是第一种和第二种方式),是直接调用拷贝构造函数而非赋值运算符。
4.第三种方式可认为“fun ()的返回值 == aa”,但应注意return后的变量不为局部变量,如果是动态变量(在堆上申请内存),应注意delete,建议不要这么用,
因为new和delete不在同一个函数中,很容易被遗忘。
这里引发我的另一个问题,为什么A bb = fun() 调用的是拷贝构造函数而不是赋值运算符。我猜测是不是赋值函数调用了拷贝函数(其实稍加推敲就知道这个想法是错误的,因为调用一个拷贝构造函数就意味新生成了一个对象,而在调用赋值运算符之前,肯定已经调用了一个构造函数,而一个对象是不可能调用类的两个构造函数的(指同一个类的两个构造函数))。为了弄清这个问题,我为class A 重载了“=”运算符:
const A A::operator=(const A& rhs)
{
cout<<this<<endl;
cout<<"Evaluate function be performanced"<<endl;
a = rhs.a;
b = rhs.b;
return *this;
}
但运行的结果依然与(1)一样。但如果把 A bb = fun();改为
A bb;
bb = Fun();
这时的运行结果
调用了普通构造函数和赋值运算符。因此得出结论:
bb = fun();与
A bb;
bb = Fun();
的运行方式是不一样的。