引用
- 数据类型 &别名 = 原名;给原名起一个别名,访问同一块内存
- 用多个变量名称访问同一个内存地址。
- 引用必须初始化,初始化后不可以更改。不可以通过赋值操作改变引用。
- 通过引用参数产生的效果和地址传参是一样的,但是引用语法更简单。
- &不是地址运算符,而是类型标识符的一部分。int &指的是指向int的引用。
- 引用与指针不同,是一种伪装指针。
- 引用变量主要被用作处理结构体和类对象的函数的参数。
- 如果一个类(如ofstream)是从另一个类(如ostream)派生出来,则基类引用可以指向派生类对象
- 引用更接近const指针,如下所示:
int & rodents = rats;
int * const pr = &rats;
- 引用常用作函数参数。
- 在对函数的参数传递过程中,使用引用不改变原数据值时,使用const
double refcube(const double &ra);
- 如果实参与引用参数不匹配,c++将生成临时变量(ra指向它)(函数调用期间存在),当前,仅当参数为const引用时,c++才允许这样做。
double refcube(const double &ra)
{
return ra * ra * ra;
}
double side = 3.0;
double * pd = &side;
double & rd = side;
long edge = 5L;
double lens[4] = { 2.0, 5.0, 10.0, 12.0};
double c1 = refcube(side); // ra is side
double c2 = refcube(lens[2]); // ra is lens[2]
double c3 = refcube(rd); // ra is rd is side
double c4 = refcube(*pd); // ra is *pd is side
double c5 = refcube(edge); // ra is temporary variable
double c6 = refcube(7.0); // ra is temporary variable
double c7 = refcube(side + 10.0); // ra is temporary variable
- 右值引用:使用&&声明
- 引用用于结构体:结构体变量名free_throws
//strc_ref.cpp -- using structure references
#include <iostream>
#include <string>
struct free_throws
{
std::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()
{
// partial initializations – remaining members set to 0
free_throws one = {"Ifelsa Branch", 13, 14};
free_throws two = {"Andor Knott", 10, 16};
free_throws three = {"Minnie Max", 7, 9};
free_throws four = {"Whily Looper", 5, 9};
free_throws five = {"Long Long", 6, 14};
free_throws team = {"Throwgoods", 0, 0};
// no initialization
free_throws dup;
set_pc(one);
display(one);
accumulate(team, one);
display(team);
// use return value as argument
display(accumulate(team, two));
accumulate(accumulate(team, three), four);
display(team);
// use return value in assignment
dup = accumulate(team,five);
std::cout << "Displaying team:\n";
display(team);
std::cout << "Displaying dup after assignment:\n";
display(dup);
set_pc(four);
// ill-advised assignment
accumulate(dup,five) = four;
std::cout << "Displaying dup after ill-advised assignment:\n";
display(dup);
return 0;
}
void display(const free_throws & ft)
{
using std::cout;
cout << "Name: " << ft.name << '\n';
cout << " Made: " << ft.made << '\t';
cout << "Attempts: " << ft.attempts << '\t';
cout << "Percent: " << ft.percent << '\n';
}
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;
}
void set_pc(free_throws & ft);
/use a reference to a structure
void display(const free_throws & ft);
don't allow changes to structure
free_throws & accumulate(free_throws & target, const free_throws & source);
accumulate(accumulate(team, three), four);
/上式中如果返回值类型为:free_throws,返回值将左拷贝。
如果返回值类型为:free_throw &,返回的是当初传递给accumulate()的team对象。
引用做函数的返回值
- 传统的返回机制与按值传递函数参数类似:计算return后面的表达式的值,整个值先被复制到一个临时位置,而调用程序再使用这个值。返回值为引用时,不会产生临时变量,直接复制对应值,这样效率更高。
1.不要返回局部变量的引用
2.函数的调用可以作为左值
例子: func() = 888;
- 返回引用时注意的问题:应该避免返回函数终止时,不再存在的内存单元引用。方法有以下两种:
1.返回一个作为参数传递给函数的引用。
const free_throws & clone2(free_throws & ft)
{
free_throws newguy; // first step to big error
newguy = ft; // copy info
return newguy; // return reference to copy
}
2.用new来分配新的存储空间。
const free_throws & clone(free_throws & ft)
{
free_throws * pt;
*pt = ft; // copy info
return *pt; // return reference to copy
}使用clone()创建无名结构体,并创建结构体指针pt
- 上述函数的使用:
free_throws & jolly = clone(three);
- clone()隐藏了对new的调用,这样很容易忘记用delete来释放内存。
引用的本质
- 在c++内部实现是一个指针常量
常量引用
- 例子:const int &a = 10; 此时编译器会临时创建一个temp:int temp = 10; const int &a = temp;
- 多用于函数中
引用用于类对象
- 可以通过使用引用,让函数将类string,ostream,istream,ofstream,ifstream等类的对象作为参数
// strquote.cpp -- different designs
#include <iostream>
#include <string>
using namespace std;
string version1(const string & s1, const string & s2);
const string & version2(string & s1, const string & s2); // has side effect
const string & version3(string & s1, const string & s2); // bad design
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;
result = version2(input, "###");
cout << "Your string enhanced: " << result << endl;
cout << "Your original string: " << input << endl;
cout << "Resetting original string.\n";
input = copy;
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) // has side effect
{
s1 = s2 + s1 + s2;
// safe to return reference passed to function
return s1;
}
const string & version3(string & s1, const string & s2) **bad design**
{
string temp;
temp = s2 + s1 + s2;
// unsafe to return reference to local variable
return temp;
}
何时使用引用参数
使用引用参数的主要原因有两个。
1.程序员能够修改调用函数中的数据对象。
2.通过传递引用而不是整个数据对象,可以提高程序的运行速度。
- 当数据对象较大时(如结构和类对象),第二个原因最重要。这些也是使用指针参数的原因。这是有道理的,因为引用参数实际上是基于指针的代码的另一个接口。那么,什么时候应使用引用、什么时候应使用指针呢?什么时候应按值传递呢?下面是一些指导原则:
- 对于使用传递的值而不作修改的函数。
1.如果数据对象很小,如内置数据类型或小型结构,则按值传递。
2.如果数据对象是数组,则使用指针,因为这是唯一的选择,并将指针声明为指向const的指针。
3.如果数据对象是较大的结构,则使用const指针或const引用,以提高程序的效率。这样可以节省复制结构所需的时间和空间。
4.如果数据对象是类对象,则使用const引用。类设计的语义常常要求使用引用,这是C++新增这项特性的主要原因。因此,传递类对象参数的标准方式是按引用传递。 - 对于修改调用函数中数据的函数:
1.如果数据对象是内置数据类型,则使用指针。如果看到诸如fxt(&x)这样的代码(其中x是int),则很明显,该函数将修改x。
2.如果数据对象是数组,则只能使用指针。
3.如果数据对象是结构,则使用引用或指针。
4.如果数据对象是类对象,则使用引用。 - 当然,这只是一些指导原则,很可能有充分的理由做出其他的选择。例如,对于基本类型,cin使用引用,因此可以使用cin>>n,而不是cin>>&n。