别名的定义、传递、返回对象

&的功能:

(1)取地址符

(2)引用符

一、定义别名

定义变量的别名

如,int n ;

int &m =n;  //m是n 的别名  ,可以用int类型的m来表示int类型的n。且m与n的地址也一样。故m 和 n 是同一个东西!

定义对象的别名

如:Human Mike;

Human &rMike = Mike;  //rMike是Mike的别名

 二、空引用

指针进行删除之后,需要将它赋值为空,引用却不需要这样做。假如该对象存放在栈中,那么在对象超出作用域时别名会和对象一起消失;假如存放在堆中,由于对中内存只能通过指针来访问,因此用不着别名,即使再定义一个盖子真的别名,那么将指针删除并赋空之后,该指针的别名中的地址也相应的赋空了。

三、按别名传递

传递数值包括:按值传递,按址传递,按别名传递

关于变量:
(1)按值传递
#include <iostream>
using namespace std;
void swap(int a,int b)
{
	int t;
	t = a;
	a = b;
	b = t;
}
int main()
{
	int x,y;
	x = 3;
	y = 5;
	swap(x,y);
	cout<<"x:"<<x<<endl;
	cout<<"y:"<<y<<endl;
	return 0;
}

  输出:

x:3
y:5

 

不能成功交换x y的值!因为在swap函数里面交换的是x ,y 的副本,不是x y

按值传递在想函数传递一个变量/对象时,

(2)按址传递
#include <iostream>
using namespace std;
void swap(int *a,int *b)
{
	int t;
	t = *a;
	*a = *b;
	*b = t;
}
int main()
{
	int x,y;
	x = 3;
	y = 5;
	swap(&x,&y);
	cout<<"x:"<<x<<endl;
	cout<<"y:"<<y<<endl;
     return 0;
}

  输出:

x:5
y:3

可以成功交换x y的值!

(3)按别名传递
#include <iostream>
using namespace std;
void swap(int &a,int &b)
{
	int t;
	t = a;
	a = b;
	b = t;
}
int main()
{
	int x,y;
	x = 3;
	y = 5;
	swap(x,y);
	cout<<"x:"<<x<<endl;
	cout<<"y:"<<y<<endl;
	return 0;
}

  输出:

x:5
y:3

可以成功交换x y的值!

 

关于对象:

#include <iostream>
using namespace std;
class A
{
	A(){cout<<"执行构造函数创建一个对象\n";}
	A(A&){cout<<"执行复制函数创建该对象的副本\n";}
	~A(){cout<<"执行析构函数删除该对象\n";}
};
A fun(A one)
{
	return one;   // 3 fun函数又将接收到的副本返回了,由于返回方式也是按值返回,所以又要调用一个副本构造函数
}

int main()
{
	A a;      //  1   创建一个对象,调用一次构造函数
	fun(a); // 2 对象 a 按值传递到fun函数中,调用fun函数的副本,创建一个对象a的副本,然后将副本传递到 fun函数值中去
	return 0;
}
/***********************
输出:
执行构造函数创建一个对象    //1
执行复制函数创建该对象的副本      //2
执行复制函数创建该对象的副本      //3
执行析构函数删除该对象
执行析构函数删除该对象
执行析构函数删除该对象

*/

  按值传递在向函数传递一个对象时,会向传递变量那样建立一个该对象的拷贝,而从函数返回一个对象时,也要建立这个被返回的对象的一个拷贝!这就导致了,当对象所占内存空间很大的时候,在传递过程中每次都要复制一个,虽然当值返回给调用程序之后会删除该对象的复制品,也会浪费巨大的空间!!!

因此,可以将程序改成按址传递

#include <iostream>
using namespace std;
class A
{
public:
	A(){cout<<"执行构造函数创建一个对象\n";}
	A(A&){cout<<"执行复制函数创建该对象的副本\n";}
	~A(){cout<<"执行析构函数删除该对象\n";}
};
A* fun(A *one)
{	
	return one;	//第二次使用按址传递。返回的是地址
}

int main()
{
	A a;
	fun(&a);		//第一次使用按址传递
	return 0;
}
/**************************
输出:
执行构造函数创建一个对象
执行析构函数删除该对象
****************************/

  

但是问题来了:使用了指针去指向对象,那不是指针也可以修改对象啦!!要是它不小心用于非法怎么办?我们来用const避免他:

#include <iostream>
using namespace std;

class A
{
	A(){cout<<"执行构造函数创建一个对象\n";}
	A(A&){cout<<"执行复制函数创建该对象的副本\n";}
	~A(){cout<<"执行析构函数删除该对象\n";}
};
const *const A fun(const A *const one)	//保证传递进来的数据不被修改,又保证了返回的数据不被修改
{	
	return one;	//第二次使用按址传递。返回的是地址
}

int main()
{
	A a;
	const A *const p = fun(&a);	//第一次使用按址传递  	const 需要匹配 fun函数的输入
	return 0;
}

或者用别名传递

#include <iostream>
using namespace std;
class A
{
	A(){cout<<"执行构造函数创建一个对象\n";}
	A(A&){cout<<"执行复制函数创建该对象的副本\n";}
	~A(){cout<<"执行析构函数删除该对象\n";}
};
const *const A fun(const A & one)	//保证传递进来的数据不被修改,又保证了返回的数据不被修改
{	
	return one;	//第二次使用按址传递。返回的是地址
}
int main()
{
	A a;
	const A & b = fun(a);	//第一次使用按址传递  	const 需要匹配 fun函数的输入
	return 0;
}

 此方法将函数的返回值和接收参数都定义为const,就可以保证函数内不可修改原始值,同时避免利用返回值对原始值进行修改。

******************************************************

使用指针还是使用别名呢?

(1)指针可以为空,但引用不能为空,指针可以被赋值,但引用只可以被初始化,不可被复位另一个对象的别名。如果需要使一个变量记录不同对象的地址,必须用指针!

(2)在中创建一块内存区域,必须要用指针才能指向该块区域!当然我们也可以用引用来引用指向内存空间的指针(没必要!!)...

如: int * &a =new int;        

*r = 3;

这样的写法容易出错!!当机器虚拟内存太小,无法创建新空间的情况下,那么new int会自动返回一个空指针。 因此会导致一个无用 的别名。而使用 '*' 读取一个无用的别名则会引起系统奔溃!!

--->解决办法是,不要将引用初始化为新建内存区域的别名,而是要将 a 初始化为指向该区域的指针的别名。前提是首先判断该指针不为空。更多的时候,一般不给指针创建别名。

******************************************************

 

四、按别名返回堆中对象

需要改变对象中的数据时:

#include <iostream>
using namespace std;
class A
{
public:
	A(int i){cout<<"执行构造函数创建一个对象\n";x=i;}
	A(A&){cout<<"执行复制函数创建该对象的副本\n";}
	~A(){cout<<"执行析构函数删除该对象\n";}
	void set(int i){x=i;}
	int get(){return x;}
private:
	int x;
};
A& fun(A&a)	//返回值是A对象的别名
{	
	cout<<"跳转到fun()函数中!\n";
	a.set(66);
	return a;	//返回的是A的对象的别名(按别名返回) 
}
int main()
{
	A *p = new A(99);	//堆中创建了一个追踪对象A(99),用p指向它 
	fun(*p);	//将这个追踪对象传递进去 
	cout<< p->get()<<endl;
	delete p;		 //删除了追踪对象指针 p
	return 0;
}
/*************************************************
输出:
执行构造函数创建一个对象
跳转到fun()函数中!
66
执行析构函数删除该对象
***********************************************/

解决了空引用问题.

 

posted @ 2016-09-24 19:17  SimonLiang  阅读(1219)  评论(0编辑  收藏  举报