面试集锦--函数的按引用返回与按地址返回

函数值按引用返回常见的一个错误:

先来看一个错误代码:

#include<iostream>
using namespace std;
class A
{
private:
	int a;
public:
	A(int num):a(num){cout<<"构造函数!"<<endl;}
	A(A &b)
	{
		a=b.a;
		cout<<"复制构造函数!"<<endl;
	}
	~A()
	{
		cout<<"析构函数"<<endl;
	}
	void Setkey(int num)
	{
		a=num;
	}
	int GetKey()
	{
		return this->a;
	}
};
//按引用返回,但是a是局部变量,因此超出fun函数作用域之后
//a就消失了,所以b是一个不存在的对象的别名,所以程序输出随机数
A&fun()
{
	A a(3);
	return a;
}
int main()
{
	A &b=fun();
	cout<<b.GetKey()<<endl;
	return 0;
}

该代码输出随机数字,而不是3,原因如代码中注释所说,按引用返回,但是a是局部变量,因此超出fun函数作用域之后a就消失了,所以b是一个不存在的对象的别名,所以程序输出随机数
我们可以做如下修改:去掉A&fun()中的&:

#include<iostream>
using namespace std;
class A
{
private:
	int a;
public:
	A(int num):a(num){cout<<"构造函数!"<<endl;}
	A(A &b)
	{
		a=b.a;
		cout<<"复制构造函数!"<<endl;
	}
	~A()
	{
		cout<<"析构函数"<<endl;
	}
	void Setkey(int num)
	{
		a=num;
	}
	int GetKey()
	{
		return this->a;
	}
};
//函数按值返回,必定会调用类的复制构造函数构造一个对象a的副本
//在main函数中定义一个对象作为副本的别名,而对于引用而言,如
//果引用的是一个临时变量,那么这个临时变量的生存期不少于引用的
//生存期。也就是说main函数中的副本会在b这个别名生存期结束
//之后才消失。
A fun()
{
	A a(3);
	return a;
}
int main()
{
	A &b=fun();
	cout<<b.GetKey()<<endl;
	return 0;
}


函数的输出结果是:

构造函数!
复制构造函数!
析构函数
3
析构函数
请按任意键继续. . .

正如代码中的注释所说,

函数按值返回,必定会调用类的复制构造函数构造一个对象a的副本在main函数中定义一个对象作为副本的别名,而对于引用而言,如引用的是一个临时变量,那么这个临时变量的生存期不少于引用的生存期。也就是说main函数中的副本会在b这个别名生存期结束之后才消失。

 

下面看一个利用按引用返回和按地址返回来解决内存泄露的问题:

#include<iostream>
using namespace std;
class A
{
private:
	int a;
public:
	A(int num):a(num){cout<<"构造函数!"<<endl;}
	A(A &b)
	{
		a=b.a;
		cout<<"复制构造函数!"<<endl;
	}
	~A()
	{
		cout<<"析构函数"<<endl;
	}
	void Setkey(int num)
	{
		a=num;
	}
	int GetKey()
	{
		return this->a;
	}
};
A fun()
{
	A *a=new A(3);
	return *a;
}
int main()
{
	A &b=fun();
	cout<<b.GetKey()<<endl;
	return 0;
}
该段代码虽然能够正确的输出结果但是却会导致内存泄露。在fun函数中在堆中开辟空间,用指针a来指向该空间,但是指针式临时变量,超出fun函数后就消失,所以开辟的空间无法消除。解决的方法是按引用传递或按地址传递。

按 引用传递的方法代码如下:

#include<iostream>
using namespace std;
class A
{
private:
	int a;
public:
	A(int num):a(num){cout<<"构造函数!"<<endl;}
	A(A &b)
	{
		a=b.a;
		cout<<"复制构造函数!"<<endl;
	}
	~A()
	{
		cout<<"析构函数"<<endl;
	}
	void Setkey(int num)
	{
		a=num;
	}
	int GetKey()
	{
		return this->a;
	}
};
A &fun()
{
	A *a=new A(3);
	return *a;
}
int main()
{
	A &b=fun();
	cout<<b.GetKey()<<endl;
	delete &b;
	return 0;
}

但是按引用传递会出现一个问题,就是出现空引用,因为b指向的内存已经被删除。

按地址传递的方法如下:

#include<iostream>
using namespace std;
class A
{
private:
	int a;
public:
	A(int num):a(num){cout<<"构造函数!"<<endl;}
	A(A &b)
	{
		a=b.a;
		cout<<"复制构造函数!"<<endl;
	}
	~A()
	{
		cout<<"析构函数"<<endl;
	}
	void Setkey(int num)
	{
		a=num;
	}
	int GetKey()
	{
		return this->a;
	}
};
A *fun()
{
	A *a=new A(3);
	return a;
}
int main()
{
	A *b=fun();
	cout<<b->GetKey()<<endl;
	delete b;
	return 0;


这两种做法都不合理,容易忽略堆中的内存删除,所以最好是在哪里开辟空间在哪里撤销空间。对本代码来说,可以在main函数中申请空间,然后通过传递参数的方式传到fun函数中,在main的末尾进行delete。代码如下:


#include<iostream>
using namespace std;
class A
{
private:
	int a;
public:
	A(int num):a(num){cout<<"构造函数!"<<endl;}
	A(A &b)
	{
		a=b.a;
		cout<<"复制构造函数!"<<endl;
	}
	~A()
	{
		cout<<"析构函数"<<endl;
	}
	void Setkey(int num)
	{
		a=num;
	}
	int GetKey()
	{
		return this->a;
	}
};
void fun(A &a)
{
	a.Setkey(99);

}
int main()
{

	A *b=new A(23);
	fun(*b);
	cout<<b->GetKey()<<endl;
	delete b;
	return 0;
}


你可能感兴趣的:(面试,delete,fun)