C/C++ 引用用作函数参数,属性,临时变量,引用参数和const

  8.2.2 将引用用作函数参数

        引用经常被用作函数参数,使得函数中的变量名成为调用程序中的变量别名(就是说,此时函数的形参和实参是同一个东西)。这种传递参数的方式称为 按引用传递 。按引用传递 允许被调用的函数访问调用函数中的变量(在函数的形参中去操作实参的别名(引用),就等于直接操作实参)。C++新增的这项特性是对C语言的超越,C语言除了指针传递的方式,只能按值传递。按值传递导致被调用函数使用调用程序的值的拷贝。

#include <iostream>
using namespace std;
void swapr(int &a,int &b);
void swapp(int *p,int *q);
void swapv(int a,int b);
int main()
{
        int wallet1 = 300;
        int wallet2 = 350;
        cout << "wallet1 = " << wallet1 << endl;
        cout << "wallet2 = " << wallet2 << endl;
        cout << "Using references to swap contents:\n";
        swapr(wallet1,wallet2);
        cout << "wallet1 = " << wallet1 << endl;
        cout << "wallet2 = " << wallet2 << endl;
        cout << "Using pointers to swap contents again:\n";
        swapp(&wallet1,&wallet2);
        cout << "wallet1 = " << wallet1 << endl;
        cout << "wallet2 = " << wallet2 << endl;
        cout << "Tring to use passing by value:\n";
        swapv(wallet1,wallet2);
        cout << "wallet1 = " << wallet1 << endl;
        cout << "wallet2 = " << wallet2 << endl;
        return 0;
}
void swapr(int &a,int &b)
{
        int temp;
        temp = a;
        a = b;
        b= temp;
}
void swapp(int *p ,int *q)
{
        int temp;
        temp = *p;
        *p = *q;
        *q = temp;
}
void swapv(int a,int b)
{
        int temp;
        temp = a;
        a = b;
        b = temp;
}

 

可以看到使用引用和指针的方式,wallet1和wallet2正确交换。

按引用传递(swapr()),按值传递(swapv()),按地址传递(sawpp())。

swapr(wallet1,wallet2);
swapv(wallet1,wallet2);

        其中swapr()和swapv()在使用的时候看起来相同。只能通过函数原型或者函数定义才能知道swapr()是按引用传递的。而地址运算符(&)使得按地址传递swapp(&wallet1,&wallet2)一目了然(类型声明int *p表明了,p是一个int指针,所以与p对应的参数应为地址)。

        swapr()和swapv()的代码唯一的外在区别是声明函数参数的方式不同:

void swapr(int &a,int &b);
void swapv(int a,int b);

        swapr()和swapv()内在区别:

        在swapr()中,变量a和b是wallet1和wallet2的别名,所以交换a和b等于交换 wallet1和wallet2;但是在swapv()中,变量a和b是复制了wallet1和wallet2的值的新变量,因此交换了a和b的值并不会影响wallet1和wallet2的值。

        比较函数swapr()(传递引用)和swapp()(传递指针)。第一个区别是声明函数参数的方式不同:

void swapr(int & a,int & b);
void swapp(int * p,int & q);

        另一个区别是指针版本需要再函数使用p和q的整个过程中使用解引用运算符*。 

        应该在定义应用变量时对其进行初始化。函数调用使用实参初始化形参,因此函数的引用参数被初始化为函数调用传递的实参。也就是说,下面的函数调用将形参a和b分别初始化为wallet1和wallet2:

swapr(wallet1,wallet2);
        8.2.3 引用的属性和特别之处

        使用引用参数时,需要了解一些特点。

#include <iostream>
double cube(double a);
double refcube(double &ra);
using namespace std;
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;
        return 0;
}
double cube (double a)
{
        a *= a*a;
        return a;
}
double refcube (double &ra)
{
        ra *= ra*ra;
        return ra;
}

        使用两个函数来计算参数的立方,其中一个函数接受double类型的参数,另一个接受double引用。

        refcube()函数修改了main()中的x值,而cube()没有,这提醒我们为何通常按值传递。变量a位于cube()中,它被初始化为x的值,但是修改a并不会影响x。但由于refcube()使用了引用参数,因此修改ra实际上就是x。如果程序员的意图是让函数使用传递给它的信息,而不对这些信息进行修改,同时又想使用引用,则应该使用常量引用。例如,在这个例子中,在函数原型和函数头中使用const:

double refcube(const double &ra);

        如果这样写代码,那么但编译器发现代码修改了ra的值的时候,将生成错误消息。

        如果要编写类似上述的函数(即使用基本数值类型),应该采用按值传递的方法,而不要采用按引用传递的方式。当数据比较大(如结构和类)时,引用参数将很有用。

        按值传递的函数如上述的cube(),可以使用多种类型的实参。例如:

double z = cube(x + 2.0);
z = cube(8.0);
int k = 10;
z = cube(k);
double yo[3] = {2.2,3.3,4.4};
z = cube(yo[2]);

        如果将上面类似的参数传递给接受引用参数的函数,将会发现,传递引用的限制更严格,毕竟,如果ra是一个变量的别名,则实参应该是该变量。下面的代码会不合理,因为表达式x + 3.0不是变量:

double z = refcube(x + 3.0);

        例如,不能将值赋给表达式:

x + 3.0 = 5.0;

        如果试图使用像refcube(x + 3.0)这样的函数调用,在现代C++中,是错误的。大多数编译器都会指出这一点,然而有些老的编译器会发出这样的警告:

Warning:Temporary used for parameter 'ra' in call to refcube(double &)

        之所以做出这种比较温和的反应是由于早期C++确实允许将表达式传递给引用变量。有些情况下,还是这么做的。

        其结果如下:由于x+3.0不是double类型的变量,因此程序将创建一个临时的无名变量,并将其初始化为表达式x+3.0的值。然后,ra将成为该临时变量的引用。

        临时变量,引用参数和const

        如果实参与引用参数不匹配,C++将生成临时变量。当前,只有参数为const引用时,C++才允许这样做,但是以前不是这样的。

        如果引用参数是const,则编译器将在下面两种情况下生成临时变量:

1.实参的类型正确,但不是左值。

2.实参的类型不正确,但是可以转换成正确的类型。

        左值(左值参数):可以被引用的数据对象,例如,变量,数组元素,结构成员,引用和接触引用用的指针都是左值。

        非左值:包括字面常量(用引号括起的字符串除外,它们由其地址表示)和包含多项的表达式。

        在C语音中,左值最初指的是可以出现在赋值语句中左边的实体,但这是引入关键字const之前的情况。现在,常规变量和const变量都可以视为左值,因为可以通过地址访问它们。但常规变量属于可修改的左值,而const变量属于不可修改的左值。

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

        尽可能使用const

将引用参数声明为常量数据的引用的理由有三个:

1.使用const可以避免无意中的修改数据的编程错误;

2.使用const使函数能够处理const和非const实参,否则将只能接受非const数据;

3.使用const引用使函数能够正确生成并使用临时变量。

因此,尽可能地将引用形参声明为const。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爱吃三文鱼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值