【C++学习教程07】引用
目录
参考
- 范磊C++(第9课时)
- Xcode VS2015
内容
什么是引用
引用的地址
#include <iostream>
using namespace std;
int main(int argc, const char * argv[]) {
int a;
int &ra=a;//引用需要初始化
cout<<"a:\t"<<&a<<endl;
cout<<"ra:\t"<<&ra<<endl;
return 0;
}
输出结果:
引用就是别名变量
#include <iostream>
using namespace std;
int main(int argc, const char * argv[]) {
int a;
int &ra=a;//引用需要初始化
a=999;
cout<<"&a:\t"<<&a<<endl;
cout<<"&ra:\t"<<&ra<<endl;
int b=888;
ra=b;
cout<<"&a:\t"<<&a<<endl;
cout<<"&ra:\t"<<&ra<<endl;
cout<<"&b:\t"<<&b<<endl;
cout<<"a:\t"<<a<<endl;
cout<<"ra:\t"<<ra<<endl;
cout<<"b:\t"<<b<<endl;
ra=1;
cout<<"a:\t"<<a<<endl;
cout<<"ra:\t"<<ra<<endl;
cout<<"b:\t"<<b<<endl;
//&ra=b;//非法操作
return 0;
}
输出结果:
引用对象
#include <iostream>
using namespace std;
class Human
{
public:
int get()const{return i;}
void set(int x){i=x;}
private:
int i;
};
int main(int argc, const char * argv[]) {
Human Mike;
Human &rMike=Mike;
rMike.set(123);
cout<<rMike.get()<<endl;
}
输出结果:
空引用
按值传递
按址传递
按别名传递
指针出错率高,且不易阅读。
利用指针和引用返回多值
例子略。
按值传递对象
在这里插入代码片
#include "stdafx.h"
#include <stdlib.h>
#include <iostream>
using namespace std;
class A
{
public:
A() { cout << "执行构造函数创建一个对象" << endl; }
A(A&) { cout << "执行复制构造函数创建该对象的副本" << endl; }//带参数的构造函数
~A() { cout << "执行析构函数删除该对象"<< endl; }
};
A func(A one) //返回值是类A的对象,输入值也是类A的一个对象。
{
return one;
}
int main(int argc, const char * argv[]) {
A a;
func(a);//按值传递
system("pause");
return 0;
}
输出结果:
注意赋值构造函数的含义!!
按址传递对象
#include "stdafx.h"
#include <stdlib.h>
#include <iostream>
using namespace std;
class A
{
public:
A() { cout << "执行构造函数创建一个对象" << endl; }
A(A&) { cout << "执行赋值构造函数创建该对象的副本" << endl; }//带参数的构造函数
~A() { cout << "执行析构函数删除该对象"<< endl; }
};
A* func(A *one) //返回值是类A的对象,输入值也是类A的一个对象。
{
//return *one;//按照值返回,也会调用赋值构造函数
return one;//返回一个地址
}
int main(int argc, const char * argv[]) {
A a;
func(&a);//按址传递
system("pause");
return 0;
}
输出结果:
使用const指针来传递对对象
但是使用const会很麻烦
用别名的方式来传递对象
省略了使用const的复杂。
#include "stdafx.h"
#include <stdlib.h>
#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;
};
const A& func(A &one)
{
return one;
}
int main(int argc, const char * argv[]) {
A a;
a.set(11);
const A &b = func(a);//这样通过别名就不能改变a
//A b = func(a);//不能将引用赋给对象
cout << b.get() << endl;
system("pause");
return 0;
}
输出结果:
引用实现了与指针相同的效果且不用const的,操作起来更加方便。
指针还是引用
- 引用只可以被初始化,但是指针能够赋值;
- 指针可以为空但是引用必须初始化;
- 堆中的内存必须用指针来访问,申请堆的时候返回的是指针。
#include "stdafx.h"
#include <stdlib.h>
#include <iostream>
using namespace std;
int main(int argc, const char * argv[]) {
int *p = new int;
if (p != NULL)
{
int &r = *p;//将r初始化为p指向的内存空间中数据的别名。
r = 3;
cout << r << endl;
}
//r = 4;//并且r的寿命只在大括号范围内有效。
system("pause");
return 0;
}
引用容易犯的错误
最后几课时需要好好学习!
#include "stdafx.h"
#include <stdlib.h>
#include <iostream>
using namespace std;
class A
{
public:
A(int i) { cout << "执行构造函数创建一个对象"<< endl; x = i; }
A(A&a) { x = a.x; cout << "执行复制构造函数创建一个对象"<< endl; }//复制构造函数
~A(){ cout << "执行析构函数" << endl; }
int get()const { return x; }
private:
int x;
};
A func()//返回创建对象a的别名 //按值传递时返回的也是副本的值,其生存期大于t它的引用的生存期。//指针就没问题
{
cout << "跳转到func函数中" << endl;
A a(23);// a在大括号后寿命就结束,a消失了,返回的就是一个并不存在的对象的别名。
cout << "对象a的地址:"<<&a << endl;
return a;
}
int main(int argc, const char * argv[]) {
A *r = &func();//并不存在的对象的别名
cout << "对象a的副本的地址:" << r << endl;
cout << r->get() << endl;
system("pause");
return 0;
}
具体分析见课件
引用一个按值返回的堆中对象
- 错误例子
#include "stdafx.h"
#include <stdlib.h>
#include <iostream>
using namespace std;
class A
{
public:
A(int i) { cout << "执行构造函数创建一个对象"<< endl; x = i; }
A(A&a) { x = a.x; cout << "执行复制构造函数创建一个对象"<< endl; }//a是堆中对象的别名->执行后p指针无效被销毁(造成内存泄漏),这个程序只能访问其副本的地址。
~A(){ cout << "执行析构函数" << endl; }
int get()const { return x; }
private:
int x;
};
A func() //按值返回的函数
{
cout << "跳转到func函数中" << endl;
A *p=new A(99);
cout << "堆中对象的地址:"<<p<< endl;
return *p;//读到堆中对象,创建其副本->复制构造函数
}
int main(int argc, const char * argv[]) {
A &r = func();//读到的是副本的地址
cout << "堆中对象的副本的地址:" << &r << endl;//地址证明他只是副本的地址
cout << r.get() << endl;//引用延长了临时变量的声明(也就是副本的)
system("pause");
return 0;
}
输出结果:
报错原因:
引用一个按别名返回的堆中对象
#include "stdafx.h"
#include <stdlib.h>
#include <iostream>
using namespace std;
class A
{
public:
A(int i) { cout << "执行构造函数创建一个对象"<< endl; x = i; }
A(A&a) { x = a.x; cout << "执行复制构造函数创建一个对象"<< endl; }//a是堆中对象的别名->执行后p指针无效被销毁(造成内存泄漏),这个程序只能访问其副本的地址。
~A(){ cout << "执行析构函数" << endl; }
int get()const { return x; }
private:
int x;
};
A& func() //按引用返回的函数
{
cout << "跳转到func函数中" << endl;
A *p=new A(99);
cout << "堆中对象的地址:"<<p<< endl;
return *p;//返回引用注意!!
}
int main(int argc, const char * argv[]) {
A &r = func();//引用
cout << "堆中对象的副本的地址:" << &r << endl;//地址证明他只是副本的地址
cout << r.get() << endl;//引用延长了临时变量的声明(也就是副本的)
A *p = &r;
delete p;//删除p所指向的内存空间//所以调用了析构函数//r变成了空别名
cout << r.get() << endl;//致命的错误
system("pause");
return 0;
}
输出结果:
在哪里创建就在哪里释放
唯一正确无误的程序,推荐使用
#include "stdafx.h"
#include <stdlib.h>
#include <iostream>
using namespace std;
class A
{
public:
A(int i) { cout << "执行构造函数创建一个对象"<< endl; x = i; }
A(A&a) { x = a.x; cout << "执行复制构造函数创建一个对象"<< endl; }
~A(){ cout << "执行析构函数" << endl; }
void set(int i) { x = i; }
int get()const { return x; }
private:
int x;
};
A& func(A &a)
{
cout << "跳转到func函数中" << endl;
a.set(66);
return a;
}
int main(int argc, const char * argv[]) {
A *p = new A(99);//在main中创建
func(*p);
cout<< p->get()<<endl;
delete p;//在main中释放
system("pause");
return 0;
}
输出结果: