【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:	"<<&a<<endl;
    cout<<"ra:	"<<&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:	"<<&a<<endl;
    cout<<"&ra:	"<<&ra<<endl;
    int b=888;
    ra=b;
    cout<<"&a:	"<<&a<<endl;
    cout<<"&ra:	"<<&ra<<endl;
    cout<<"&b:	"<<&b<<endl;
    cout<<"a:	"<<a<<endl;
    cout<<"ra:	"<<ra<<endl;
    cout<<"b:	"<<b<<endl;
    ra=1;
    cout<<"a:	"<<a<<endl;
    cout<<"ra:	"<<ra<<endl;
    cout<<"b:	"<<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;
}

输出结果:
在这里插入图片描述

原文地址:https://www.cnblogs.com/vrijheid/p/14222991.html