C++内联函数
内联函数是为了提高程序运行速度所做的一项改进。常规函数与内联函数的主要区别不在于编写方式,而在于C++编译器如何将它们组合到程序中。
对于 C++内联函数,编译器将使用相应的函数代码替换函数调用,程序无需跳到另一个位置处执行代码,再跳回来。因此内联函数的运行速度比常规函数稍快,但代价是需要占用更多的内存。所以应该有选择地使用内联函数。
要使用这项特性,必须采用下述措施之一:
- 在函数声明前加上关键字inline;
- 在函数定义前加上关键字inline;
程序员请求将函数作为内联函数,编译器并不一定会满足这种请求,它可能认为该函数过大或函数调用了自己(内联函数不能递归),因此不将其作为内联函数
#include<iostream>
using namespace std;
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);
cout << "a = " << a << " ,b = " << b << endl;
cout << "c = " <<square(c)<< endl;
return 0;
}
引用变量
C++新增了一种复合类型——引用变量。引用是已定义的变量的别名(另一个名称)。(例如,如果将twain作为clement变量的引用,则可以交替使用twain和clement来表示该变量)。引用变量的主要用途是用作函数的形参,通过将引用变量用作参数,函数将使用原始数据而不是其副本。
创建引用变量
C和C++使用&符号来指示变量的地址。C++给&符号赋予了另一个含义,将其用来声明引用。
例如要将rodents作为rats变量的别名,可以这样做:
int rats;
int & rodents = rats;
其中&不是地址运算符,而是类型标识符的一部分。就像char*指的是指向char指针,int& 指的是指向int的引用。
上述引用声明允许将rats和rodents互换——它们指向相同的值和内存单元
#include<iostream>
using namespace std;
int main()
{
int rats = 101;
int& rodents = rats;
cout << "rats = " << rats << " at " << &rats<<endl;
cout << ",rodents = " << rodents <<" at "<<&rodents<< endl;
rodents++;
cout << endl;
cout << "rats = " << rats << " at " << &rats << endl;
cout << ",rodents = " << rodents << " at " << &rodents << endl;
}
注意区分&在下面两个语句中的作用是不一样的
int &rodents = rats;
cout << "at" << &rodents << endl;
第一个语句中&运算符是引用的意思;第二个语句中&运算符是取地址运算符
必须在声明引用时将其初始化,一旦与某个变量关联起来,就一直效忠于它。
将引用用作函数参数
引用经常被用作函数参数,使得函数中的变量名称为调用程序中的变量别名,这种传递参数的方式称为按引用传递。
下面用三种方法交换两个变量的值。
#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;
cout << " wallet2 = $" << wallet2 << endl;
cout << "Using reference to swap contents:\n";
swapr(wallet1, wallet2);
cout << "wallet1 = $" << wallet1;
cout << " wallet2 = $" << wallet2 << endl;
cout << "Using pointers to swap contents again:\n";
swapp(&wallet1, &wallet2);
cout << "wallet1 = $" << wallet1;
cout << " wallet2 = $" << wallet2 << endl;
cout << "Trying to use passing by value:\n";
swapv(wallet1, wallet2);
cout << "wallet1 = $" << wallet1;
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;
}
应尽可能使用const
将引用参数声明为常量数据的引用的理由有三个:
- 使用const可以避免无意中修改数据的编程错误
- 使用const使函数能够处理const和非const实参,否则将只能接受非const数据
- 使用const引用使函数能够正确生成并使用临时变量
何时使用引用参数?
使用引用参数的主要原因有两个:
- 程序员能够修改调用函数中的数据对象
- 通过传递引用而不是整个数据对象,可以提高程序的运行速度
当数据对象较大时(如结构和类对象),第二个原因最重要,这些也是使用指针参数的原因。
那么什么时候应使用引用、什么时候应使用指针呢?什么时候应按值传递呢?下面是一些指导原则:
对于使用传递的值而不作修改的函数。
- 如果数据对象很小,如内置数据类型或小型结构,则按值传递
- 如果数据对象是数组,则使用指针,因为这是唯一的选择,并将指针声明为指向const的指针
- 如果数据对象是较大的结构,则使用const指针或const引用,以提高程序的效率,这样可以节省复制结构所需的时间和空间
- 如果数据对象是类对象,则使用const引用。类设计的语义常常要求使用引用。因此传递类对象参数的标准方式是按引用传递
对于修改调用函数中数据的函数
- 如果数据对象是内置数据类型,则使用指针
- 如果数据对象是数组,则只能使用指针
- 如果数据对象是结构,则使用引用或指针
- 如果数据对象是类对象,则使用引用
函数重载
函数多态是C++在C语言基础上新增的功能。函数多态(重载)让程序员能够使用多个同名的函数。多态和重载这两个术语是同一回事,我们通常使用函数重载。
C++使用上下文来确定要使用的重载函数版本
函数重载的关键是函数的参数列表——也称为函数特征标。如果两个函数的参数数目和类型相同,同时参数的排列顺序也相同,则它们的特征标相同,而变量名是无关紧要的;如果参数数目或参数类型不同,则特征标也不同,例如,可以定义一组原型如下的print()函数:
void print(const char *str, int width); #1
void print(double d, int width); #2
void print(long l, int width); #3
void print(int i, int width); #4
void print(const char *str); #5
使用print()函数时,编译器将根据所采取的用法使用有相应特征标的原型:
print("Pank",15); // use #1
print("Syrup"); // use #5
print(1999.0,10); // use #2
print(1999,12); // use #4
print(1999L,15); // use #3
虽然函数重载很诱人,但是不要滥用。仅当函数基本上执行相同的任务,但使用不同形式的数据时,才应采用函数重载。
函数模板
现在的C++编译器实现了C++新增的一项特性——函数模板。函数模板是通用的函数描述,也就是说它们使用泛型来定义函数,其中的泛型可用具体的类型(如 int 或 double)替换。通过将类型作为参数传递给模板,可使编译器生成该类型的函数。
假如已经定义了一个交换两个int值的函数,但是现在需要交换两个double值,则一种方法是复制原来的代码,并用double替换所有的int。如果需要交换两个char,可以再一次使用同样的技术。
C++的函数模板功能能自动完成这一过程
函数模板允许以任意类型的方式来定义函数,例如可以这样建立一个交换模板
Template<typename AnyType>
void Swap(AnyType &a,AnyType &b)
{
AnyType temp;
temp=a;
a=b;
b=temp;
}
第一行指出要建立一个模板,并将其类型命名为AnyType。关键字template和typename是必需的,除非可以使用关键字class代替typename。另外必须使用尖括号。模板并不创建任何函数,而只是告诉编译器如何定义函数。