第九章 引用
第九章 引用
1. 什么是引用?
引用就是别名。int &ra=a;此处的&不是取址运算符,而是引用运算符
3. 引用就是别名常量
4. 引用对象
只能 int &ra=a;// 注意定义引用时要对其初始化
int &ra;
ra=a; //这样是错误的,引用就像常量,只能初始化不能赋值
对象也可以定义一个引用,但是类不能,因为它没有具体的内存地址
5. 空引用
指针删除后需要赋空,引用不需要
6. 按值传递
按值传递(传递的是副本)≠按地址传递()≠按别名传递(有什么不同?)
9.1.利用指针返回多值
9.2.用引用来返回多值
使用指针或者别名传递变量允许在被调函数体内改变原来的变量,因此可以借此返回多个函数值;
☆10. 按值传递对象
何时使用复制构造函数:按值传递对象(参数);函数(按值)返回对象(return);用一个对象初始化另一个对象即复制初始化时(初始化);根据元素初始化列表初始化数组元素。这四种情况都将调用复制构造函数。记住,复制构造函数只能用于初始化,不能用于赋值,赋值时不会调用复制构造函数,而是使用赋值操作符。
11. 按址传递对象
(避免调用复制构造函数的方法)
A *func(A *one);
int main()
{
A a;
func(&a);
cout<<func(&a)<<endl;
return 0;
}
A *func(A *one){ return one(地址); }
12. 使用const指针来传递对象
使用const为按址传递提供保护机制(类似实现了值传递的功能)
A const*const func(const A * const one)const{return one;}
const A * const p=func(&a);
a.set(44);
a.set(88);
每个const都有不同意思,还有const不管怎么加都不会修改实参变量的属性(不懂看视频)
int get()const { x=5;return x;}
这里const不能改变成变量的值
13. 按别名来传递对象
由于引用是常量不能被分配给另一个对象,所以与指针相比省了两个const
A const &func(const A &one)const{return one}
A& b=func(a);//在接受一个对象的别名时被接受方只能是别名,目的是为了避免调用复制构造函数
//A b=func(a);
14. 到底是使用引用还是指针
这是因为指针可以为空,但是引用不能为空。指针可以被赋值,但是引用只能被初始化。不可以被赋为另一个对象的别名,①如果你想使用一个对象记录不同变量的地址,那么你只能使用指针。
②另外,在堆中创建一块内存区域,必须要用指针来指向它,否则这块区域就会变成无法访问的内存空间。当然我们可以用引用来引用指向内存空间的指针。
☆指针与引用的区别:
指针可以为空,引用不能;
指针可以被赋值,引用不能;
指针可以指向堆中空间,引用不能;指针可以delete,引用不能;
int *&r=new int;//功能等价于 int *r=new int 但这样写是有问题的,当new int 返回NULL 别名r就有问题,因为别名不能被赋空,会导致系统崩溃
所以new一个对象是一定要用指针
15. 引用和指针可以一块声明
int *r,&ra=a;//只有一个指针
16. 引用容易犯的错误(重新看视频)
17. 调用一个按值返回的堆中对象
//return p后p被释放 但没有delete p 就不能删除p创建的堆中对象
//从而导致这个堆中对象不能再被访问 出现内存泄露
1 #include "iostream" 2 using namespace std; 3 class A 4 { 5 public: 6 A(int i){x=i;} 7 int get(){return x;} 8 private: 9 int x; 10 }; 11 A func(); 12 int main() 13 { 14 A rp=func(); 15 cout<<"对象rp的地址:"<<&rp<<endl; 16 cout<<rp.get()<<endl; 17 return 0; 18 } 19 A func() 20 { 21 A *p=new A(99); 22 cout<<"对象*p的地址:"<<p<<endl; 23 return *p;//按值返回,P开辟的堆中空间没有被释放 24 }
结果:对象rp与*p的的地址值不一样
18. 调用一个按别名返回的堆中对象
// r成了空引用 空引用是个隐蔽的杀手要尽量避免
1 #include "iostream" 2 using namespace std; 3 class A 4 { 5 public: 6 A(int i){x=i;} 7 int get(){return x;} 8 private: 9 int x; 10 }; 11 A &func(); 12 int main() 13 { 14 A &rp=func();//接收一个对象的别名时,被接收方只能是别面 15 cout<<"对象rp的地址:"<<&rp<<endl; 16 cout<<rp.get()<<endl; 17 A *pa=&rp; 18 delete pa;// r成了空引用 空引用是个隐蔽的杀手要尽量避免 19 pa=NULL; 20 return 0; 21 } 22 A &func() 23 { 24 A *p=new A(99); 25 cout<<"对象*p的地址:"<<p<<endl; 26 return *p;// 27 }
结果:对象rp与*p的的地址值一样
18.1 调用一个按指针返回的对象(可以用,但不方便阅读)
1 //本质上没有错误,只是用起来容易忘记删除func()中的指针 2 #include "iostream" 3 using namespace std; 4 class A 5 { 6 public: 7 A(int i){x=i;} 8 int get(){return x;} 9 private: 10 int x; 11 }; 12 A *func(); 13 int main() 14 { 15 A *rp=func();//接收一个对象的别名时,被接收方只能是别面 16 cout<<"对象rp的地址:"<<rp<<endl; 17 cout<<rp->get()<<endl; 18 delete rp; 19 rp=NULL; 20 return 0; 21 } 22 A *func() 23 { 24 A *p=new A(99); 25 cout<<"对象*p的地址:"<<p<<endl; 26 return p;// 27 } 28 //结果:对象rp与*p的的地址值一样
☆19. 在哪里创建,就在哪里释放
1 //为了避免混淆我们尽量在哪里new在哪里delete 2 #include "iostream" 3 using namespace std; 4 class A 5 { 6 public: 7 A(int i){x=i;} 8 int get(){return x;} 9 void set(int i){x=i;} 10 private: 11 int x; 12 }; 13 14 //按指针调用返回 15 //const A * const func(A *); 16 //int main() 17 //{ 18 // A *p=new A(99); 19 // cout<<p->get()<<endl; 20 // func(p); 21 // cout<<p->get()<<endl; 22 // delete p; 23 // p=NULL; 24 // return 0; 25 //} 26 //const A * const func(A *rp) 27 //{ 28 // rp->set(66); 29 // return rp;// 30 //} 31 32 33 //按别名调用返回 34 const A & func(A&); 35 int main() 36 { 37 A *p=new A(99); 38 cout<<p->get()<<endl; 39 func(*p); 40 cout<<p->get()<<endl; 41 delete p; 42 p=NULL; 43 return 0; 44 } 45 const A & func(A &rp) 46 { 47 rp.set(66); 48 return rp;// 49 }
本章总结:
1. 按值传递(传递的是副本)≠按地址传递≠按别名传递
2. 按值传递对象,会调用复制构造函数
按址传递对象,可以避免调用复制构造函数(引用,指针)(记得与const联用)
3. ☆何时调用复制构造函数
① 同类型对象对另一对象的初始化
② 按值传递对象
③ 按值返回对象
④ 初始化顺序容器中的元素
⑤ 根据元素初识化列表初始化数组元素
string strs[]={string(“tonnie”),string(“john”),string(“mark”)};
4. ☆const为按址传递提供保护机制(类似实现了按值传递的功能)
A const * const func(const A * const p)const{}
A const * & func(const A * & r)const{}
5. 指针与引用的区别
① 指针可以为空,引用不可以(p=NULL)
② 指针可以被赋值,引用不可以
③ 指针可以指向堆中空间,用于不可以
④ deldete不能对引用使用
int *&r=new int;//等价于 int *r=new int;
但是,当机子虚拟内存太小,new int会自动返回一个空指针。而引用不能为空(非常规型),所以会导致系统崩溃
6. ☆int* r,&ra=a; //只有一个指针
7. new和delete最好在一个函数体内(为了避免指针混淆,最好在哪创建在哪释放)