// 1 什么是引用 //引用也是C++的初学者比较容易迷惑的,它几乎拥用指针所有的功能,但是语法更加简单 //本单我们就来不官瘾引用,并且分清它与指针的区别 /* #include <iostream> using namespace std; int main() { int num; //mum是num的别名,这两个变量是一个变量,只不过 名字不同而已 //这就好像李四有个名号叫李大嘴,大家称呼李四指的是李四这个人,称呼李大嘴也是指李四这个人 //李四和李大嘴都是一个人,只是名字出现了不同 //所以,我们对mum的操作实际就是对num的操作 //这里要注意事项的是,别名mum前面的符号&不是取址运算符,而是引用运算符,虽然它们符号相同,但是功能却不一样 int &mum = num; //定义一个int类的引用mum,这里请注意,&mum是一个定义引用的符号,页不是取地址运算符 num = 99; cout<<"mum:"<<mum<<endl; mum = 0; cout<<"num:"<<num<<endl; return 0; }*/ //第二节 引用的地址 /* #include <iostream> using namespace std; int main() { int a; int &ra = a; a = 999; cout<<"&a:"<<&a<<endl; cout<<"&ra:"<<&ra<<endl; //引用和变量的地址都是一样的 return 0; } */ // 3 引用就是别名常量 /* 引用就象中国古代的女人一样,一旦嫁给某人,就要跟他一辈子,因此假如你定义了某个变量的别名 那该别名就永远属于这个变量,它会忠习耿耿的跟随该变量,即使中间有别的变量来收买它,他也不会更换 自已的主人,不过它会收下该变量的金钱,从而导致它的主人也牵连 */ /* #include <iostream> using namespace std; int main() { int a; int &ra = a; a = 99; cout<<"&a:"<<&a<<endl; //99 cout<<"&ra:"<<&ra<<endl;//99 int b = 88; ra = b; cout<<"&a:"<<&a<<endl; cout<<"&ra:"<<&ra<<endl; cout<<"&b:"<<&b<<endl; cout<<"a:"<<a<<endl; //88 cout<<"ra:"<<ra<<endl;//88 cout<<"b:"<<b<<endl; //88 ra = 1; cout<<"a:"<<a<<endl; //1 cout<<"ra:"<<ra<<endl; //1 cout<<"b:"<<b<<endl; //88 return 0; }*/ // 4 引用对象 /* #include <iostream> using namespace std; class Human { public: int get(){ return i;} void set(int x){ i = x;} private: int i; }; int main() { Human Mike; Human &rMike = Mike; rMike.set(111); cout<<rMike.get()<<endl; int a; int &ra = a; //请注意,这里在引用初始化时,必须给引用赋值 //int &s; //这样是错误的,引用就像常量,只能对其衩始化,不能赋值 //s = a; return 0; } */ // 5 空引用 /* 我们知道指针进行删除扣作后,需要便宜它们赋为空,引用却不需要这么做,这是因为引用原来的对象的别名 假如该对象存放在栈中,那么在对象超出作用域时别名会和对象一起消失,假如该对象存放在堆中, 由于堆中内存空间必须使用指针来访问,因此用不着别名,即使再定义一个该指针的别名,那么指针删除并赋空之后 该指钍的别名中的地址也相应的赋空了 */ //第六节 按值传递 /* #include <iostream> using namespace std; void swap(int a, int b) { int c; cout<<"swap函数中,交换前, a:"<<a<<", b:"<<b<<endl; // 1 9 c = a; a = b; b = c; cout<<"swap函数中,交换后, a:"<<a<<", b:"<<b<<endl; // 9 1 } int main() { int a = 1, b = 9; cout<<"中程序中,交换前, a:"<<a<<", b:"<<b<<endl; // 1 9 swap(a,b); cout<<"中程序中,交换后, a:"<<a<<", b:"<<b<<endl; // 1 9 return 0; } */ /* 这到低是怎么因事呢? 假如说wwap函数没有效换主程序中的a和b,那么它交换的到底是谁的值呢? 这个问题看起来复杂,其实很简单,swap函数交换的是main函数中a和b的副本的值 也就是说main函数中定义的a和b的备份的值 swap函数交换的是main函数中的a和b的副本,而不是a和b本身 那么为什么swap函数不直接交换a和b的本身,却去交换它们的副本的值呢 这是因为当我们直接将a和b传递给swap函数时,这样的传递方式是按值传递 假如将a和b按值传递给swap函灵敏,那么编译器会自动在栈中创建a和b的拷贝,然后将a和b的拷由传递 给swqp函数,在swap函数中对a和b的拷贝进行交换,因此我们看到的输出语句,a和b确实进行了交换,只不过交换的是a和b的副本 */ //7 按址传递 /* #include <iostream> using namespace std; void swap(int *a, int *b) { int c; cout<<"没有交换值之前, a:"<<*a<<", b:"<<*b<<endl; c = *a; *a = *b; *b = c; cout<<"没有交换值之后, a:"<<*a<<", b:"<<*b<<endl;s } int main() { int a = 1, b = 9; cout<<"中程序中,交换前, a:"<<a<<", b:"<<b<<endl; // 1 9 swap(&a,&b); cout<<"中程序中,交换后, a:"<<a<<", b:"<<b<<endl; // 1 9 return 0; } */ //8 按别名传递 /* #include <iostream> using namespace std; //这里接别外传递 void swap(int &a, int &b) { int c; cout<<"没有交换值之前, a:"<<a<<", b:"<<b<<endl; c = a; a = b; b = c; cout<<"没有交换值之后, a:"<<a<<", b:"<<b<<endl; } int main() { int a = 1, b = 9; cout<<"中程序中,交换前, a:"<<a<<", b:"<<b<<endl; // 1 9 swap(a,b); cout<<"中程序中,交换后, a:"<<a<<", b:"<<b<<endl; // 1 9 return 0; } */ // 第九节 让函数返回多个值 利用指针返回多值 /* #include <iostream> using namespace std; int func(int a, int *b, int *c); int main() { int a=1,b=2, c=3; cout<<"主程序中,执行前......"<<endl; cout<<"a:"<<a<<", b:"<<b<<", c:"<<c<<endl; func(a,&b,&c); //我们也可以把a作为返回的判断值,把*b和*c作为运算的返回值,用该种方法我们可以实现汇报执行程序时的非法操作信息 cout<<"主程序中,执行后......"<<endl; cout<<"a:"<<a<<", b:"<<b<<", c:"<<c<<endl; return 0; } int func(int a, int *b, int *c) { cout<<"计算前: a:"<<a<<", b:"<<*b<<" c:"<<*c<<endl; a= a+1; *b = (*b) * (*b); *c = (*c) * (*c) * (*c); cout<<"计算后: a:"<<a<<", b:"<<*b<<" c:"<<*c<<endl; return a; }*/ /* #include <iostream> using namespace std; int func(int a, int *b, int *c); int main() { int a, b, c; int check; cout<<"请输入一个数字:"<<endl; cin>>a; check = func(a,&b,&c); if(check){ cout<<"你输入的数字不合法!"<<endl; }else{ cout<<"圆的面积是:"<<b<<endl; cout<<"正方形的面积是"<<c<<endl; } return 0; } int func(int a, int *b, int *c) { if(a > 20000) { a = 1; }else{ *b = a*a*3.14; *c = a*a; a=0; } return a; }*/ //但是我们要注意怕是,这两个值不是通过返回机制来得到的,而是通过改变函数 //指针参数*b 和 *c所指向的内存区域中的值来实现的 //第九节 用引用来返回多个值 /* #include <iostream> using namespace std; int func(int a, int &b, int &c); int main() { int a, b, c; int check; cout<<"请输入一个数字:"<<endl; cin>>a; check = func(a,b, c); if(check) { cout<<"你输入的数字不合法!"<<endl; } else{ cout<<"圆的面积是:"<<b<<endl; cout<<"正方形的面积是"<<c<<endl; } return 0; } int func(int a, int &b, int &c) { if(a>20000) { a=1; }else{ b = a*a*3.14; c = a*a; a=0; } return a; } */ // 10 按值传递对像 /* 从前面几节我们了解了按地址与按值传递的区别,按址传递可以修改原始变量的值,按值传递由于是 传递的原始变量的副本,因此它不能修改原始变量的值 假如仅仅是传递变量的话,采用指钍或者引用这种按址传递方式的优势不是很明显,但是假如是传递较大的对象的话,这种优势是相当明显的 这是因为,按值传递在向函数传递一个对象时,会象传递变量那样建立一个该对象的拷贝,而从函数返回一个对象时,也是建立这个被返回的对象的一个拷贝 然后,按值传递所付出的开销远不止如此,由于在传递过程中需要复制对象,因此会默认用复制构造函数 该函数的作用就是创建某个对象的临时拷拷贝都会自动调用复制构造函数即可 而当函数返回时,传递该对象时该对象的副本会被删除,这时候又会自动调用该对象的析构函烽 来释放内存,假设返回的仍然是该对象,并且仍旧采用按值传递的方式,那么就不会调用复制构造函数 建立一个该对象的监时副本,当该值被成功返回给调用程序后,然后再调用该对象的析构函数删除监时拷贝并释放内存 */ /* #include <iostream> using namespace std; class A { public: A(){ cout<<"构造函数执行..."<<endl;} A(A&){ cout<<"复制构造函数执行..."<<endl;} ~A(){ cout<<"析构函数执行..."<<endl;} }; //这里定义一个返回A类型的函数func,它的一个参数为A 类型的one A func(A one) { return one; } int main() { A a; //在这定义了一个类A的对象a //创一个对象会自动调用构造函数 //由于构造函数中加入了一条输出信息 //通过这个程序我们就可以看到,将一个对象按值传递给一个函数 //会调用两次复制构造函数和两次析构函数 func(a); return 0; } */ // 11 按地址传递对像 /* #include <iostream> using namespace std; class A { public: A(){ cout<<"构造函数执行..."<<endl;} A(A&){ cout<<"复制构造函数执行..."<<endl;} ~A(){ cout<<"析构函数执行..."<<endl;} }; //这里定义一个返回A类型的函数func,它的一个参数为A 类型的one //A func(A *one) A* func(A *one) { //return *one; //这里是读取one的值,按值返回的话就会调用复制构造函数 return one; } int main() { A a; //在这定义了一个类A的对象a //创一个对象会自动调用构造函数 //由于构造函数中加入了一条输出信息 //通过这个程序我们就可以看到,将一个对象按值传递给一个函数 //会调用两次复制构造函数和两次析构函数 func(&a); return 0; } */ //12使用const指针来传递对像 /* 按址传递对象虽然可以避免调用复制构造函数和析构函数,但是由于它得到了该对象的内存地址,可以 随时修改对象的数据,所以它实际上是破坏了按值传递的保护机制 */ /* #include <iostream> using namespace std; class A { public: A(){ cout<<"构造函数执行..."<<endl;} A(A&){ cout<<"复制构造函数执行..."<<endl;} ~A(){ cout<<"析构函数执行..."<<endl;} void set(int i){ x = i;} int get()const{ return x;} private: int x; }; //这里定义一个返回A类型的函数func,它的一个参数为A 类型的one //A func(A *one) const A* const func(const A *const one) { //A *const one: 指这里的one值不能修改 //const A *const one: 指被传入的A对像不能修改 //A* const func(): 指返回的one指针不能修改 //const A* const func(): 指返回的one指针的对像不能修改 //return *one; //这里是读取one的值,按值返回的话就会调用复制构造函数 cout<<one->get()<<endl; //one = new A; //这里的one是const所以不能重新赋值 //one->set(22); //这里的set是因为const A所以不能操作one的指向的对像 return one; } int main() { A a; //在这定义了一个类A的对象a //创一个对象会自动调用构造函数 //由于构造函数中加入了一条输出信息 //通过这个程序我们就可以看到,将一个对象按值传递给一个函数 //会调用两次复制构造函数和两次析构函数 const A* const ss = func(&a); ss->get(); //读取是没事的 //ss->set(33); //const func()不能操作所返回的值 //ss = new A(); return 0; } //本节主要讲了const的用法 //我们将函数的返回值和接收参数都定义为const //就可以保证函数内不可修改原始值,同时避免利用返回值对原始值进行修改 //所以加上这个const实际上是为了实现按值传递的保护机制, //同时又避免按值传递的开销,因为不同再调复制构造函数 */ // 13 第十三节,使用引用来传递对像 /* #include <iostream> using namespace std; class A { public: A(){ cout<<"构造函数执行..."<<endl;} A(A&){ cout<<"复制构造函数执行..."<<endl;} ~A(){ cout<<"析构函数执行..."<<endl;} void set(int i){ x = i;} int get()const{ return x;} private: int x; }; //这里定义一个返回A类型的函数func,它的一个参数为A 类型的one //A func(A *one) const A& func(const A &one) { //A *const one: 指这里的one值不能修改 //const A *const one: 指被传入的A对像不能修改 //A* const func(): 指返回的one指针不能修改 //const A* const func(): 指返回的one指针的对像不能修改 //return *one; //这里是读取one的值,按值返回的话就会调用复制构造函数 //cout<<one->get()<<endl; //one = new A; //这里的one是const所以不能重新赋值 //one->set(22); //这里的set是因为const A所以不能操作one的指向的对像 cout<<one.get()<<endl; return one; } int main() { A a; a.set(11); //为A新建了一个常量的另名为b A const &b = func(a); b.get(); //ss->get(); //读取是没事的 //ss->set(33); //const func()不能操作所返回的值 //ss = new A(); return 0; } */ //第十四节 到低是使用引用还是指针 /////////////////// ///到底是使用引用还是指针 //既然引用实现了指针的功能,而且使用起来更加方便,为什么还要指针呢? //这是因为指针可以为空,但是引用不能为空,指针可以被赋值,但是引用只可以被初始化, //不可以被赋为另一个对象的别名,如果你想使用一个变量记录不同的对象的地址,那么就必须使用指针 /*#include <iostream> using namespace std; int main() { //这是指针的使用 /*int a=6; int *p = &a; int b=9; p=&b;*/ //引用 //int a=6; //int &p = a; //int b = 9; //p=&b; //这个是不可以的,因为引用不能更改值 //另外,在堆中创建一块内存区域,必须要用指针来指向它,否由该区域就会变成无法访问的空间 //当然我们也可以使用引用来引用指向内存空间的指针*/ /*int *p = new int; int &r = *p; r=4; cout<<*p<<endl; cout<<r<<endl;*/ //但是我们要明白一点,但我们不可以直接用引用空间来指向堆中新建的空间 //因为引用只是一个别名,它不可以作为指针来使用 //这个是把指针给一个指针并且为引用r的 //因此在机器运行不正常的情况下,也就是机器虚拟内存太小,无法创建新空间的情况下 //那么new int会自动返回一个空指针, //我们知道引用不能为空,因此这种史下使用该语句: //int *&r = new int; //就会导致辞一个无用的别名,而使用星号(*)读取一个无用的别名则会引起系统崩溃 //int *&r = new int; //*r = 3; //cout<<*r<<endl; //解决的办法是不要将引用初始化为新建内存区域的别名,而要将r初始化为指向该区域的指针的别名 //前提是首先要判断该指针不为空 //*int *p=new int; //p=10; /* if(p!=NULL) { //将r初始化为p指向的仙存空间中数据的别名 int &r=*p; r=3; //然后通过r保存到空间中 cout<<r<<endl; //注意,这个r只能在这个大括号中使用,因为他是在这里声明的,他的生命周期就只有在这个大括号内 } return 0; }*/ //指针与引用的区别: //指针可以为空,引用不能为空 //指针可以被赋值,引用不能被赋值 //指针可以指向堆中的空间,引用不可以指向堆中空间 // 15 引用和指针可以一块用 /* #include <iostream> using namespace std; int main() { int *func(int &one, int *two, int x); //该行语句声明了一个func函数,该函数有三个参数,第一个是int类型变量的别名one //第二个是指向int型变量的指针two //第三个是整型参数x.该函数返回一个指向int型变量的指针 //初学者也许会认为r和ra都是指向int型变量的指针,假如这么想的话就错了 //其实只有r是指向int型变量的指针,而ra是int型变量,因此我们最好将*r 与int 隔开写 //int*r, ra; int *r, ra; return 0; }*/ /* //16 引用容易的错误 #include <iostream> using namespace std; class A { public: A(int a){ cout<<"构造函数执行"<<endl; this->i=a;} A(A&a){ i = a.i; cout<<"复制构造函数执行"<<endl;} ~A(){ cout<<"析构函数执行"<<endl;} int get(){ return this->i; } private: int i; }; A func() { cout<<"跳转到func函数中"<<endl; A a(99); cout<<a.get(); cout<<"对像a的地址是:"<<&a<<endl; return a; //这里并不是返回的a对象自己,而是返回的a对像的一个副本 //类a在这里已经就已经结束了 } int main() { //这是引用 //cout<<"实例开始"<<endl; //A &r = func(); //cout<<"对像r的地址是:"<<&r<<endl; //cout<<r.get()<<endl; //这里用指针 cout<<"实例开始"<<endl; A *r = &func();//这里会执行两次析构函数,而不像引用只有才main函数结束以后在执行析构函数 cout<<"对像r的地址是:"<<r<<endl; cout<<r->get()<<endl; //那么这里为什么又输出了它的成员x的值呢? //这是因为析构函数调用并析构某个对像后,只是告诉编译器这一块内存不再为某个对像独占了,你可以访问它, //别的对像或者变量也可以访问它并使用该内存区域存储它们的数据,但是它们使用之前,存放在该内存区域的数据并没有删除,因此使用指针来访问该区域仍然能够得到未被修改的x的值 return 0; } */ /* //17 引用一个按值返回的堆中对像 #include <iostream> using namespace std; class A { public: A(int a){ cout<<"构造函数执行"<<endl; this->i=a;} A(A&a){ i = a.i; cout<<"复制构造函数执行"<<endl;} ~A(){ cout<<"析构函数执行"<<endl;} int get(){ return this->i; } private: int i; }; A func() { cout<<"跳转到func函数中"<<endl; //A a(99); A *p = new A(88); //cout<<a.get(); cout<<p->get(); cout<<"对像a的地址是:"<<a<<endl; //因为这里的指针p是存在在堆中的空间,所以只能用delete指针来收回 //但func函数则是存放站中的,站中的内容中系统自动释放的 //所以这里就出现了问题,那p指针就成了野指针 return *a; //而这里返回的只是p指针的一个复本,并不是p指针自己 //func函数返回的不是堆中对像,而是堆中对像的副本 } int main() { cout<<"实例开始"<<endl; A r = func();//这里会执行两次析构函数,而不像引用只有才main函数结束以后在执行析构函数 cout<<"对像r的地址是:"<<&r<<endl; cout<<r.get()<<endl; //那么这里为什么又输出了它的成员x的值呢? //由于返回的是堆中对像的副本,这个副本是调用复制构造函数创建的 //所以这个副本保存在栈中,而不是堆中 //我们知道存放在栈中的数据都是系统自动释放,而delete运算符删除的是堆中的空间,而不是栈中的空间 return 0; } */ /* // 18 引用一个按别名返回的堆中对像 #include <iostream> using namespace std; class A { public: A(int a){ cout<<"构造函数执行"<<endl; this->i=a;} A(A&a){ i = a.i; cout<<"复制构造函数执行"<<endl;} ~A(){ cout<<"析构函数执行"<<endl;} int get(){ return this->i; } private: int i; }; //A func() //按别名返回 A& func() { cout<<"跳转到func函数中"<<endl; A *p = new A(88); //cout<<p->get(); cout<<"对像p的地址是:"<<p<<endl; return *p; } int main() { cout<<"实例开始"<<endl; A &r = func();//这里会执行两次析构函数,而不像引用只有才main函数结束以后在执行析构函数 cout<<"对像r的地址是:"<<&r<<endl; cout<<r.get()<<endl; //那么这里为什么又输出了它的成员x的值呢? //重新定义一个指针,将指针指向于func()返回的指针地址 A *s = &r; //清空 delete s; //但是现在又出现一种错误的情况 cout<<r.get()<<endl; //这里会出现一个随机的内存数,因为&r所指向的地址已经被我们释放了 return 0; } */ //19 在哪些里创建就在哪些里释放 #include <iostream> using namespace std; class A { public: A(int a){ cout<<"构造函数执行"<<endl; this->i=a;} A(A&a){ i = a.i; cout<<"复制构造函数执行"<<endl;} ~A(){ cout<<"析构函数执行"<<endl;} int get(){ return this->i; } void set(int y){ i = y;} private: int i; }; //按r别名返回 void func(A &a) { a.set(22); //return a; //return *p; } int main() { A *p = new A(99); func(*p);//*p 这才是读取指针的对象,p只是读取指针所保存的指 cout<<p->get()<<endl; delete p; return 0; }