C++基本知识三
46.经常对未初始化的指针赋值NULL,叫做空值,其值本身没有多少含义,能用0代替,也能用1等代替,只要这些值和系统实际的有效地址不会冲突即可。不要自作聪明地认为NULL就是0,要判断的时候还是要老老实实地将其和NULL比较,别想当然地用什么!pt之类的写法,因为在某个特定的环境下,NULL可能不是0,而系统返回的是NULL而不是0,那是你的函数就会出现莫名其妙的错误。
47.内联函数的运行速度比常规函数稍快,但代价是需要占用更多的内存。如果程序在10个不同的地方调用同一内联函数,则该程序将包含该函数的10个代码拷贝。
48.常规函数和内联函数之间的主要区别不在于编写方式,而在于C++编译器如何将他们组合到程序中。
49.内联函数不能递归。
50.C语言使用预处理器语句#define来提供宏---内联函数的原始实现,注意内联函数和宏的作用方式不同。下面是一个计算平方的宏:#define SQUARE(X) X*X
这并不是通过传递参数实现的,而是通过文本替换来实现的---X是参数的符号标记,如:
a=SQUARE(5.0); 将替换成a=5.0*5.0;
b=SQUARE(4.5+7.5); 将被替换成 b=4.5+7.5*4.5+7.5;
注意,上面只有第一个例子才能正常工作。
51.引用变量的主要用途是用作函数的形参,通过将引用变量用作形参,函数将使用原来的数据,而不是其拷贝。这样除了指针外,引用也为函数处理大型数据结构提供了一种非常方便的途径。
52.注意引用变量的创建方式:int rat; int & rodent=rat;
其中&不是地址操作符,而是类型标识符的一部分,同指针的*。上述引用声明允许rat和rodent互换---他们指向相同的值和内存单元。
53.引用必须在声明时将其初始化,而不能像指针那样先声明再赋值。
int & rodent=rat; 就是下属代码的伪装表示:int *const pt=&rant;
54.如果实参与引用参数类型不匹配,C++将生成临时变量。现在,仅当参数为const引用时,C++才允许这样做,这是一种新的限制。临时变量在函数调用期间存在,在以后编译器可以随意将其删除。
如果接受引用参数的函数的意图是修改作为参数传递的变量,则创建临时变量将阻止这种意图的实现。对于形参为const引用的C++函数,如果实参不匹配,则其行为类似于按值传递;为确保原始数据不被修改,将使用临时变量存储该值。
55.使用const参数能够处理const和非const实参,否则将只能接受非const数据。
56.什么时候应该使用引用,什么时候应该使用指针呢?什么时候又应按值传递呢?下面是一些指导原则:
A.对于使用传递的值而不做修改的函数:
a.如果数据对象很小,如内置类型或小型结构,则按值传递;
b.如果数据对象是数组,则使用指针,因为这是唯一的选择,并将指针声明为指向const的指针。
c.如果数据对象为较大的结构,则使用const指针或const引用,以提高程序的效率。这样可以节省复制结构所需要的时间和空间。
d.如果数据对象是类对象,则使用const引用。类设计的语义常常要求使用引用,这是C++新增这项特性的主要原因。因此传递类对象参数的标准方式是按引用传递。
B.对于修改调用函数中数据的函数:
a.如果数据对象是内置对象,则使用指针或引用。
b.如果数据对象是数组,则只能使用指针。
c.如果数据对象是结构,则使用引用或结构。
d.如果数据对象是类对象,则使用引用。
57.默认参数指的是当函数调用中胜利了实参时自动使用的一个值。
要设置默认参数必须通过函数原型。由于编译器通过查看原型来了解函数所使用参数数目,因此函数原型也必须将可能的默认参数告知程序,方法是将值付给原型中的参数,而函数定义部分的函数头中可以省略设置默认参数。
对于带参数列表的函数,必须从右向左添加默认值。也就是说,要某个参数设置默认值,则必须为它右边的所有参数提供默认值。
实参按从左向右的顺序依次被付给相应的形参,而不能跳过任何参数。
58.函数重载的关键是函数的参数列表---也称为函数特征标。C++函数重载的唯一要求是函数的特征标不同。如果参数数目或/和参数类型不同,则特征标也不同。
59.对于没有函数重载的函数,假设类型不匹配时,如果形参可以兼容实参,则将会使用标准转换类型转换强制进行匹配。当有函数重载时,将不会发生强制转换,而会将其视为错误。
60.编译器在检查函数特征标时,将把类型引用和类型本身视为同一特征标,非函数重载时,匹配函数并不区分const和非const变量;而函数重载时将区分。
61.在函数调用时,将非const实参付给const形参是合法的;而将const实参付给非const形参是非法的。
62.是特征标(即函数参数列表),而不是函数原型(即函数返回的值的类型),是的可以对函数进行重载。当仅有返回值不同的两个函数不能共存,不是函数重载。
63.由于函数模板允许以通用类型(而不是具体类型)的方式编写程序,因此有时也被称为通用编程。函数模板不能缩短可执行程序。最终的代码不包含任何模板,而只包含为程序生成的实际函数。使用了n次模板将产生几个相应的实际函数存在于代码中。使用模板的好处是,它使生成多个函数定义更简单、更可靠。
64.并非所有的模板参数都必须是模板参数类型,可以是具体类型。
65.以下C++标准规定,值得注意:
a,对于给定的函数名,可以有非模板函数,模板函数和显式具体化模板函数以及他们的重载版本。
b.显示具体化的原型和定义应以template<>打头,并通过名称来指出类型。
c.具体化将覆盖常规模板,而非模板函数将覆盖具体化和常规模板。
模板:template <class Any> void swap(Any &a, Any &b);
显示具体化:template <> void swap<job>(job& ,job&);其中swap<job>中的<job>是可选的,因为函数参数类型表明,这是一个job的具体化。
66.给定一个模板,显式实例化的方法是:template void swap<int>(int,int);
显式具体化的方法是:template<> void swap<int>(int,int);
注意两个函数差一个”<>”,用以区分显示实例化和显式具体化。
试图在同一个编程单元中使用同一类型的显式实例化和具体化将出错。
隐式实例化、显示实例化和显示具体化统称为具体化。
67.函数重载时,指向非const数据的指针和引用优先与非const的指针和引用参数匹配。形参中使用const和非const之间的区别只适用于指针和引用指向的数据。
68.重载解析将寻找最匹配的函数。如果只存在一个这样的函数,则选择他;如果存在多个这样的函数,但其中只有一个是非模板函数,则选择该函数;如果存在多个适合的函数,并且他们都为模板函数,但其中有一个函数比其他函数更具体,则选择该函数。如果有多个同样合适的非模板函数或模板函数,但没有一个函数比其他函数更具体,则函数调用将是不确定的,因此是错误的。如果不存在匹配的函数,则也是错误的。
69.对于有多个参数的函数,重载解析时如果找到比其他可行函数都合适的函数,则选择该函数。一个函数要比其他函数都合适,其所有参数的匹配程度都必须不比其他函数差,同时至少有一个参数的匹配程度比其他函数都高。
70.头文件管理。在同一文件中只能将同一个头文件包含一次。但经常在不知情的情况下将头文件包含多次,有一种C++/c标准的方法可以避免多次包含同一头文件。他是基于预处理器编译指令#ifndef(即if not defined)的,线面代码片段:
#ifndef COORDIN_H_
#define COORDIN_H_
{…}
#endif
意味着仅当以前没有使用预处理器编译指令#define定义名称COORDIN_H_时,才处理#ifndef和#endif之间的语句。编译器首次遇到该文件时,名称COORDIN_H_没有定义(我们根据include文件名来选择名称,并加上一些下划线以创建一些其他地方不太可能被定义的名称)。在这种情况下编译器将查看#ifndef和#endif之间的内容,并读取定义COORDIN_H_的一行。如果在同一文件中遇到其他包含coordin.h的代码,编译器将知道COORDIN_H_已经被定义了,从而跳到#endif后面的一行。注意,这种方法并不能防止编译器将文件包含两次,而只是让他忽略除第一次包含之外的所有内容,大多数标准C和C++都使用这种防护方案。