1 内联函数
编译过程的最终产品是可执行程序——由一组机器语言指令组成,运行程序时,操作系统将这些指令载入到计算机中,因此每条指令都有特定的内存地址。常规的调用函数过程是:执行到函数调用指令时,程序将在函数调用后立即储存该指令的地址,并将函数的参数复制到堆栈,跳到标记函数起点的内存单元,执行函数代码,然后跳回到地址被保存的指令处继续执行,这些过程在时间和空间方面都有开销。于是C++提供了另一种办法——内联函数,也就是说,编译器将使用相应的函数代码替换函数调用。如图所示:
注意,那些函数适合内联函数:只有一行代码的小型、非递归函数(函数不能调用自己)。另外,应有选择性的使用内联函数,如果执行函数代码的时间比处理函数调用机制的时间长,则节省的时间将只占整个过程很小的一部分,如果代码执行时间很短,则内联调用就可以节省非内联调用使用的大部分时间,但是由于这个过程相当快,因此尽管节省了该过程的大部分时间,但节省时间的绝对值并不大,除非该函数经常被调用。
内联函数定义和声明时使用关键字inline,语法形式如下:
inline 类型标识符 被调函数名(含类型说明的形参表);
下面举个内联函数的例子:
#include<iostream> inline double square(double x){return x*x;} int main() { double a,b; double c=13.0; a=square(5.0); b=square(4.5+7.5); std::cout<<"a="<<a<<",b="<<b<<"\n"; std::cout<<"c="<<c; std::cout<<",c squared = "<<square(c++)<<"\n"; std::cout<<"Now c="<<c<<"\n"; return 0; }
程序输出为:
2 引用变量
我们先来看一段代码,主要就是要交换两个数的值,我们分别用引用,指针和按值传递的方法:
#include<iostream> void swapr(int & a,int & b); void swapp(int * p,int * q); void swapv(int a,int b); int main() { using namespace std; int s1=300; int s2=350; cout<<"s1="<<s1; cout<<" s2="<<s2<<endl; cout<<"Using reference to swap contents:\n"; swapr(s1,s2); cout<<"s1="<<s1; cout<<" s2="<<s2<<endl; cout<<"Using pointers to swap contents again:\n"; swapp(&s1,&s2); cout<<"s1="<<s1; cout<<" s2="<<s2<<endl; cout<<"Trying to use passing by value:\n"; swapv(s1,s2); cout<<"s1="<<s1; cout<<" s2="<<s2<<endl; return 0; } void swapr(int & a,int & b)//use references { int temp; temp=a; a=b; b=temp; } void swapp(int * p,int * q)//use pointers { int temp; temp=*p; *p=*q; *q=temp; } void swapv(int a,int b)//try using values { int temp; temp=a; a=b; b=temp; }
程序输出为:
从输出的结果,我们可以看出,按引用和指针传递都实现了数据的交换,而按值传递没有实现。从程序来看,按引用传递swapr(s1,s2);和按值传递swapv(s1,s2);的代码是一样的,只有通过函数参数原型才能区分swapr(int & a,int & b);是按照引用传递的,另外地址运算符&使得按地址传递swapp(&s1,&s2);显得一目了然(类型声明int*p表明,p是一个int指针,因此与p对应的应为地址)。
下面来讲一下引用变量,C,C++使用&符号来指示变量的地址。C++给&符号赋予了另一个含义,将其用来声明引用。例如要将rodents作为rats变量的别名,可以这样做 :
很多人看到引用会想到指针,其实两者之间还是有区别的
(1)创建区别
int rats; int &rodents=rats;//rodents a reference int * prats=&rats;//prats a pointer
(2)声明区别引用必须在声明之初将其初始化。下面这样就不行:
引用,更接近于const指针,必须在创建时进行初始化,一旦与某个变量关联起来,就将一直效忠于它。看个例子:int rat; int & rodent; rodent=rat;//no,you can't do this
程序输出为:#include<iostream> int main() { using namespace std; int rats=101; int & rodents=rats; cout<<"rats="<<rats; cout<<",rodents="<<rodents<<endl; cout<<"rats address"<<&rats; cout<<",rodents address"<<&rodents<<endl; int bunnines=50; rodents=bunnines; cout<<"bunnines="<<bunnines; cout<<",rats="<<rats; cout<<",rodents="<<rodents<<endl; cout<<"bunnies address"<<&bunnines; cout<<",rodents address"<<&rodents<<endl; return 0; }
引用非常适合于结构和类,而引入引用也主要是为了用于这些类型,而不是基本的内置类型。引用的典型例子:(1)将引用用于结构
如果job为结构,可以这样编写函数原型:void set_pc(job & ft);//use a reference to a structure;如果不希望函数修改传入的结构,可使用const:void set_pc(const job & ft);
(2)将引用用于类对象
将类对象传递给函数时,C++通常的做法是使用引用。例如,可以通过使用引用,让函数将类string、ostream、istream、ofstream和ifstream等类的对象作为参数。举个例子:
程序输出为:#include<iostream> #include<string> #include <cctype> using namespace std; int display(string & str); void main() { string list; int a; cout<<"Enter a string (q to quit):"; getline(cin,list); a=display(list); while(a==1) { cout<<"Next string(q to quit):"; getline(cin,list); a=display(list); } } int display(string & str) { char s; for (int i=0;i<str.length();i++) { if (str[i]!='q') { s=toupper(str[i]); cout<<s; } else { cout<<"Bye."; cout<<endl; return 0; } } cout<<endl; return 1; }
综上所述,那么何时应使用引用,什么时候应使用指针,什么时候应按值传递?
(1)如果数据对象很小,如内置数据类型或小型结构,则按值传递。
(2)如果数据对象为数组,则使用指针,因为这是唯一的选择,并将指针声明为指向const的指针。
(3)如果数据对象是较大的结构,则使用const指针或者const引用,以提高程序效率。这样可以节省复制结构所需要的时间和空间。
(4)如果数据对象时类对象,则使用const引用。类设计的语义常常要求使用引用,这是C++新增这项特性的主要原因。因此,传递类对象的标准方式是按引用传递。