转载:关于函数返回值的几种情况
来源:互联网 发布:手机移动数据怎么关闭 编辑:程序博客网 时间:2024/05/24 07:43
关于函数返回值的几种情况
在一个函数的内部,return的时候返回的都是一个拷贝,不管是变量、对象还是指针都是返回拷贝,但是这个拷贝是浅拷贝。
分为以下几种情况:1)返回一个基本类型的变量;2)返回非动态分配的指针;3)返回动态分配的指针;4)返回非基本类型(对象等)…
1. 如果返回一个基本类型的变量,比如:
int a;
a = 5;
return a;
那么就会a的一个拷贝,即5返回,然后a就被销毁了。尽管a被销毁了,但它的副本5还是成功地返回了,所以这样做没有问题。
2. 但是对于非动态分配(new/malloc)得到的指针,像1那么做就会有问题,比如在某个函数内部:
int a[] = {1, 2};
return a;
那么也会返回指针a的一个拷贝,我们假定a的地址值为0x002345FC,那么这个0x2345FC是能够成功返回的。当return执行完成后,a就要被销毁,也就是0x002345FC所指向的内存被回收了。如果这时候在函数外面,去地址0x002345FC取值,那得到的结果肯定是不对的。这就是为什么不能返回局部指针的原因。返回局部变量的引用的道理和这个类似。注意:不能返回局部变量的指针或引用!
注意:《高质量程序设计指南中C++/C语言》中指出:return语句不可返回“堆栈内存”的指针或者“引用”,因为该内存单元在函数体结束时就被自动释放。
强调举例一下,看下面的程序:
- #include<iostream>
- using namespace std;
- char *GetMemory(void) // char *GetMemory(char *p,int num)
- {
- char p[ ] = “hello world”; // p= (char *)malloc(sizeof(char) *num);
- return p;
- }
- int main ()
- {
- char *str = NULL;
- str = GetMemory() ; //str = GetMemory(str,100) ;
- cout << str ;
- return 0;
- }
#include<iostream>using namespace std;char *GetMemory(void) // char *GetMemory(char *p,int num){char p[ ] = "hello world"; // p= (char *)malloc(sizeof(char) *num);return p; }int main (){ char *str = NULL; str = GetMemory() ; //str = GetMemory(str,100) ; cout << str ; return 0;}
答案,可能是乱码,也可能是正常输出。因为GetMemory返回的是指向“栈内存”的指针,该指针的地址不是NULL,但其原来的内容已经被清除,新内容不可知。
为什么不是输出的是数组的首地址呢??如果函数是黑体部分的话,可以利用函数返回值来传递动态内存。那是因为注释部分是堆操作,不是会被os自动释放的栈内存。
但是若用非黑体的部分怎么就不行了呢?栈内存就是编译器自动支配的,也就是说非动态分配的东西基本上都在栈中
这些问题可以用下面的例子回答:
- int testA (void)
- {
- int b = 1 ;
- return b ;
- }
- char * testB (void)
- {
- char str[] = “abc” ;
- return str ; //但这样的方法是不推荐的
- }
- int main()
- {
- printf( ” the value of testA is %d \n”, testA() ) ;
- printf( ” the value of testB is %c ”, *( testB() ) ) ;
- }
int testA (void) { int b = 1 ; return b ; }char * testB (void) { char str[] = "abc" ; return str ; //但这样的方法是不推荐的} int main() { printf( " the value of testA is %d \n", testA() ) ; printf( " the value of testB is %c ", *( testB() ) ) ; }
对于返回值的情况:
testA与main函数同在栈区,testA结束时C++创建临时变量,然后将返回值复制给该临时变量。printf( ” the value of testA is %d \n”, testA() ) 时输出的是该临时变量的值,testA中的b已经不存在。
对于返回指针的情况:这是最复杂的部分。首先,对于上面的情形:返回一个数组的首地址,由于是返回char *类型,所以C++会首先创建一个char *类型的临时变量,再把该数组的首地址赋给临时变量;函数结束后该数组也就被销毁,这就意味着临时变量指向了一个“未声明的地址”,幸运的情况下,这段内存暂时还没有被其他的数据所覆盖,因此还能输出正确的内容。在testB里面,如果换成char* str=”abc”;return str; 由于这时str指向的是全局数据区的一段内存地址,所以函数结束后临时变量也指向该地址,所以编译器不会提出警告。但这样的方法是不推荐的。
返回引用:这中情况的效率最高,它直接返回一个对象,不产生返回值的副本。但同时也要注意避免返回局部引用的情况。
3. 对于返回(动态分配得到的)指针的另外一种情况,比如在函数内部:
int a = new int(5);
return a;
这样做是可以的。return a执行完后,a并没有被销毁(必须要用delete才能销毁a),所以这里返回的a是有效的。
4. 如果不是基本数据类型,比如:
class A
{
public:
OtherClass * …
};
如果在某个函数内部有一个A类的局部变量,比如:
A a;
return a;
这时候也会返回a的一个拷贝,如果A没有写深拷贝构造函数,就会调用缺省的拷贝构造函数(浅拷贝),这样做就会失败的;
如果A中提供了深拷贝构造函数,则这样做就是可以的。
实验代码如下:
- #include <iostream>
- using namespace std;
- int some_fun1()
- {
- cout << ”\nsome_fun1()…” << endl;
- int a = 5;
- return a; //OK
- }
- int* some_fun2()
- {
- cout << ”\nsome_fun2()…” << endl;
- int a = 5;
- int *b = &a;
- return b; // not OK
- }
- int* some_fun3()
- {
- cout << ”\nsome_fun3()…” << endl;
- 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
- {
- 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;
- }
- private:
- CSomething* sth; // 以指针形式存在的成员变量
- };
- CA some_fun4()
- {
- cout << ”\nsome_fun4()…” << endl;
- CSomething c(1, 2);
- CA a(&c);
- cout << ”\\ some_fun4()…” << endl;
- 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;
- }
- <img src=”http://img.my.csdn.net/uploads/201211/10/1352544421_7259.jpg” alt=“”>
#include <iostream>using namespace std;int some_fun1(){ cout << "\nsome_fun1()..." << endl; int a = 5; return a; //OK}int* some_fun2(){ cout << "\nsome_fun2()..." << endl; int a = 5; int *b = &a; return b; // not OK}int* some_fun3(){ cout << "\nsome_fun3()..." << endl; 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{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; }private: CSomething* sth; // 以指针形式存在的成员变量};CA some_fun4(){ cout << "\nsome_fun4()..." << endl; CSomething c(1, 2); CA a(&c); cout << "\\ some_fun4()..." << endl; 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;}
参考了网上部分资源!
- 转载:关于函数返回值的几种情况
- 关于函数返回值的几种情况
- 关于函数返回值的几种情况
- 关于函数返回值的几种情况
- 关于函数返回值的几种情况
- 关于函数返回值的几种情况
- 关于函数返回值的几种情况
- 关于函数返回值的几种情况
- 关于函数返回值的几种情况
- 关于函数返回值的几种情况
- 关于函数返回值的几种情况
- 关于函数返回值的几种情况
- 关于函数返回值的几种情况
- 关于函数返回值的几种情况
- 关于函数返回值的几种情况
- 关于函数返回值的几种情况
- 关于函数返回值的几种情况
- 关于函数返回值的几种情况
- 统计学习方法笔记(8)——提升方法之前向分布算法和提升树
- 欢迎使用CSDN-markdown编辑器
- JSP(1)
- dpdk 在VMware 中的安装部署
- DFS——HDU1010(经典的奇偶剪枝)
- 转载:关于函数返回值的几种情况
- L3-005. 垃圾箱分布
- Linux下安装jdk(openSUSE为例)
- 5.CAS增加验证码
- hdu1010 dfs+剪枝
- window 命令行运行的java 和 javac 显示的版本不同
- Quartz入门例子简介 从入门到菜鸟(四)
- MySQL 手动设置自动递增伟某一值
- UGUI背包(二)鼠标移动会显示信息框,拖拽物品