21天学C++(九)引用

完整目录、平台简介、安装环境及版本:参考《21天学C++ 概览》

九、引用

9.1 引用

9.1.1 引用就是一个别名

  • 当声明一个引用时,应该把它初始化为另一个对象名;
  • 对引用的操作就是对其引用对象的操作;

9.1.2 格式

如int &rSomeRef = someInt;

#include <iostream>

int main()
{
	using namespace std;
	int  intOne;
	int &rSomeRef = intOne;

	intOne = 5;
	cout << "intOne: " << intOne << endl;
	cout << "rSomeRef: " << rSomeRef << endl;

	rSomeRef = 7;
	cout << "intOne: " << intOne << endl;
	cout << "rSomeRef: " << rSomeRef << endl;

	return 0;
}

执行结果如下:

intOne: 5
rSomeRef: 5
intOne: 7
rSomeRef: 7
请按任意键继续. . .

9.1.3 引用运算符&和去地址运算符号相同,但不是同一个运算符

#include <iostream>

int main()
{
	using namespace std;
	int  intOne;
	int &rSomeRef = intOne;

	intOne = 5;
	cout << "intOne: " << intOne << endl;
	cout << "rSomeRef: " << rSomeRef << endl;

	cout << "&intOne: "  << &intOne << endl;
	cout << "&rSomeRef: " << &rSomeRef << endl;

	return 0;
}

执行结果如下:

intOne: 5
rSomeRef: 5
&intOne: 0073F9A0
&rSomeRef: 0073F9A0
请按任意键继续. . .

9.1.4 给引用重新赋值

对应用重新赋值相当于对目标重新赋值;

#include <iostream>

int main()
{
	using namespace std;
	int  intOne;
	int &rSomeRef = intOne;

	intOne = 5;
	cout << "intOne:    " << intOne << endl;
	cout << "rSomeRef:  " << rSomeRef << endl;
	cout << "&intOne:   "  << &intOne << endl;
	cout << "&rSomeRef: " << &rSomeRef << endl;

	int intTwo = 8;
	rSomeRef = intTwo;  // not what you think!
	cout << "\nintOne:    " << intOne << endl;
	cout << "intTwo:    " << intTwo << endl;
	cout << "rSomeRef:  " << rSomeRef << endl;
	cout << "&intOne:   "  << &intOne << endl;
	cout << "&intTwo:   "  << &intTwo << endl;
	cout << "&rSomeRef: " << &rSomeRef << endl;
	return 0;
}

执行结果如下:

intOne:    5
rSomeRef:  5
&intOne:   00EFF900
&rSomeRef: 00EFF900

intOne:    8
intTwo:    8
rSomeRef:  8
&intOne:   00EFF900
&intTwo:   00EFF8E8
&rSomeRef: 00EFF900
请按任意键继续. . .

9.1.5 什么可以被引用

任何对象都可以被引用,包括用户定义的对象;

int & rIntRef = int; //错,必须是对象,不能是对类的引用
Cat & rCatRef = Cat; //错,必须是对象,不能是对类的引用

创建引用时,必须对引用进行初始化;

int howBig = 200;
int & rIntRef =  howBig;

Cat frisky;
Cat & rCatRef = frisky;

9.1.6 空指针和空引用

  • 指针没有进行初始化或进行删除时,应该将其赋为空值;
  • 引用不能为空,空引用是无效的;

9.2 引用/指针传递和返回

9.2.1 使用指针递,使用引用传递

  • 不在函数的作用域中创建一个拷贝,而是直接把原对象传递给函数(效率高);
  • 通过指针或者引用传值时,栈(或寄存器)存储的是对象的地址而非整个对象;
#include <iostream>

using namespace std;
void swap(int x, int y);

int main()
{
	int x = 5, y = 10;

	cout << "Main. Before swap, x: " << x << " y: " << y << endl;
	swap(x,y);
	cout << "Main. After swap, x: " << x << " y: " << y << endl;
	return 0;
}

void swap (int x, int y)
{
	int temp;

	cout << "Swap. Before swap, x: " << x << " y: " << y << endl;

	temp = x;
	x = y;
	y = temp;

	cout << "Swap. After swap, x: " << x << " y: " << y << endl;
}

执行结果如下:

Main. Before swap, x: 5 y: 10
Swap. Before swap, x: 5 y: 10
Swap. After swap, x: 10 y: 5
Main. After swap, x: 5 y: 10
请按任意键继续. . .
#include <iostream>

using namespace std;
void swap(int *x, int *y);

int main()
{
	int x = 5, y = 10;

	cout << "Main. Before swap, x: " << x << " y: " << y << endl;
	swap(&x,&y);
	cout << "Main. After swap, x: " << x << " y: " << y << endl;
	return 0;
}

void swap (int *px, int *py)
{
	int temp;

	cout << "Swap. Before swap, *px: " << *px <<
		" *py: " << *py << endl;

	temp = *px;
	*px = *py;
	*py = temp;

	cout << "Swap. After swap, *px: " << *px << 
		" *py: " << *py << endl;

}

执行结果如下:

Main. Before swap, x: 5 y: 10
Swap. Before swap, *px: 5 *py: 10
Swap. After swap, *px: 10 *py: 5
Main. After swap, x: 10 y: 5
请按任意键继续. . .
#include <iostream>

using namespace std;
void swap(int &x, int &y);

int main()
{
	int x = 5, y = 10;

	cout << "Main. Before swap, x: " << x << " y: " 
		<< y << endl;

	swap(x,y);

	cout << "Main. After swap, x: " << x << " y: " 
		<< y << endl;

	return 0;
}

void swap (int &rx, int &ry)
{
	int temp;

	cout << "Swap. Before swap, rx: " << rx << " ry: " 
		<< ry << endl;

	temp = rx;
	rx = ry;
	ry = temp;


	cout << "Swap. After swap, rx: " << rx << " ry: " 
		<< ry << endl;

}

执行结果如下:

Main. Before swap, x: 5 y: 10
Swap. Before swap, rx: 5 ry: 10
Swap. After swap, rx: 10 ry: 5
Main. After swap, x: 10 y: 5
请按任意键继续. . .

9.2.2 返回多个值

  • 通过指针传递多个对象给函数,允许函数改变原对象,有效的使函数返回多个信息;
  • 通过引用传递多个对象给函数,允许函数改变原对象,有效的使函数返回多个信息;
#include <iostream>

using namespace std;
short Factor(int n, int* pSquared, int* pCubed);

int main()
{
	int number, squared, cubed;
	short error;

	cout << "Enter a number (0 - 20): ";
	cin >> number;

	error = Factor(number, &squared, &cubed);

	if (!error)
	{
		cout << "number: " << number << endl;
		cout << "square: " << squared << endl;
		cout << "cubed: "  << cubed   << endl;
	}
	else
		cout << "Error encountered!!" << endl;
	return 0;
}

short Factor(int n, int *pSquared, int *pCubed)
{
	short Value = 0;
	if (n > 20)
		Value = 1;
	else
	{
		*pSquared = n*n;
		*pCubed = n*n*n;
		Value = 0;
	}
	return Value;
}

执行结果如下:

Enter a number (0 - 20): 5
number: 5
square: 25
cubed: 125
请按任意键继续. . .
#include <iostream>

using namespace std;

enum ERR_CODE { SUCCESS, ERROR };

ERR_CODE Factor(int, int&, int&);

int main()
{
	int number, squared, cubed;
	ERR_CODE result;

	cout << "Enter a number (0 - 20): ";
	cin >> number;

	result = Factor(number, squared, cubed);

	if (result == SUCCESS)
	{
		cout << "number: " << number << endl;
		cout << "square: " << squared << endl;
		cout << "cubed: "  << cubed   << endl;
	}
	else
		cout << "Error encountered!!" << endl;
	return 0;
}

ERR_CODE Factor(int n, int &rSquared, int &rCubed)
{
	if (n > 20)
		return ERROR;   // simple error code
	else
	{
		rSquared = n*n;
		rCubed = n*n*n;
		return SUCCESS;
	}
}

执行结果如下:

Enter a number (0 - 20): 5
number: 5
square: 25
cubed: 125
请按任意键继续. . .

9.2.3 提高传递效率

  • 值传递,会建立一个对象的拷贝;
  • 值返回,会建立另一个拷贝;
  • 值传递对象时,通过赋值构造函数建立拷贝,删除时利用析构函数释放,当对象很大时,调用构造函数和析构函数在速度和内存方面都会造成很大的开销;
#include <iostream>

using namespace std;
class SimpleCat
{
public:
	SimpleCat ();              // constructor
	SimpleCat(SimpleCat&);     // copy constructor
	~SimpleCat();              // destructor
};

SimpleCat::SimpleCat()
{
	cout << "Simple Cat Constructor..." << endl;
}

SimpleCat::SimpleCat(SimpleCat&)
{
	cout << "Simple Cat Copy Constructor..." << endl;
}

SimpleCat::~SimpleCat()
{
	cout << "Simple Cat Destructor..." << endl;
}

SimpleCat FunctionOne (SimpleCat theCat);
SimpleCat* FunctionTwo (SimpleCat *theCat);
SimpleCat &FunctionThree (SimpleCat &theCat);

int main()
{
	cout << "Making a cat..." << endl;
	SimpleCat Frisky;
	cout << "Calling FunctionOne..." << endl;
	FunctionOne(Frisky);
	cout << "Calling FunctionTwo..." << endl;
	FunctionTwo(&Frisky);
	cout << "Calling FunctionThree..." << endl;
	FunctionThree(Frisky);
	return 0;
}

SimpleCat FunctionOne(SimpleCat theCat)
{
	cout << "Function One. Returning... " << endl;
	return theCat;
}

SimpleCat* FunctionTwo (SimpleCat  *theCat)
{
	cout << "Function Two. Returning... " << endl;
	return theCat;
}

SimpleCat &FunctionThree (SimpleCat  &theCat)
{
	cout << "Function Three. Returning... " << endl;
	return theCat;
}

执行结果如下:

Making a cat...
Simple Cat Constructor...
Calling FunctionOne...
Simple Cat Copy Constructor...
Function One. Returning...
Simple Cat Copy Constructor...
Simple Cat Destructor...
Simple Cat Destructor...
Calling FunctionTwo...
Function Two. Returning...
Calling FunctionThree...
Function Three. Returning...
Simple Cat Destructor...
请按任意键继续. . .

9.2.4 const引用/指针

指针/引用传递效率高,但是其值可以函数中被修改,有时这是很危险的,使用const既可以利用指针/引用传递效率的优势,又可以避免值在函数中被修改

#include <iostream>

using namespace std;
class SimpleCat
{
public:
	SimpleCat();
	SimpleCat(SimpleCat&);
	~SimpleCat();

	int GetAge() const { return itsAge; }
	void SetAge(int age) { itsAge = age; }

private:
	int itsAge;
};

SimpleCat::SimpleCat()
{
	cout << "Simple Cat Constructor..." << endl;
	itsAge = 1;
}

SimpleCat::SimpleCat(SimpleCat&)
{
	cout << "Simple Cat Copy Constructor..." << endl;
}

SimpleCat::~SimpleCat()
{
	cout << "Simple Cat Destructor..." << endl;
}

const SimpleCat * const FunctionTwo (const SimpleCat * const theCat);

int main()
{
	cout << "Making a cat..." << endl;
	SimpleCat Frisky;
	cout << "Frisky is " ;
	cout << Frisky.GetAge();
	cout << " years old" << endl;
	int age = 5;
	Frisky.SetAge(age);
	cout << "Frisky is " ;
	cout << Frisky.GetAge();
	cout << " years old" << endl;
	cout << "Calling FunctionTwo..." << endl;
	FunctionTwo(&Frisky);
	cout << "Frisky is " ;
	cout << Frisky.GetAge();
	cout << " years old" << endl;
	return 0;
}

// functionTwo, passes a const pointer
const SimpleCat * const FunctionTwo (const SimpleCat * const theCat)
{
	cout << "Function Two. Returning..." << endl;
	cout << "Frisky is now " << theCat->GetAge();
	cout << " years old " << endl;
	 //theCat->SetAge(8);   const!
	//theCat = new SimpleCat();//错误	1	error C3892: “theCat”: 不能给常量赋值	e:\study\source\c++\09011_const\main.cpp	65	09011_Const

	return theCat;
}

执行结果如下:

Making a cat...
Simple Cat Constructor...
Frisky is 1 years old
Frisky is 5 years old
Calling FunctionTwo...
Function Two. Returning...
Frisky is now 5 years old
Frisky is 5 years old
Simple Cat Destructor...
请按任意键继续. . .

9.3 引用/指针使用事项

9.3.1 何时使用引用、何时使用指针

  • 引用相对于指针使程序更清晰、更易于使用;
  • 引用不能被重新赋值,如果使用一个变量指向不同对象时,则必须使用指针;
  • 引用不能为空,如果存在可能为空的情况,则必须使用指针;
  • 尽可能多的使用引用传递参数,使用引用返回,使用const包括引用和指针;

9.3.2 不要返回对不在作用域中的对象的引用

如返回一个被释放对象的引用;

#include <iostream>

class SimpleCat
{
public:
	SimpleCat (int age, int weight);
	~SimpleCat() {}
	int GetAge() { return itsAge; }
	int GetWeight() { return itsWeight; }
private:
	int itsAge;
	int itsWeight;
};

SimpleCat::SimpleCat(int age, int weight)
{
	itsAge = age;
	itsWeight = weight;
}

SimpleCat &TheFunction();

int main()
{
	SimpleCat &rCat = TheFunction();
	int age = rCat.GetAge();
	std::cout << "rCat is " << age << " years old!" << std::endl;
	return 0;
}

SimpleCat &TheFunction()
{
	SimpleCat Frisky(5,9);
	return Frisky;
}

执行结果如下:

rCat is -858993460 years old!
请按任意键继续. . .

9.3.3 返回堆中的对象

  • 堆中创建的对象在函数返回时不会被释放;
  • 返回引用后,如何删除堆空间?
  • 新建一个指针指向堆空间,然后释放对空间,释放后,原引用就会引用一个空对象?

三种解决办法:

  • 传值方式返回局部对象;
  • 返回指针,然后delete该指针;
  • main中声明对象,引用方式传递给函数;

在一个函数中分配内存,在另一个函数中释放时非常危险的,要么忘记删除指针,要么重复删除指针;

int main(){
    SimpleCat & rCat = TheFunction();
    int age = rCat.GetAge();

    SimpleCat * pCat = &rCat;
    delete pCat;
	
    return 0;
}
SimpleCat &TheFunction(){
    SimpleCat * pFrisky = new SimpleCat(5,9);
    return *pFrisky;
}

发表回复