c++之引用
来源:互联网 发布:全球最出名的程序员 编辑:程序博客网 时间:2024/05/21 14:56
引用与指针相似,但是不同。&是引用符,而不是取地址符,引用只是变量的另一个别名,他们指向同一个地址。引用是别名常量,只能进行初始化,而且初始化之后所引用的对象都不能再改变。可以通过引用来改变所绑定对象的值。
一、对变量的引用
#include<iostream>using namespace std;int main(){ int a=9; int &ra=a; cout<<"a:"<<a<<endl; cout<<"&a:"<<&a<<endl; cout<<"ra:"<<ra<<endl; cout<<"&ra:"<<&ra<<endl; int b=30; ra=b; ///ra只是拥有了b中存放的值,ra的地址并没有改变 cout<<"b:"<<b<<endl; cout<<"&b:"<<&b<<endl; cout<<"ra:"<<ra<<endl; cout<<"&ra:"<<&ra<<endl; ra=999; cout<<"a:"<<a<<endl; cout<<"b:"<<b<<endl; cout<<"ra:"<<ra<<endl; return 0;}
二、对对象的引用
#include<iostream>using namespace std;class A{public: int get(){return i;} void set(int x){i=x;}private: int i;};int main(){ A a; A &b=a;///对对象的引用 a.set(20); cout<<"a.get():"<<a.get()<<endl; cout<<"b.get():"<<b.get()<<endl; b.set(26); cout<<"a.get():"<<a.get()<<endl; cout<<"b.get():"<<b.get()<<endl; return 0;}
三、对堆中对象的引用
引用只是一个变量的别名,当堆中对象被删除之后,这个别名也不存在了。因为堆中都是匿名对象,所以引用不可用于堆中,但是引用可以是堆中指针的别名。一般不常用,因为堆中的指针没必要有别名。
#include<iostream>using namespace std;int main(){ int *p=new int; int &r=*p; *p=12; cout<<"*p:"<<*p<<endl; r=7; cout<<"r:"<<r<<endl; cout<<"*p:"<<*p<<endl; return 0;}
四、传递类中对象的几种方式
1.按值传递
#include<iostream>using namespace std;class A{public: A(){cout<<"执行构造函数,创建对象"<<endl;} A(A&){cout<<"执行复制构造函数创建副本"<<endl;} ~A(){cout<<"执行析构函数,删除对象"<<endl;}};A func(A one){//one的删除 return one;//析构函数one的副本,返回one,返回方式是按值返回,所以又创建one的副本,调用复制构造函数}//返回的临时对象没有被接收,所以在func结束时调用析构函数int main(){ A a; //创建对象 func(a);//按值传递,复制构造函数创建a的副本 return 0;}
在调用func函数时,由于是按值传递对象,所以程序调用复制构造函数创建了一个a的副本;此时程序跳转到func函数处,将a的副本传递给one。接着函数又返回one,由于返回方式是返回一个A类对象,也是按值返回,所以又创建一个one的临时副本。但这个临时副本没有被接收,所以丢弃。函数调用了三次析构函数,第一次是析构one的副本,第二次是析构a的副本,第三次是析构对象a。
由此可见,按值传递对象需要调用两次复制构造函数,两次析构函数,这对于程序的开销很大,所以下面的函数按地址来传参。
2.按址传递(返回类型为对象)
class A{public: A(){cout<<"执行构造函数,创建对象"<<endl;} A(A&){cout<<"执行复制构造函数创建副本"<<endl;} ~A(){cout<<"执行析构函数,删除对象"<<endl;}};A func(A *one)//用指针接收地址{ return *one;//*one读取的是对象a处的值,返回的是对象,返回方式是按值返回,所以此处调用复制构造函数创建临时对象;并在函数结束时析构该对象}int main(){ A a; func(&a);//传递对象a的地址 return 0;}
可以看到结果比上个程序少调用了一次复制构造函数和析构函数
3.按址传递对象(返回类型为指针)
class A{public: A(){cout<<"执行构造函数,创建对象"<<endl;} A(A&){cout<<"执行复制构造函数创建副本"<<endl;} ~A(){cout<<"执行析构函数,删除对象"<<endl;}};A *func(A *one){ return one;//返回指针}int main(){ A a; func(&a);//传递地址 return 0;}程序比上个结果少调用了一次析构函数和复制构造函数
但是使用指针还有一个弊端,那就是可以通过指针随意改变所指向地址的内容。假如不想让指针改变内容,就用const来加以限制。程序中
4.按别名传递对象(返回类型为对象)
#include<iostream>using namespace std;class A{public: A(){cout<<"构造函数在执行...\n";} A(A &p){cout<<"复制构造函数在执行...\n";} ~A(){cout<<"析构函数在执行...\n";} void set(int x){i=x;} int get(){return i;}private: int i;};A func(A &one){ return one;}int main(){ A a; a.set(99); func(a); A b; b=func(a); return 0;}
5.按别名传递对象(返回类型为别名)
返回类型是别名,并不是一个对象,引用不会调用复制构造函数
#include<iostream>using namespace std;class A{public: A(){cout<<"构造函数在执行..."<<endl;} A(A&){cout<<"复制构造函数在执行..."<<endl;} ~A(){cout<<"析构函数在执行..."<<endl;} int get(){return i;} void set(int x){i=x;}private: int i;};A &fun(A &one) ///返回类型为A &,one自动转换为函数的返回类型{ return one;}int main(){ A a; a.set(11); cout<<a.get()<<" "; A &p=fun(a); cout<<p.get()<<endl; p.set(90); cout<<a.get()<<" "<<p.get()<<endl; return 0;}
五、使用引用常见错误
假如对象不存在或被销毁,继续引用这个对象会出现什么后果?
#include<iostream>using namespace std;class A{public: A(int i){x=i;} int get(){return x;} void set(int i){x=i;}private: int x;};A & func(){ A a(23); //a为局部对象,生存期只在func函数中 return a; //func结束后a消失,返回的是不存在的对象的别名}int main(){ A &r=func(); //r接收的也是不存在对象的别名 cout<<r.get()<<endl; //输出结果为随机数 return 0;}去掉func函数中的&符号,为什么输出的不是随机值?因为此时函数返回的是一个对象的值,这是会调用复制构造函数创建对象的副本,这个副本赋给引用r,引用会延长副本的生存期至程序结束。r接收的是对象a的副本,这个程序验证了引用的是临时变量,临时变量的生存期不少于引用的生存期。但是指针就没有延长临时副本的功能
#include <iostream>using namespace std;class A{public: A(int i){cout<<"执行构造函数创建一个对象\n";x=i;} A(const A&a){x=a.x;cout<<"执行复制构造函数创建一个对象\n";} ~A(){cout<<"执行析构函数!\n";} int get() {return x;} void set(int i){x=i;}private:int x;};A func( ){ cout<<"跳转到func函数中!\n"; A a(23); cout<<"对象a的地址:"<<&a<<endl; return a; //返回对象a的副本}int main(){ A &r=func(); //引用延长了临时副本的生存期,结果中第一个析构函数析构的是对象a;最后才析构临时副本,说明引用延长 cout<<"对象a的副本的地址:"<<&r<<endl;<span style="font-family: Arial, Helvetica, sans-serif;">//了副本的生存期</span> cout<<r.get()<<endl;return 0;}
指针没有延长临时变量生存期的功能例如:
因为析构函数析构某个对象之后,只是告诉编译器这块内存不再为某个对象独占,别的对象可以访问并存放数据,但是在别的对象使用之前,这块内存中的数据并没有被删除,因此指针r访问该区域仍能够得到未被修改的值23。由运行结果可以看出,接连两次的析构函数,分别析构了对象a和临时副本,所以指针没有延长临时变量的生存期。
#include <iostream>using namespace std;class A{public: A(int i){cout<<"执行构造函数创建一个对象\n";x=i;} A(const A&a){x=a.x;cout<<"执行复制构造函数创建一个对象\n";} ~A(){x=0;cout<<"执行析构函数!\n";} int get() {return x;} void set(int i){x=i;}private:int x;};A func( ){ cout<<"跳转到func函数中!\n"; A a(23); cout<<"对象a的地址:"<<&a<<endl; return a;}int main(){ A *r=&func(); //当指针r接收临时对象之后,这个临时对象就被释放了,那么程序最后为什么能输出23呢? cout<<"对象a的副本的地址:"<<r<<endl; cout<<r->get()<<endl;return 0;}
六、引用堆中对象常见错误
r的地址就是堆中对象的副本的内存地址,不过这个副本是保存在栈中,在func函数结束时指针p被自动释放,因为指针生存期只在func函数中,但堆中的指针,只能由delete释放,所以p指针指向的空间找不到,造成了内存泄露。
#include <iostream>using namespace std;class A{public: A(int i){cout<<"执行构造函数创建一个对象\n";x=i;} A(const A&a){x=a.x;cout<<"执行复制构造函数创建一个对象\n";} ~A(){cout<<"执行析构函数!\n";} int get(){return x;}private:int x;};A func()//返回的是一个对象,按值返回{ cout<<"跳转到func函数中!"<<endl; A *p=new A(99); //创建一个堆中的对象 cout<<"堆中对象的地址:"<<p<<endl; return *p; //返回指针p指向的对象,但是指针p是局部对象,在func函数结束时被释放 //由此在堆中创建的内存产生泄露,r接收的是堆中对象的副本, //并不是p指向的对象本身}int main(){ A&r=func(); //用r接收返回的对象,引用会延长临时变量的生命 cout<<"堆中对象的副本的地址:<<"&r<<endl; cout<<r.get()<<endl; return 0;}
解决办法:返回别名
A &func(){ A *p=new A(99); cout<<"堆中对象的地址:"<<p<<endl; return *p; //返回堆中对象的别名}int main(){ A &r=func();//r作为*p的别名,也就是堆中对象的别名 cout<<"堆中对象副本的地址&r:"<<&r<<endl;//r的地址就是堆中对象的地址 cout<<r.get()<<endl; //使用r这个别名来访问get()函数 A*pa=&r;//将别名的内存地址,也就是堆中对象的地址赋给pa delete pa; //删除pa指向的堆中对象的内存空间 cout<<r.get()<<endl; //别名r成了个空引用 return 0;}
- C++Primer读书笔记之引用
- c之引用作为参数
- C语言之指针,数组,引用
- C常见问题之头文件引用
- objective-c基础之引用计数
- 《唐老师C++》之const引用
- c,c++易错点之指针与引用
- 黑马程序员-Object C之循环引用
- C语言之指针,数组,引用
- Objective-C内存管理之引用计数
- 【C++】:C++之C++的引用
- C与C++之引用与指针
- 引用(c++)
- C++----引用
- c++-引用
- C ++ 引用
- 【c++】引用
- 【c++】引用
- nyoj 免费馅饼 613 (DP状态转移方程)
- CF--B - Queries on a String
- Android Handler 消息机制
- IOS 给UIView 设置背景图片
- CoreData简单介绍
- c++之引用
- JDBC基本写法
- iOS 16进制颜色字符串转换成可以正常使用的UIColor
- 学习工具-学习网站等
- Object公用方法
- android 入门记录
- 文章标题
- JDBC典型应用-存储过程
- eclipse集成maven3后,创建java项目详细图解