第八天(函数进阶 · 一)

      这是C++函数的独特内容,一部分是与java相似,需时较多,分两天吧。

 

2011-10-10(Adventures In Functions I)
1、内联函数(inline function)。函数的一种,用作提高C++程序的运行速度。普通函数的调用是这样:首先要明白的是,程序里的每条指令都有其内存地址,当程序执行到要调用程序的指令是,系统会将这条指令地址保存,而后跳到要调用函数代码内存单元,执行完后再跳回,每次遇到函数调用均如此。如此跳来跳去是要时间的。所以内联函数的方法时将函数代码直接镶嵌到主函数中,当然,如果是递归函数是不能镶嵌的。这样做的结果是占用更大的内存。
可以想象,当一个函数执行的时间远远大于调用的时间时是不适合使用内联函数的;当一个函数短而经常使用则非常适合。声明一个函数是内联函数,使用关键inline,一般的做法是将内联函数的声明和定义放在与本放函数原型的地方:

#include <iostream>

inline int add(int a, int b){return a + b;}

int main()
{
    using namespace std;
    int a, b, c = 20;
    a = add(4,6);
    b = add(3+4,3);
    if(c == add(++a,++b))
        cout << "c is equals to a + b\n";
    else 
        cout << "c is unequals to a + b\n";
    return 0;
}
内联函数的用法和宏很像,不过内联函数更为方便。
2、引用变量(reference variable)。C++中用符号&来声明一个引用变量,主要用于在变量为函数的参数是,传递的是原始数据而不是其拷贝,这样就可以改变变量的值。
I、基本语法。通过一程序说明:
#include <iostream>

int main()
{
    using namespace std;
    int a = 5,b = 10;
    int& ar = a;
    cout << "a = " << a << "; ar = " << ar << endl;
    ar++;
    cout << "After incrementing, a = " << a << "; ar = " << ar << endl;
    cout << "a adress = " << &a << "; ar adress = " << &ar << endl;

    ar = b; 
    cout << "\nAfter assigning, a = " << a << "; ar = " << ar << "; b = " << b << endl; 
    cout << "a adress = " << &a << "; ar adress = " << &ar << "; b adress = " << &b << endl;
    return 0;
}
输出:
                   a = 5; ar = 5
        After incrementing, a = 6; ar = 6
        a adress = 0012FF60; ar adress = 0012FF60

        After assigning, a = 10; ar = 10; b = 10
        a adress = 0012FF60; ar adress = 0012FF60; b adress = 0012FF54

①6、7行,引用必须在器声明时初始化,即不能使用下面语句:
int a = 5;
int& b;
b = a;
②对应于的操作即对其变量本身;
③引用不能重新改变其“指向”。第13行,ar = b等价于a = b;
II、引用作为函数参数。如前述,引用是直接使用原始数据而非拷贝,这种传递方式称为按引用传递。举例:
#include <iostream>

void swap(int&, int&);
int main()
{
    using namespace std;
    int a = 5, b = 10;
    cout << "a = " << a << "; b = " << b << endl;
    swap(a,b);
    cout << "After swapping, a = " << a << "; b = " << b << endl;
    return 0;
}

void swap(int& a, int& b)
{
    int temp;
    temp = a; a = b; b = temp;
}
输出: 
                      a = 5; b = 10
                      After swapping, a = 10; b = 5

不同之处在于多了一个符号&,其他操作不变。对函数参数的操作,即是对数据本体的操作,因为参数就是数据的引用。
按引用传递也有尴尬之时:
#include <iostream>

int doubleValue(int&);
int main()
{
    using namespace std;
    int a = 5;
    cout << "a = " << a << endl;
    cout << "2 * " << a << "= " << doubleValue(a) << endl;
    return 0;
}

int doubleValue(int& a)
{
    a = 2 * a;
    return a;
}
输出:
                        a = 5
                        2 * 10= 10

即经过函数调用后,a的值亦随之而变。如此相当于将函数的代码直接写在主函数中,无局部变量之说。到目前为止,所接触到的按引用传递函数的参数都是“确定”是引用类型。即不会像下面的情况(以上面的doubleValue为例):
doubleValue(a + 5);
long c = 6;
doubleValue(c);             //type mismatching
编译会对此上报错误,除非是将形参定义为const,即:
                          doubleValue(const int& a);
当然,定义为const后,函数就不能改变形参的值了。其工作原理是这样的,编译将生成一个匿名变量用于储存“非法”值,并是a成为这个匿名变量的引用,而再继续操作。在说明为什么必须要定义为const前,先说明怎么样的值传递到函数时要创建一个匿名变量:
①实参类型正确,但不是可以被引用的对象(如上面的a + 5);
②实参类型不正确,但可转换为正确类型(如上面的6L);
下面在说形参为何必须为const。首先假设不为const时发生的情况:上面的swap(int&, int&),加入传了两个long型值,执行这函数时要创建两个匿名变量,那么交换的不是a、b本体而是这两个匿名变量,整个函数将无意义。所以设置为const后,参数将不能改变,匿名变量也不能改变,避免了这种无意义的事情发生。
但如此设置为const后,将极大的限制了函数的功能,与一般函数无异。
3、返回结构体引用。
#include <iostream>

using namespace std;
struct SCII
{
    char race[10];
    int rank;
};

const SCII& rankUp(SCII&);
int main()
{
    SCII MVP1 = {"Terran", 3};
    SCII MVP2 = rankUp(MVP1);
    cout << "MVP1.rank = " << MVP1.rank << endl;
    cout << "MVP2.rank = " << MVP2.rank << endl;
    cout << "rankUp(MVP1).rank = " << rankUp(MVP1).rank << endl;
    return 0;
}

const SCII& rankUp(SCII& player)
{
    cout << "Race: " << player.race << endl;
    player.rank --;
    return player;
}
                     Race: Terran
                     MVP1.rank = 2 
                     MVP2.rank = 2 
                     Race: Terran
                     rankUp(MVP1).rank = 1 

①这里要注意的是按引用传递的性质:对参数的操作即对数据本体的操作;
虽名为“返回结构体引用”,但返回的还是它的真身,因为引用只是一个数据的第二个名字。所以函数返回的是一个真真实实的MVP1被修改以后的结构体。
因为返回的是被修改后的MVP1,而且函数本身也要求返回这样类型,所以一下语句:
const SCII& rankUp(SCII& player)
{
    cout << "Race: " << player.race << endl;
    player.rank --;
    SCII MVP = player;
    return MVP;
}
编译器将发出警告:warning: 返回局部变量或临时变量的地址。应避免返回临时变量。当然可以这样解决: 
const SCII& rankUp(SCII& player)
{
    cout << "Race: " << player.race << endl;
    player.rank --;
    SCII *MVP = new SCII;
    *MVP = player;
    return *MVP;
}
不过这么搞很容忘记使用delete()来释放内存。
④使用const的原因:不能对返回值进行直接操作:
                   rankUp(MVP1).rank = 10;        //invalid
这个等效于:
                   rankUp(MVP1);
                   MVP1.rank = 10;

如此看来逻辑比较清楚,不容易犯错。
4、一个返回局部变量的例子。
#include <iostream>
#include <string>
using namespace std;

string& crash(string&);
int main()
{
    string s1 = "abc";
    string s2 = crash(s1);
    return 0;
}
string& crash(string& str)
{
    string temp = str;
    return temp;
}
如3③,将发出不能返回临时变量的警告。运行,程序直接崩溃。因为返回的是一个引用,而程序调用crash()后,临时变量temp将不复存在,所以程序要做的是,引用一个已经释放的内存,导致崩溃。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值