首页 > 代码库 > 别名的定义、传递、返回对象
别名的定义、传递、返回对象
&的功能:
(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执行析构函数删除该对象***********************************************/
解决了空引用问题.
别名的定义、传递、返回对象