第8章——函数探幽

来源:互联网 发布:javascript精通 编辑:程序博客网 时间:2024/06/14 05:39
 

l         C++内联函数

内联函数是C++为提高程序运行速度所做的一项改进。相对于非内联函数,系统内存只存在一个此函数。如果调用,就会使程序指针来回跳跃,需要一定的开销。而内联函数相当于静态函数,系统内存里在需要执行到的地方都有一个拷贝。基于内联函数这个特点,它应该经常被调用,或很短。

要使用这项特性:

n         在函数声明前加上关键字inline

n         在函数定义前加上关键字inline

l         引用变量

C++新增了一种复合类型——引用变量。引用是已定义的变量的别名。

创建引用变量

eg:int rats;

       cint & rodents=rats;

‘&’不是地址操作符,而是类型标识符的一部分。上述引用声明表示rats和rodents具有相同的值和内存单元。

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

eg:int rat;

       int &rodent;

       rodent=rat;

上述做法是禁止的。

 

引用经常被用作函数参数,使得函数中的变量名成为调用程序中的变量的别名。这种传递参数的方法称为按引用传递。C++新增的这项特性是对C语言的超越,C语言只能按值传递。按值传递导致被调用函数使用调用程序中的值的拷贝。

eg:main()函数中调用swap(a,b),在函数swap声明却是这样的swap(&a,&b)

以下是一个经典的说明按值传递,按引用传递和按指针传递的程序,其中引用和指针方法都成功实现了交换,但按值传递没有完成任务。

// swaps.cpp -- swapping with references and with pointers

#include <iostream>

void swapr(int & a, int & b);   // a, b are aliases for ints

void swapp(int * p, int * q);   // p, q are addresses of ints

void swapv(int a, int b);       // a, b are new variables

int main()

{

    using namespace std;

    int wallet1 = 300;

    int wallet2 = 350;

 

    cout << "wallet1 = $" << wallet1;

    cout << " wallet2 = $" << wallet2 << endl;

 

    cout << "Using references to swap contents:\n";

    swapr(wallet1, wallet2);   // pass variables

    cout << "wallet1 = $" << wallet1;

    cout << " wallet2 = $" << wallet2 << endl;

 

    cout << "Using pointers to swap contents again:\n";

    swapp(&wallet1, &wallet2); // pass addresses of variables

    cout << "wallet1 = $" << wallet1;

    cout << " wallet2 = $" << wallet2 << endl;

 

    cout << "Trying to use passing by value:\n";

    swapv(wallet1, wallet2);   // pass values of variables

    cout << "wallet1 = $" << wallet1;

    cout << " wallet2 = $" << wallet2 << endl;

    return 0;

}

 

void swapr(int & a, int & b)    // use references

{

    int temp;

 

    temp = a;       // use a, b for values of variables

    a = b;

    b = temp;

}

 

void swapp(int * p, int * q)    // use pointers

{

    int temp;

 

    temp = *p;      // use *p, *q for values of variables

    *p = *q;

    *q = temp;

}

 

void swapv(int a, int b)        // try using values

{

    int temp;

 

    temp = a;      // use a, b for values of variables

    a = b;

    b = temp;

}

 

按引用传递时,如果实参与引用参数不匹配,仅当参数为const引用时,C++将生成临时变量。什么时候生成临时变量呢?

n         实参的类型正确,但不是左值(变量,数组元素,结构成员,引用,指针)

n         实参的类型不正确,但可以转换成正确的类型

如果接受引用参数的函数意图修改作为参数传递的变量,则创建临时变量将阻止这种意图的实现。实际上,对于形参为const引用的C++函数,如果实参不匹配,则行为类似于按值传递,为确保原始数据不被修改,将使用临时变量来存储值。

 

将引用用于结构体

eg:struct sysop

{

       char name[26];

       char quote[64];

       int used;

}

const sysop &use(sysop & sysopref)

int main()

{

       …

       use(looper);

sysop copycat;

copycat=use(looper);

cout<<”use(looper):  ”<<use(looper).used;

       }

       上述程序可以说明:

n         按引用传递可以传递结构体

n         按引用可以用来返回值

n         使用函数调用来访问结构的成员

 

返回引用时注意的问题

n         避免返回函数中的局部变量。

n         可以返回一个作为参数传递给函数的引用

n         使用new来分配新的存储空间。

最后一点eg:

const sysop &clone(sysop &sysopref)

{

              sysop *psysop = new sysop;

              *psysop=sysopref;

              return *psysop;

}

 

将引用用于类对象

string input;

getline(cin,input);

上面的语句示了一种对string类初始化的办法。

eg:const string &version(string &s1,const string &s2);

int main()

{

string input;

string result;

result=version(input,”***”);

       }

       上述代码这明可以将C-风格字符串用作string对象引用参数,即string类定义了一种转换:字符串字面量、以空字符串结尾的char数组或指向char的指针变量可以转换成string类对象。

 

对象、继承、引用

能够从一个类传递给别一个类的语言特性被称为继承,继承的另一特性是,基类引用可以指向派生类引用。一个接受基类引用作为参数的函数,调用该函数时,可以将基类对象作为参数,也可以将派生类对象作为参数。

ios_base::fmtflags是一种存储格式化信息的数据类型。它可由setf()函数返回当前的有效设置。

ios_base::fmtflags initial;

initial=os.setf(ios_base::fixed);//定点显示模式

os.precision(0)//显示0位小数

os.setf(ios::showpoint);//显示小数点模式

os.width(12)//下一次输出使用的字段宽度,作用只限于显示下一个值,然后恢复默认。

 

使用按引用传递方式的指导原则:

n         数据对象是类对象,使用按引用

n         数据对象较大,可以使用按引用

n         数据对象是数组,要使用指针

 

l         默认参数

C++的函数可以有默认的参数。默认参数指的是当函数调用中省略了实参时自动使用的一个值。设置默认值的方法是将值赋给原型中的参数。对于带参数列表的函数,必须从右向左添加默认值,实参按从左到右的顺序依次被赋给相应的形参,而不能跳过任何参数。但如果有默认值,可以省略后面的参数。

eg:char *left(const char *str,int n=1);

int main()

{

       …

       char sample[80];

       …

       char *ps=left(sample)

}

l         函数重载

函数多态是C++新增的功能。默认参数让您能够使用不同数目的参数调用同一个函数,而函数多态(函数重载)让您能够使用多个同名的函数。

函数重载的关键是函数的参数列表——也称为函数特征标。如果两个函数的参数数目和类型相同,同时参数的排列顺序也相同,则它们的特征标相同。

上图说明在有const变量的情况下,参数对应情况时,const变量对应const变量,非const变量对应非const变量。但是如果调用函数中与函数原型中的const变量不能相对应时:调用函数的非const变量可以对应函数原型中的const变量,但是调用函数中的const变量不能对应函数原型中的非const变量。

 

仅当函数基本上执行相同的任务,但使用不同形式的数据时,才应采用函数重载。

l         函数模板

函数模板是C++的一项新特性。

eg:template<class Any>

void swap(Any &a,Any &b)

{

       Any temp;

       temp=a;

       a=b;

       b=temp;

}

关键字template和class是必需的,除非可以使用关键字typename代替class。类型名(Any)可以任意选择。

模板使用

eg:template <class Any>

void swap(Any &a,Any &b);

int main()

{

       …

       int i,j;

       …

       swap(i,j);

       …

}

template <class Any>

void swap(Any &a,Any &b)

{

Any temp;

       temp=a;

       a=b;

       b=temp;

}

 

可以像重载常规函数那样重载模板定义。和常规重载一样,被重载的模板的函数特征标必须不同。

 

模板可以被实例化和具体化。在代码中包含函数模板本身不会生成函数定义,它只是一个用于生成函数定义的方案。编译器使用模板为特定类型生成函数定义时,得到的是模板实例。在上述实例中,swap(i,j)导致编译器生成swap()的一个实例。这种实例化的方式被称为隐式实例化。

显示实例化

template void swap<int>(int,int);

编译器看到上述声明后,将使用swap()模板生成一个使用int类型的实例。该声明的意思即是“使用swap()模板生成int类型的函数定义”

 

显式具体化

template <> void swap<int>(int&,int&);

template<>void swap(int &,int&);

这些声明的意思是“不要使用swap()模板来生成定义,而应使用独立的、专门的函数定义显式地为int类型生成函数定义”。它与实例化的区别是显式具体化声明在关键字template后包含”<>”,而显式实例化没有。

 

在有多个匹配的函数原型时,若有非模板函数,则(编译器)选择该函数。若皆为模板函数,则显式具体化将优先于模板生成的版本。试图在同一个编程单元中使用同一种类型的显式实例化和显式具体化将出错。