C++ 引用& 和 右值(纯右值、将亡值)引用&& (1)

 我们先来简单介绍下&引用:

C和C++使用&符号来只是变量的地址。C++给&符号赋予了另一个含义,将其来声明引用。

例如,要将rodents作为rats变量的别名,可以这样做:

int rats;
int & rodents = rates;

其中,&不是地址运算符,而是类型标识符的一部分。上诉引用声明允许将rats和rodents互换——它们指向相同的值和内存单元。

必须在声明引用时将其初始化,而不能向指针那样,先声明,再赋值。

举个例子:

// secref.cpp -- defining and using a reference
#include 
int main()
{
    using namespace std;
    int rats = 101;
    int & rodents = rats;   // rodents is a reference

    cout << "rats = " << rats;
    cout << ", rodents = " << rodents << endl;

    cout << "rats address = " << &rats;
    cout << ", rodents address = " << &rodents << endl;

    int bunnies = 50;
    rodents = bunnies;       // can we change the reference?
    cout << "bunnies = " << bunnies;
    cout << ", rats = " << rats;
    cout << ", rodents = " << rodents << endl;

    cout << "bunnies address = " << &bunnies;
    cout << ", rodents address = " << &rodents << endl;
    // cin.get();
    return 0; 
}

输出:

rats=101,rodents=101

rats address=0x00065fd44,rodents address= 0x00065fd44

bunnies=50,rate=50,rodents=50

rats address=0x00065fd48,rodents address= 0x00065fd44

也就是说,rats=bunnies 意味着“ 将bunnies变量的值付给rat变量 ”。简而言之,可以通过初始化声明来设置引用,但是不能用赋值来设置。

C++ 引用& 和 右值(纯右值、将亡值)引用&& (1)_第1张图片

 

假设程序员试图这样做:

int rats=101;
int *pt =&rats;
int &rodents=*pt;
int bunnies=50;
pt =&bunnies;

将rodents初始化为*pt使得rodents指向rats。接下来将pt改为指向bunnies,并不能改变这样的事实,即rodents引用的是rats。

将引用用作函数参数

// cubes.cpp -- regular and reference arguments
#include 
double cube(double a);
double refcube(double &ra);
int main ()
{
    using namespace std;
    double x = 3.0;

    cout << cube(x);
    cout << " = cube of " << x << endl;
    cout << refcube(x);
    cout << " = cube of " << x << endl;
    // cin.get();
    return 0;
}

double cube(double a)
{
    a *= a * a;
    return a;
}

double refcube(double &ra)
{
    ra *= ra * ra;
    return ra; 
}

 输出:

27=cube of 3

27 = cube of 27

如果程序员的意图是让函数使用传递给它的信息,而不对这些信息进行修改,同时又想使用引用,则应使用常量引用。 

double refcube(const double &ra);

如果这样做,当编译器发现代码修改了ra的值,将报错。

如果实参与引用参数不匹配,C++将生成临时参数。 当前,仅当参数为const引用时,C++才允许这样做(c++ primer plus 264页)。因为假如类型不匹配的话,将创建临时参数,使用临时参数的数据改变不会影响传入的数据。换句话说,如果接受引用参数的函数的意图是修改作为参数传递的变量,则创建临时变量将阻止这种意图的实现。

注意:如果函数调用的参数不是左值或与相应的const引用参数的类型不匹配,则C++将创建类型正确的匿名变量,将函数调用的参数的值传递给该匿名变量,并让参数来引用该变量。

举个例子:

void swap(int &a,int &b)
{
    int temp;

    temp=a;
    a=b;
    b=temp;
}

long a=3,b=5;
swap(a,b);

这里的a,b与传入函数参数的a,b类型不匹配,因此编译器将创建两个临时int变量,将它们初始为3和5,然后交换临时变量的内容,而a和b保持不变。

 

 

然后我们来看下右值引用&&

C++ 引用& 和 右值(纯右值、将亡值)引用&& (1)_第2张图片

C++ 引用& 和 右值(纯右值、将亡值)引用&& (1)_第3张图片

C++ 引用& 和 右值(纯右值、将亡值)引用&& (1)_第4张图片

引用右值的只要目的是实现移动定义。

C++11 中为了引入强大的右值引用,将右值的概念进行了进一步的划分,分为:纯右值、将亡值。

纯右值(prvalue, pure rvalue),纯粹的右值,要么是纯粹的字面量,例如 10true;要么是求值结果相当于字面量或匿名临时对象,例如 1+2。非引用返回的临时变量、运算表达式产生的临时变量、原始字面量、Lambda 表达式都属于纯右值。

将亡值(xvalue, expiring value),是 C++11 为了引入右值引用而提出的概念(因此在传统 C++中,纯右值和右值是统一个概念),也就是即将被销毁、却能够被移动的值

将亡值可能稍有些难以理解,我们来看这样的代码:

std::vector foo() {
    std::vector temp = {1, 2, 3, 4};
    return temp;
}

std::vector v = foo();

在这样的代码中,函数 foo 的返回值 temp 在内部创建然后被赋值给 v,然而 v 获得这个对象时,会将整个 temp 拷贝一份,然后把 temp 销毁,如果这个 temp 非常大,这将造成大量额外的开销(这也就是传统 C++ 一直被诟病的问题)。在最后一行中,v 是左值、foo() 返回的值就是右值(也是纯右值)。

但是,v 可以被别的变量捕获到,而 foo() 产生的那个返回值作为一个临时值,一旦被 v 复制后,将立即被销毁,无法获取、也不能修改。

将亡值就定义了这样一种行为:临时的值能够被识别、同时又能够被移动。

更多例子也已参考下篇文章: 左值和右值、左值引用与右值引用(2)

你可能感兴趣的:(C/C++)