引用变量
引用变量是C++的一种复合类型,是已定义变量的别名,主要用途是做函数的参数。通过将引用做参数,函数将使用原始数据而非副本。这样除指针之外,引用也为函数处理大型大型结构提供了一种方便的途径,同时对于设计类来说,引用也是不可避免的
- 创建引用变量
int rats;
int & rodents = rats; // makes rodents an alias for rats
/* &不是地址运算符,而是类型标识符,int &指的是指向int的引用 */
- 引用与指针的区别
- 指针可以先声明在赋值,而引用必须在声明时赋值
/* 指针 */
int rat;
int * rodents;
rodents = &rat; // OK
/* 引用 */
int rat;
int & rodents;
rodent = rat; // No, you can't do this.
- 引用接近const指针,必须在创建时进行初始化,一旦与某个变量关联起来,就一直效忠于它
int a = 3;
int & b = a;
int c = 50;
b = c;
std::cout << "variable c value is " << c << ", storage address is " << &c << std::endl;
std::cout << "variable b value is " << b << ", storage address is " << &b << std::endl;
std::cout << "variable a value is " << a << ", storage address is " << &a << std::endl;
$ ./example
variable c value is 50, storage address is 0x7fff609c05f0
variable b value is 50, storage address is 0x7fff609c05f4
variable a value is 50, storage address is 0x7fff609c05f4
/* 由上得知, b与a的地址相同, 而与c的地址不同,由于b是a的别名, 因此b=c, 与a=b等效,
结论:可以通过初始化声明来设置引用,但不能通过赋值操作来设置
*/
-
将引用用作函数参数
引用常用作函数参数,使得函数中变量名成为调用程序中的变量的变量,这种传递参数的方法称为按引用传递。C++新增的这项特性是对C语言的超越,C语言只能按值传递,按值传递导致被调用函数使用调用程序的值的拷贝。C语言也有避开按值传递的限制,采用按指针传递的方式 -
引用的属性和特别之处
/* 临时变量、引用参数和const */ void test(int ra) { cout << ra << endl; } int a = 1; int b = test(a + 1.1); // 不合理,因为表达式a+1非变量 /* 如果实参与引用参数不匹配,C++将生成临时变量。当前,仅当参数为const引用时, C++才允许这样做 */ /* 如果引用参数是const, 则编译器将在下面两种情况下生成临时变量: 1. 实参的类型正确,但不是左值 2. 实参的类型不正确,但可以转换为正确的类型 */ /* 在早期的C++中*/ void swapr(int & a, int & b) { int temp; temp = a; a = b; b = temp; } long a = 3, b = 5; swapr(a, b); // 在早期C++较为宽松的规则下,执行这样的操作时,因为类型不匹配,因此编译器将创建两个临时int变量,并分别初始化为3和5,然后交换临时变量的内容,而a和b保持不变
注意: 如果函数调用的参数不是左值或与相应的const引用参数的类型不匹配,则C++将创建类型正确的匿名变量,将函数调用的参数的值传递给匿名变量,并让参数来应用该变量
尽可能的使用const:
- 使用const可以避免无意中修改数据的编程错误
- 使用const使函数能够处理const和非const实参,否则只能接受非const数据
- 使用const引用使函数能够正确的生成临时变量
** 右值引用:**
/* C++11新增了另一种引用——右值引用(rvalue reference) 这种引用可指向右值,使用&&声明 */
double && rref = sqrt(36.00); // not allowed for double &
double j = 15.0;
double && jref = 2.0 * j + 18.5; // not allowed for double &
cout << rref << endl; // display 6.0
cout << jref << endl; // display 48.5
/* 新增右值引用的主要目的是,让库设计人员能够提供有些操作的有效实现, C++ primer plus chapter 18详细描述如何使用右值引用来实现移动语义(move semantics)。以前的引用(使用&声明的引用)现在称为左值引用 */
- 将引用用于结构
引入引用的主要目的是用于结构和类,而非内置的基本类型
注意: 返回引用的函数实际上是被引用的变量的别名// 返回引用还不是非常理解, 暂时写个相对简单的例子 /* 函数返回引用 */ struct Student { int age; std::string name; }; // 方式1 Student & test_return_quote(Student & a) { a = {25, "LiuDeHua"}; std::cout << "init success" << std::endl; // Student c = {20, "aaa"}; //return c; 不可以返回已经不再存在的内存单元 return a; } /* 另一种方法是: 使用new来分配新的存储空间,并返回指向该内存空间的指针 而调用test_return_quote2()时隐藏了对new的调用,应该使 用delete释放,或者使用auto_ptr模板以及C++11新增的unique_ptr自动完成释放工作 */ Student & test_return_quote2(Student & a) { Student *pt; *pt = a; return *pt; } int main() { Student s; Student a; s = test_retuen_quote(a); std::cout << s.age << std::endl; std::cout << s.name << std::endl; /* 传统返回机制与返回应用的不同, 传统返回机制与按值传递函数类似; 计算关键字return后面的表达式,并将结果返回给调用函数。 从概念上说, 这个值(test_return_quote中的a)复制到一个临时位置, 然后将这个值拷贝给调用方s。 但在返回引用时,将直接把a赋值到s, 其效率更高 */ return 0; }
返回引用时需注意的问题:
1. 返回引用时,应避免返回函数终止时不再存在的内存单元引用
2. 如果用new来分配新的存储空间,并返回指向该内存空间的指针时,应该做到及时delete或智能指针及时释放