引用

C++·语法 2019-01-10 4364 字 5 浏览 点赞

概念

定义:引用是已定义变量的别名。

分类:左值引用(&),右值引用(&&)。

语法:double& lref; | double&& rref;

优点:节省时间和内存。

主要用途:作为参数的形参。引用变量用作参数,函数将使用原始数据,而不是其副本。

示例:

#include <iostream>

using namespace std;

int main(int argc, char *argv[])
{
    double x = 512.0;
    double& r1 = x;
    double& r2 = r1;

    cout << "r1 = " << r1 << endl;
    cout << "r2 = " << r2 << endl;

    cout << &x << " " << &r1 << " " << &r2 << endl;
    return 0;
}

// 输出:
// r1 = 512
// r2 = 512
// 0x61fe90 0x61fe90 0x61fe90  
// 引用变量与被引用变量的地址相同

特点:一生只效忠一个变量。引用必须在声明的时候初始化,类似指针常量:int* const p = num;

示例:

int main()
{
    double num1 = 512;
    double* p = &num1;
    double& r = *p;

    double num2 = 1024;
    p = &num2;

    // 利用以上方式并不能改变引用变量r的指向
    cout << "num1 = " << num1 << endl;
    cout << "r = " << r << endl;
    cout << "*p = " << *p << endl;
    cout << "num2 = " << num2 << endl;
    return 0;
}

// 输出:
// num1 = 512
// r = 512
// *p = 1024
// num2 = 1024

引用传递

引用经常被用作函数参数,这种方式叫作引用传递。

区别于值传递

  • 值传递:创建一个变量x,来接收实参的值。只能操作实参的副本;
  • 引用传递:创建一个引用变量r,作为实参的别名。可直接对实参操作。
void swap(int& a, int& b)
{
    a = a + b;
    b = a - b;
    a = a - b;
}

int main()
{
    int a = 512, b = 1024;
    swap(a, b);

    cout << "a = " << a << endl;
    cout << "b = " << b << endl;
    return 0;
}

// 输出:
// a = 1024
// b = 512

但引用传递的使用是隐晦的:

// 声明
void swap(int a, int b);  // 值传递
void swap(int& a, int& b);  // 引用传递
void swap(int* a, int* b);  // 指针传递

// 定义
...

// 调用
swap(a, b);
swap(a, b);  // 隐晦(与值传递相同)
swap(&a, &b);  // 明显

常量引用

引用容易引起原数据被改变。如果不希望原数据被改变,可考虑值传递或者常量引用

double func(const double& r);

建议:如果基本数值类型,应采用值传递;当数据比较大时(类、结构),考虑引用传递。


一般地,引用参数只接收变量

double func(double& rre)
{
    return rre;
}

int main()
{
    double num = 512;
    double& r = num;
    cout << func(r + 3) << endl;  // (r + 3) 不是变量

    return 0;
}

此时编译器会报错:

error: invalid initialization of non-const reference of type 'double&' from an rvalue of type 'double'

const引用解决了这种尴尬:

double func(const double& rre)
{
    return rre;
}

int main()
{
    double num = 512;
    double& r = num;
    cout << func(r + 3) << endl;

    return 0;
}

// 输出:
// 515

这是因为实参与引用参数不匹配,C++将生成临时变量(在函数调用期间存在),可打印两个变量的地址如下,确定它们并非同一变量。仅当参数为const引用时

cout << &rre << endl;  // 0x61fe88
cout << &r << endl;  // 0x61fe90

出现临时变量的两种情况:

  • 实参的类型正确,但不是左值;
  • 实参的类型不正确,但可以转换为正确的类型。
double func(const double& rre)
{
    cout << &rre << endl;
    return rre;
}

int main()
{
    int num = 512;
    int& r = num;
    cout << &r << endl;

    func(r + 3);
    return 0;
}

// 输出:
// 0x61fe8c
// 0x61fe90

左值:常规变量、const变量。

非左值:字面常量(字符串外,因为它们是其地址表示),包含多项的表达式。

const的好处

  • 避免无意中修改数据;
  • 使用const的函数能够处理const和非const实参,不使用的只能处理非const;
  • const引用使函数能够正常生成并使用临时变量。

返回引用

返回引用的函数实际上就是被引用的变量的别名。因此使得以下语句语法正确:

double& func(double& rre)
{
    return rre;
}

int main()
{
    double num = 512;
    func(num) = 1024;  // 语法正确
    cout << "num = " << num << endl;
    return 0;
}

// 输出:
// num = 1024

如果想避免函数结果成为左值,可以加上const:

const double& func(double& rre) {...}

传统返回机制与值传递类似:结果被复制到一个临时位置,调用程序将使用这个值。

double m = sqrt(16.0);  // 值4.0被复制到临时位置,然后被复制给m

返回引用则是:可直接使用结果。

double& func(xxx) {...}
double x = func(xxx)  // 直接把func的结果赋值给x

常规返回类型是右值,不能通过地址访问。如字面值10.0,表达式x+y等。常规函数返回值之所以也是右值,是因为它的返回值位于临时内存单元中,运行到下一条语句时,它们可能不再存在。==需要注意的是:==即便函数返回的是全局变量或者static变量,传统返回机制也是copy一份临时变量,再通过这个临时变量赋值。

注意事项

在返回引用中额外需要注意:

  • 避免返回函数终止时不再存在的内存单元的引用;
  • 考虑返回一个作为参数传递给函数的引用。

引用用于类和对象

基类引用可以指向派生类对象。

总结

使用引用的两个主要原因:

  • 能够修改调用函数中的数据对象;
  • 通过传递引用,可以提高程序的运行速度。

使用传递的值而不作修改的函数:

  • 如果数据对象很小,如内置数据类型或小型结构,用值传递;
  • 如果数据对象时数组,则使用指针(因为这是唯一的选择),并将指针声明为指向const的指针;
  • 如果数据对象是较大的结构,则使用const指针或const引用,提升程序的效率(时间和空间);
  • 如果数据对象是类对象,则使用const引用。(类的设计语义常常要求使用引用,这是C++新增此特性的主要原因,因此,传递类对象参数的标准是引用传递。);
  • 如果对象是内置数据类型,选择指针传递;
  • 如果数据对象是数组,则只能使用指针;
  • 如果数据对象是结构,则使用引用或指针;
  • 如果数据对象是类对象,则使用引用。

不必严格按照以上标准。



本文由 Guan 创作,采用 知识共享署名 3.0,可自由转载、引用,但需署名作者且注明文章出处。

还不快抢沙发

添加新评论