函数千万不要返回局部对象的引用或指针
局部变量在函数里面,当函数执行结束后将释放局部变量,如果返回引用或批针这个时候引用或指针指向所指向的内存空间已经释放。指针和引用将是垂悬指针。很危险!
但是如果返回的“局部变量”是堆中的内存值就可以返回了
C++函数为什么要使用引用?
C语言之中大量利用指针作为形参或者函数返回值,这是由于值拷贝会有很大的消耗(比如传入传出一个大的结构体)。所以在C++之中使用引用作为函数参数和返回值的目的和使用指针是一样的。而且形式上更加直观,所以C++提倡使用引用。
1)返回非引用类型:函数的返回值用于初始化在跳出函数时候创建的临时对象。用函数返回值来初始化临时对象与用实参初始化形参的方法是一样的。如果返回类型不是引用的话,在函数返回的地方,会将返回值复制给临时对象。且其返回值既可以是局部对象,也可以是表达式的结果。
2)返回引用:当函数返回引用类型的时候,没有复制返回值,而是返回对象的引用(即对象本身)。
函数返回引用:实际上是一个变量的内存地址,既然是内存地址的话,那么肯定可以读写该地址所对应的内存区域的值,即就是“左值”,可以出现在赋值语句的左边。
3) 函数返回引用的时候,可以利用全局变量(作为函数返回),或者在函数的形参表中有引用或者指针(作为函数返回),这两者有一个共同点,就是返回执行完毕以后,变量依然存在,那么返回的引用才有意义。
1、引用其实本质就是地址
2、当函数返回值类型为引用时,一般就用引用类型去接收,或者就使用了引用的作用,如果用非引用类型接受,就等于将函数返回的引用的数据值,复制给了该接收对象,和函数返回非引用类型是一样的效果。
3、当函数返回值类型为引用时,如果不考虑接收问题,则有一个特性,则是可以直接操作该函数返回的引用,如放在=左面 +=等.
小结:
(1)使用引用当作函数参数和返回值,效率更高。
(2)函数返回的对象引用,必须在调用函数前就已经存在,不允许返回局部变量的引用!
(3)当不希望返回的对象被修改的时候,可以添加const。
- class Node
- {
- public:
- double a;
- int b;
- char c;
- Node(double a=0,int b=0,char c=0)
- {
- this->a=a;
- this->b=b;
- this->c=c;
- }
- //拷贝构造函数
- //Node(const Node &n)
- //{
- // a=n.a;
- // b=n.b;
- // c=n.c;
- //}
- };
- Node& GetNode()
- {
- Node str(10.0,2,‘c‘);
- return str;
- }
- int main()
- {
- Node newnode=GetNode();
- cout<<newnode.a<<" "<<newnode.b<<" "<<newnode.c<<endl;
- return 0;
- }
在上面的代码中,最后能够输出正确的值,然而在函数GetNode()中,str是一个局部的对象,内存空间在栈上,当函数退出时,str的内存空间被回收,这是在高级语言的层面上讲的。但是为什么最后的结果是正确的?原因就是Node newnode=GetNode();这句调用的是默认的拷贝构造函数,如果是自己重新重写拷贝构造函数,而不用默认的拷贝构造函数,那么这段代码就绝对有问题,最后输出的是未定义的数。
然而,为什么用默认的拷贝构造函数就能够正确的输出最后的值呢?
通过调用这段代码的反汇编我们可以看到:
Node newnode=GetNode();
013215FE call GetNode (1321019h)
01321603 mov ecx,dword ptr [eax] //eax调用GetNode 函数返回的栈的栈顶指针
01321605 mov dword ptr [newnode],ecx
01321608 mov edx,dword ptr [eax+4]
0132160B mov dword ptr [ebp-10h],edx //这句为double的a数据成员赋值
0132160E mov ecx,dword ptr [eax+8]
01321611 mov dword ptr [ebp-0Ch],ecx //这句为int的b数据成员赋值
01321614 mov edx,dword ptr [eax+0Ch]
01321617 mov dword ptr [ebp-8],edx //这句为char的c数据成员赋值
cout<<newnode.a<<" "<<newnode.b<<" "<<newnode.c<<endl;
也就是说直接调用默认的拷贝构造函数(或者默认的赋值函数),函数返回,在函数中的局部变量的销毁,其实在栈中只是栈顶的指针的移动,仍然还是可以为其赋值的。
然而:
Node &newnode=GetNode();
cout<<newnode.a<<" "<<newnode.b<<" "<<newnode.c<<endl;//能够输出正确的值
cout<<newnode.a<<" "<<newnode.b<<" "<<newnode.c<<endl;//不能输出正确的值
cout<<&newnode<<endl;
对于代码,newnode是对GetNode函数的引用,当函数结束时,其实是已经吧局部对象str销毁了,但是由于输出的结果仍然还在堆栈中,所以第一个能够输出正确的值,第二个是由于堆栈已经被其他的数覆盖,所以不能输出正确的值,而且这个对象也不能为其赋值,因为它的内存空间已经不存在了。