zoukankan      html  css  js  c++  java
  • 别名的定义、传递、返回对象

    &的功能:

    (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<<"执行构造函数创建一个对象
    ";}
    	A(A&){cout<<"执行复制函数创建该对象的副本
    ";}
    	~A(){cout<<"执行析构函数删除该对象
    ";}
    };
    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<<"执行构造函数创建一个对象
    ";}
    	A(A&){cout<<"执行复制函数创建该对象的副本
    ";}
    	~A(){cout<<"执行析构函数删除该对象
    ";}
    };
    A* fun(A *one)
    {	
    	return one;	//第二次使用按址传递。返回的是地址
    }
    
    int main()
    {
    	A a;
    	fun(&a);		//第一次使用按址传递
    	return 0;
    }
    /**************************
    输出:
    执行构造函数创建一个对象
    执行析构函数删除该对象
    ****************************/
    

      

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

    #include <iostream>
    using namespace std;
    
    class A
    {
    	A(){cout<<"执行构造函数创建一个对象
    ";}
    	A(A&){cout<<"执行复制函数创建该对象的副本
    ";}
    	~A(){cout<<"执行析构函数删除该对象
    ";}
    };
    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<<"执行构造函数创建一个对象
    ";}
    	A(A&){cout<<"执行复制函数创建该对象的副本
    ";}
    	~A(){cout<<"执行析构函数删除该对象
    ";}
    };
    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<<"执行构造函数创建一个对象
    ";x=i;}
    	A(A&){cout<<"执行复制函数创建该对象的副本
    ";}
    	~A(){cout<<"执行析构函数删除该对象
    ";}
    	void set(int i){x=i;}
    	int get(){return x;}
    private:
    	int x;
    };
    A& fun(A&a)	//返回值是A对象的别名
    {	
    	cout<<"跳转到fun()函数中!
    ";
    	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
    执行析构函数删除该对象
    ***********************************************/
    

    解决了空引用问题.

  • 相关阅读:
    POJ 1082 博弈推规律
    博弈论总结
    POJ 2502 Subway 关键在建图
    【算法设计与数据结构】为何程序员喜欢将INF设置为0x3f3f3f3f?
    一位ACMer过来人的心得
    POJ1724 Dij队列优化邻接表
    Dijkstra队列优化矩阵版
    Uva247 Floyd传递闭包+map处理
    简爬新浪新闻网
    爬href链接
  • 原文地址:https://www.cnblogs.com/simonLiang/p/5903888.html
Copyright © 2011-2022 走看看