c++提供了许多新的函数特性,包括内联函数、按引用传递变量、默认的参数值、函数重载(多态)以及模板函数。
c++内联函数
-
内联函数是c++为提高程序运行速度所做的一项改进。常规函数和内联函数之间的主要区别不在于编程方式,而在于c++编译器如何将它们组合到程序中。
-
编译过程的最终产品是可执行程序----由一组机器语言指令组成。运行程序时,操作系统将这些指令载入到计算机内存中,因此每条指令都有特定内存地址。计算机随后将逐步执行这些指令。有时将跳过一些指令,向前或向后跳到特定地址。
-
c++内联函数提供了另一种选择。内联函数的编译代码与其他程序代码“内联”起来了。也就是说,编译器将使用相应的函数代码替换函数调用。对于内联代码,程序无需跳到另一个位置处执行代码,再跳回来。因此,内联函数的运行速度比常规函数稍快,但代价是需要占用更多的内存。
-
要使用这项特性,必须采取下述措施之一:
i 在函数声明前加上关键字inline;
ii 在函数定义前加上关键字inline。
通常的做法是省略原型,将整个定义(即函数头和所有函数代码)放在本应提供原型的地方。 -
程序员请求将函数作为内联函数时,编译器不一定会满足这种要求。它可能认为该函数过大或注意到函数调用了自己(内联函数不能递归),因此不将其作为内联函数;而有些编译器没有启用或实现这种特性。
内联函数square()(计算参数平方)演示了内联技术。注意到整个函数定义都放在一行中,但并不一定非得这样做。然而,如果函数定义占用多行(假定没有使用冗长的标识符),则将其作为内联函数就不太合适。
inline double square(double x){return x * x}
int main()
{
...
cout << square(c++);
...
}
- inline工具是c++新增的特性,c++使用#define来提供宏即内联代码的原始实现。如,下面是一个计算平方的宏:
#define SQUARE(X) X*X
这并不是通过传递参数实现的,而是通过文本替换来实现的。这种情况会出现问题。如:
b = SQUARE(4.2 + 4.1);
结果为b = 4.2 + 4.1 x 4.2 + 4.1
而不是b =(4.2+4.1)x(4.2+4.1)。
因此,如果使用了类似函数的功能,应将它们转化为c++内联函数。
引用变量
- 引用是已知定义的变量的别名。引用变量的主要用途是用作函数的形参。通过将引用变量作为参数,函数将使用原始数据,而不是其副本。这样除指针之外,引用也为函数处理大型结构提供一种非常方便的途径。
- c和c++使用&符号来指示变量的地址。c++赋予&符号另一个定义,将其用来声明引用。如将B作为A变量的别名:
int A;
int & B = A;
上述引用声明允许将A和B互换—它们指向相同的值和内存单元。其中,&不是地址运算符,而是类型标识符的一部分。就像生命中的char*指的是指向char的指针一样,int &指的是执行int的引用。
- 引用看上去就像伪装表示的指针。实际上,引用还是不同于指针。除了表示法不同外,还有其他差别,其中之一是,必须在声明引用时将其初始化,而不能像指针那样,先声明,再赋值。
- 引用更接近const指针,必须在创建时进行初始化,一旦与某个变量关联起来,就将一直效忠它。也就是说:
int & Bs = As;
实际上是下列代码的伪装表示:
int * const pr = &As;
- 引用经常被用作函数参数,使得函数中的变量名成为调用程序中的变量的别名。这种传递参数的方法称为按引用传递。按引用传递允许被调用的函数能够访问调用函数中的变量。
- 引用变量传递时,下面代码不合理
double z = refcube(x + 3.0);
因为表达式x + 3.0不是变量
其中refcube()为接受引用参数的函数
- 左值参数是可被引用的数据对象,如变量、数组元素、结构成员、引用和解除引用都是左值。非左值包括字面常量(用引号括起的字符串除外,他们由其地址表示)和包含多项的表达式。
- 常规变量和const变量都可视为左值,因为可通过地址访问。常规变量属于可修改的左值,而const变量属于不可修改的左值。
- 将引用参数声明为常量数据的引用的理由有三个:
i 使用const可以避免无意中修改数据的编程错误;
ii 使用const使函数能够处理const和非const实参,否则将只能接受非const数据;
iii 使用const引用使函数能够正确生成并使用临时变量。
因此,应尽可能将引用形参声明为const。 - c++11新增了另一种引用----右值引用。这种引用可指向右值,是使用&&声明的:
double && rref = std::sqrt(36.00);
double j = 15.0;
double && jref = 2.0 * j + 18.5;
std::cout << rref << "\n"; //display 6.0
std::cout << jref << "\n"; //display 48.5
新增右值引用的主要目的是,让库设计人员能够提供有操作更有效的实现。
- 引用非常适合用于结构和类(c++的用户定义类型)。
- 将引用用于结构
如有下结构定义:
struct free_throws
{
....
}
则可以这样编写原型函数,在函数中将指向该结构的应用作为参数:
void set_pc(free_throws & ft);
如果不希望函数参数修改传入的结构,可使用const:
void set_pc(const free_throws & ft);
- 为何要返回引用:
void set_pc(free_throws & ft);
free_throws & accumulate(free_throws & A, const free_throws & B);
struct free_throws
{
std::string name;
int made;
int attempts;
float percent;
}
void main()
{
free_throws team = {"MMD", 0, 0};
free_throws five = {"hello", 23, 10};
...
}
void set_pc(free_throws & ft)
{}
free_throws & accumulate(free_throws & A, const free_throws & B)
{
A.attempts += B.attempts;
A.made += B.made;
set_pc(A);
return A;
}
...
现在看下面的语句:
dup = accumulate(team, five);
如果accumulate()返回一个结构,而不是结构的引用,将把整个结构复制到一个临时位置,再将这个拷贝复制给dup。但在返回值引用时,将直接把team复制到dup,其效率更高。
- 将类对象传递给函数时,c++通常的做法是使用引用。
- 假设实参的类型与引用参数类型不匹配,但可被转换为引用类型,程序将创建一个正确类型的临时变量,使用转换后的实参值来初始化它,然后传递一个指向该临时变量的引用。
- 使得能够将特性从一个类传递给另一个类的语言特性被称为继承。
- 派生类继承了基类的方法,意味着派生类可以使用基类的特性。继承的另一个特征是,基类引用可以指向派生类对象,而无需进行强制类型转换。这种特征的一个实际结果是,可以定义一个接受基类引用作为参数的函数,调用该函数时,可以将基类对象作为参数,也可以将派生类对象作为参数。
- 方法setf()让你能够设置各种格式化状态。
- 何时使用引用参数:
默认参数
- 对于带参数列表的函数,必须从右向左添加默认值。也就是说,要为某个参数设置默认值,则必须为它右边的所有参数提供默认值。
- 实参按从左到右的顺序依次被赋给相应的形参,而不能跳过任何参数。
函数重载
- 函数重载的关键是函数的参数列表----也称为函数特征标。
函数模板
- 函数模板允许以任意类型的方式来定义函数。如下列交换模板:
template <typename AnyType>
void Swap(AnyType &a, AnyType &b)
{
AnyType temp;
temp = a;
a = b;
b = temp;
}
第一行指出,要建立一个模板,并将类型命名为Anytype。关键字template和typename是必须的,除非使用关键字class代替typename。另外,必须使用尖括号。类型名可以任意选择(这里为AnyType),只要遵循c++命名规则即可;许多程序员都是用简单的名称,如T。
- 如果需要多个将同一种算法用于不同类型的函数,请使用模板。
- 模板的局限性,编写的模板函数很可能无法处理某些类型。
- 在代码中包含函数模板本身并不会生成函数定义,它只是一个用于生成函数定义的方案。编译器使用模板为特定类型生成函数定义时,得到的是模板实例。模板并非函数定义,但使用int模板实例是函数定义。这种实例化方式被称为隐式实例化。
- 最初,编译器只能通过隐式实例化,来使用模板生成函数定义,但现在c++还允许显示实例化。
- 显示具体化声明在关键字template后包含<>,而显示实例化没有。
- 试图在同一个文件(或转换单元)中使用同一种类型的显示实例和显示具体化将出错。
- 隐式实例化、显示实例化和显示具体化统称为具体化。它们的相同之处在于,它们表示的都是使用具体类型的函数定义,而不是通用描述。
- 引入显示实例化后,必须使用新的语法-----在声明中使用前缀template和template<>,以区分显示实例化和显示具体化。
template <> void Swap<job>(job &, job &); //显示具体化
template void Swap<job>(job &, job &); //显示实例化
- 关键字decltype:c++11新增的关键字,作用如下
int x;
decltype(x) y; //让y与x类型一致
decltype提供的参数可以是表达式:
decltype(x + y) z; //让z与x+y类型一致