引用变量
引用变量是C++引进的新的复合类型——引用是已定义的变量的 别名。主要用途是用作函数的 形参。通过将引用变量做参数,函数将使用原始数据,而不是副本。
1.1 创建引用变量
#include<iostream>
using namespace std;
int main()
{
int apple;
int & malus = apple;
cout << "Enter the numbers of apple: ";
cin >> apple;
cout << "Now we have " << apple << " apples." << endl;
malus ++;
cout << "Now we have " << apple << " apples." << endl;
cout << "The address of value apple is: " << &apple << endl;
cout << "The address of value malus is: " << &malus << endl;
return 0;
}
上述例子中,将 malus 声明为 int & 类型,即指向 int 变量的引用。apple 与 malus 的地址相同。
**注意:**引用与指针本质上不同!
1.2 将引用用作函数参数
#include<iostream>
using namespace std;
void swap1(int, int);
void swap2(int *a,int *b);
void swap3(int &, int &);
void shownum();
static int num1 = 5;
static int num2 = 6;
int main()
{
shownum();
swap1(num1,num2);
shownum();
swap2(&num1,&num2);
shownum();
swap3(num1,num2);
shownum();
return 0;
}
//按值传递
void swap1(int a, int b)
{
int temp;
temp = a;
a = b;
b = temp;
cout << " done " << endl;
}
//按指针传递
void swap2(int *a,int *b)
{
int temp;
temp = *a;
*a = *b;
*b = temp;
cout << " done " << endl;
}
//按引用传递
void swap3(int &a, int &b)
{
int temp;
temp = a;
a = b;
b = temp;
cout << " done " << endl;
}
void shownum()
{
cout << "NUM1: " << num1 << endl;
cout << "NUM2: " << num2 << endl;
}
以上用交换函数作为例子,输出结果如下:
NUM1: 5
NUM2: 6
done
NUM1: 5
NUM2: 6
done
NUM1: 6
NUM2: 5
done
NUM1: 5
NUM2: 6
即按引用传递和按指针传递都使原本的值交换了。
1.3 补充:左值?右值?
1.4 引用与结构体
引用的语法功能就是为了结构体和类配套服务的,而不是基础数据类型。
1.4.1 代码实例
#include <iostream>
#include <string>
using namespace std;
struct free_throws
{
string name;
int made;
int attempts;
float percent;
};
void display(const free_throws &ft);
void set_pc(free_throws & ft);
free_throws & accumulate(free_throws & target, const free_throws & source);
int main()
{
free_throws one = {"A",13,14};
free_throws two = {"B",10,16};
free_throws three = {"C",7,9};
free_throws four = {"D",5,9};
free_throws five = {"E",6,14};
free_throws team = {"team",0,0};
free_throws dup;
set_pc(one);
display(one);
accumulate(team,one);
display(team);
display(accumulate(team,two));
accumulate(accumulate(team,three),four);
display(team);
dup = accumulate(team,five);
cout << "Displaying team: " << endl;
display(team);
cout << "Displaying dup after assignment: " << endl;
display(dup);
set_pc(four);
accumulate(dup,five) = four;
cout << "Display dup after ill-advised assignment: " << endl;
display(dup);
return 0;
}
void display(const free_throws & ft) //显示结构体的内容,所以使用const防止被修改
{
cout << "Name: " << ft.name << endl;
cout << "Made: " << ft.made << endl;
cout << "Attempts: " << ft.attempts << endl;
cout << "Percent: " << ft.percent << endl;
}
void set_pc(free_throws & ft)
{
if(ft.attempts != 0)
ft.percent = 100.0f * float(ft.made)/float(ft.attempts);
else
ft.percent = 0;
}
free_throws & accumulate(free_throws & target, const free_throws & source) //返回一个引用
{
target.attempts += source.attempts;
target.made += source.made;
set_pc(target);
return target;
}
1.4.2 函数说明
- display()
由于 display() 显示的是结构体内容,并不对它进行修改,所以使用了 const 引用参数。按值传递也可以,但与复制原始结构的拷贝相比,使用引用节省了时间和内存。 - accumulate()
dup=accumulate(team,five)
该函数返回的是一个引用。假设该函数返回的是一个结构体,而不是引用,将把整个结构体复制到一个临时位置,再将这个拷贝复制给dup。但在返回值为引用时,则直接把team复制到dup,效率明显更高。
注:返回引用的函数,实际上是被引用的变量的别名。
1.5 引用与类
1.5.1 代码实例
#include <iostream>
#include <string>
using namespace std;
string version1(const string & s1, const string & s2);
const string & version2(string & s1, const string & s2);
const string & version3(string & s1, const string & s2);
int main()
{
string input;
string copy;
string result;
cout << "Enter a string: ";
getline(cin, input);
copy = input;
cout << "Your string as entered: " << input << endl;
result = version1(input,"***");
cout << "Your string enhanced: " << result << endl;
cout << "Your original string: " << input << endl;
cout << "Your string as entered: " << input << endl;
result = version2(input,"###");
cout << "Your string enhanced: " << result << endl;
cout << "Your original string: " << input << endl;
input = copy;
cout << "Your string as entered: " << input << endl;
result = version3(input,"@@@");
cout << "Your string enhanced: " << result << endl;
cout << "Your original string: " << input << endl;
return 0;
}
string version1(const string & s1, const string & s2)
{
string temp;
temp = s2 + s1 + s2;
return temp;
}
const string & version2(string & s1, const string & s2)
{
s1 = s2 + s1 + s2;
return s1;
}
const string & version3(string & s1, const string & s2)
{
string temp;
temp = s2 + s1 + s2;
return temp;
}
1.5.2 函数说明
-
string version1(const string & s1, const string & s2)
该函数的返回类型为 string, 意味着 temp 的内容将被复制到一个临时存储单元中,然后在main()中,该存储单元的内容被复制到一个名为result的string中。 -
const string & version2(string & s1, const string & s2)
该函数会把原始的s1值修改,如果要保留原来的字符串不变,这将是一种错误设计。 -
const string & version3(string & s1, const string & s2)
该函数存在致命的缺陷:程序视图引用已经释放的内存。(temp)**具体在内存模型中探讨这个问题**。
输出如下:Enter a string: happy Your string as entered: happy Your string enhanced: ***happy*** Your original string: happy Your string as entered: happy Your string enhanced: ###happy### Your original string: ###happy### Your string as entered: happy Your string enhanced: 换譤L#@ //程序崩溃
1.6 对象、继承和引用
C++简单文件输入/输出中提到了ostream和ofstream。这时候不得不说到继承,使得能够将一个特性从一个类传递给另一个类的语言特性被称为继承。
简单地说,ostream是基类,ofstream是派生类。
- 派生类继承了基类的方法,意味着ofstream对象可以使用基类的特性,如格式化方法precision()和setf()。
- 基类引用可以指向派生类对象。即可以定义一个接受基类引用作为参数的函数,调用该函数时,可以将基类对象作为参数,也可以将派生类对象作为参数。
具体看以下代码实例。
1.6.1 代码实例
#include <iostream>
#include <fstream>
#include <cstdlib>
using namespace std;
void file_it(ostream & os, double fo, const double fe[], int n);
const int LIMIT = 5;
int main()
{
ofstream fout;
const char * fn = "ep-data.txt";
fout.open(fn);
if(!fout.is_open())
{
cout << "Can't open " << fn << ". Bye." << endl;
exit(EXIT_FAILURE);
}
double objective;
cout << "Enter the focal length of your "
"telescope objective in mm: ";
cin >> objective;
double eps[LIMIT];
cout << "Enter the focal length of your "
"eyepieces: " << endl;
for (int i = 0; i < LIMIT; i++)
{
cout << "Eyepiece #" << i + 1 << ": ";
cin >> eps[i];
}
file_it(fout, objective, eps, LIMIT);
file_it(cout, objective, eps, LIMIT);
return 0;
}
void file_it(ostream & os, double fo, const double fe[], int n)
{
ios_base::fmtflags initial;
initial = os.setf(ios_base::fixed);
os.precision(0);
os << "Focal length of objective: " << fo << " mm" << endl;
os.setf(ios::showpoint);
os.precision(1);
os.width(12);
os << "f.1. eyepiece";
os.width(15);
os << "magnification" << endl;
for (int i = 0; i < n; i++)
{
os.width(12);
os << fe[i];
os.width(15);
os << int(fo/fe[i] + 0.5) << endl;
}
os.setf(initial);
}
文件中存储结果
1.6.2 简单说明
对于该程序,最重要的一点就是,参数 os(其类型为ostream &)可以指向 ostream 对象 (如 cout),也可以指向 ofstream 对象(如 fout)。还演示了 ostream 类中的格式化方法,具体以后再讨论。
1.7 小结
何时使用引用参数?
➢ 程序员能够修改调用函数中的客户程序的数据对象;
➢ 通过传递引用,可以提高程序的运行速度。
使用引用参数的一些指导原则:
➢ 如果数据对象很小,尽量按值传递;
➢ 如果数据对象是数组,则使用指针;
➢ 如果数据对象是较大的结构,则使用指针或引用,以节省复制结构对象所需的时间和空间;
➢ 如果数据对象是类对象,则使用引用。
类设计的语义常常使用到引用参数,这也是C++相对于C语言新增引用语法功能的重要原因。
资料参考 C++ Primer Plus (第六版)中文版,仅学习使用