关于返回值的几种情况
1.参考链接:关于返回值的几种情况
2.思路
在一个函数的内部,return的时候返回的都是一个拷贝,不管是变量、对象还是指针都是返回拷贝,但是这个拷贝是浅拷贝。
- 如果返回一个基本类型的变量,比如:
int a;
a = 5;
return a;
那么就会a的一个拷贝,即5返回,然后a就被销毁了。尽管a被销毁了,但它的副本5还是成功地返回了,所以这样做没有问题。
- 但是对于非动态分配(new/malloc)得到的指针,像1那么做就会有问题,比如在某个函数内部:
int a[] = {1, 2};
return a;
那么也会返回指针a的一个拷贝,我们假定a的地址值为0x002345FC,那么这个0x2345FC是能够成功返回的。当return执行完成后,a就要被销毁,也就是0x002345FC所指向的内存被回收了。如果这时候在函数外面,去地址0x002345FC取值,那得到的结果肯定是不对的。这就是为什么不能返回局部指针的原因。返回局部变量的引用的道理和这个类似。
这里也就是不能返回局部变量的应用的原因,这里的应用实际上就是一个隐式指针,但是返回局部指针的做法是危险且错误的!
- 对于返回(动态分配得到的)指针的另外一种情况,比如在函数内部:
int a = new int(5);
return a;
这样做是可以的。return a执行完后,a并没有被销毁(必须要用delete才能销毁a),所以这里返回的a是有效的。
- 如果不是基本数据类型,比如:
class A
{
public:
OtherClass * ...
};
如果在某个函数内部有一个A类的局部变量,比如:
A a;
return a;
这时候也会返回a的一个拷贝,如果A没有写深拷贝构造函数,就会调用缺省的拷贝构造函数(浅拷贝),这样做就会失败的;
如果A中提供了深拷贝构造函数,则这样做就是可以的。
实例
实验代码如下:
#include <iostream>
using namespace std;
int some_fun1()
{
int a = 5;
return a; //OK
}
int* some_fun2()
{
int a = 5;
int *b = &a;
return b; // not OK
}
int* some_fun3()
{
int *c = new int(5);
return c; // OK, return c执行完后,并没被销毁(必须要用delete才能销毁)
}
class CSomething
{
public:
int a;
int b;
public:
CSomething(int a, int b)
{
this->a = a;
this->b = b;
}
};
class CA
{
private:
CSomething* sth; // 以指针形式存在的成员变量
public:
CA(CSomething* sth)
{
this->sth = new CSomething(sth->a, sth->b);
}
// 如果不实现深拷贝,请注释这个拷贝构造函数
CA(CA& obj)
{
sth = new CSomething((obj.sth)->a, (obj.sth)->b);
}
~CA()
{
cout << "In the destructor of class CA..." << endl;
if (NULL != sth) delete sth;
}
void Show()
{
cout << "(" << sth->a << ", " << sth->b << ")" << endl;
}
void setValue(int a, int b)
{
sth->a = a;
sth->b = b;
}
void getSthAddress()
{
cout << sth << endl;
}
};
CA some_fun4()
{
CSomething c(1, 2);
CA a(&c);
return a; // 如果CA没有实现深拷贝,则not OK;如果实现深拷贝,则OK
}
int main(int argc, char* argv[])
{
int a = some_fun1();
cout << a << endl; // OK
int *b = some_fun2();
cout << *b << endl; // not OK,即便返回结果正确,也不过是运气好而已
int *c = some_fun3(); // OK, return c执行完后,c并没有被销毁(必须要用delete才能销毁)
cout << *c << endl;
delete c;
CA d = some_fun4(); // 如果CA没有实现深拷贝,则not OK;如果实现深拷贝,则OK
d.Show();
return 0;
}
若函数返回值是vector对象时:
vector是STL中定义的一个类,而且实现了拷贝构造函数,在函数返回一个vector类型的对象时,就会调用vector的拷贝构造函数,产生一个临时的副本,在函数外部得到的就是这个临时的副本,而原对象本身此时已经被析构了。返回数组的话,你返回的只是数组的首地址(相当于一个unsigned int值而已),函数返回时,会返回该地址的一个副本,然后数组就被销毁了,即原数组所占用的内存空间,被系统回收,并告诉其他程序,这块内存大家可以用了,在函数外面得到的那个副本(和原数组的首地址一样)本身没有什么问题,但此时它所指向的空间,已经被系统回收了(即前面提到的标记可以被其它程序使用),所以是否还能获取原数组的内容就变得很不可靠了,因为如果被其它程序使用那块内容,数据肯定已经发生了变化,当然如果一直没有程序使用那块内存,你还是可以把数组中的内容读出来的,但这是一种非常不可靠的行为。
vector的底层数据结构是数组,当你用返回对象的方法返回vector时,vector会进行整个数组的拷贝,如果数组较大,那么效率是很低的。
所以,如果你要返回的vector是在函数内部new的,那么可以返回该vector的指针,这样的话你必须注意该vector的释放问题。
另外,由于vector的存储空间位置可能在插入删除的时候变化,所以要小心迭代器的失效等问题。
如果vector 里存的不是基本类型, 而是自定义类型的话,最好重写这个类的拷贝构造函数