c++关键字详解

来源:互联网 发布:微信开发源码 编辑:程序博客网 时间:2024/06/05 17:11

 inline


c++中引入inline关键字的原因:


  inline 关键字用来定义一个类的内联函数,引入它的主要原因是用它替代c中表达式形式的宏定义。


表达式形式的宏定义一例:  

 #define expressionname(var1,var2) (var1+var2)*(var1-var2)


宏在某些场合能得到类似于函数的功能,如上是一个常见的例子:但我们至少有一打理由拒绝使用它,以下是最明显的:
① 宏缺少类型安全检测,如:
ADD ('A' 0. 0l);
这样的调用将被解释为合法,而事实上,很少的用户期望能写出这样的语句;

② 宏不会为参数引入临时拷贝

③ 宏不具有地址,例如可能在一个计算器程序中有:
case ' +': Operator = & ADD;
并不能得到合理解释。


  那么为什么在c中还要使用这种形式宏定义呢?c语言是一个效率很高的语言,这种宏定义在形式及使用上像一个函数,但它使用预处理器实现,没有了参数压栈,代码生成等一系列的操作,因此,效率很高,这是它在c中被使用的一个主要原因。


  但宏定义只是在形式上类似于一个函数,在使用它时,仅仅只是做预处理器符号表中的简单替换,因此它不能进行参数有效性的检测,也就不能享受c++编译器严格类型检查的好处,另外它的返回值也不能被强制转换为可转换的合适的类型,这样,它的使用就存在着一系列的隐患和局限性。


  在c++中引入了类及类的访问控制,这样,如果一个操作或者说一个表达式涉及到类的保护成员或私有成员,你就不可能使用这种宏定义来实现(因为无法将this指针放在合适的位置)。


  inline 推出的目的,也正是为了取代这种表达式形式的宏定义,它消除了它的缺点,同时又很好地继承了它的优点。


为什么inline能很好地取代表达式形式的预定义呢?


对应于上面的1-3点,阐述如下:


1. inline 定义的类的内联函数,函数的代码被放入符号表中,在使用时直接进行替换,(像宏一样展开),没有了调用的开销,效率也很高。  


  2. 很明显,类的内联函数也是一个真正的函数,编译器在调用一个内联函数时,会首先检查它的参数的类型,保证调用正确。然后进行一系列的相关检查,就像对待任何一个真正的函数一样。这样就消除了它的隐患和局限性。


  3. inline 可以作为某个类的成员函数,当然就可以在其中使用所在类的保护成员及私有成员。


在何时使用inline函数:


首先,你可以使用inline函数完全取代表达式形式的宏定义。  


  另外要注意,内联函数一般只会用在函数内容非常简单的时候,这是因为,内联函数的代码会在任何调用它的地方展开,如果函数太复杂,代码膨胀带来的恶果很可能会大于效率的提高带来的益处。并且内联函数本身不能直接调用递归函数(自己内部还调用自己的函数)。内联函数最重要的使用地方是用于类的存取函数。


如何使用类的inline函数:


简单提一下inline 的使用吧:


在类中定义这种函数:


class classname{


.....


getwidth(){return m_lpicwidth;}; // 如果在类中直接定义,可以不使用inline修饰


....
}


在类中声明,在类外定义:


class classname{
....

getwidth(); // 如果在类中直接定义,可以不使用inline修饰

....

}

inline getwidth(){


return m_lpicwidth;
}  


在本篇中,谈了一种特殊的函数,类的inline函数,它的源起和特点在某种说法上与const很类似,可以与const搭配起来看。


Key word 四:virtual


面向对象程序设计的基本观点是用程式来仿真大千世界,这使得它的各种根本特性非常人性化,如封装、继承、多态等等,而虚拟函数就是c++中实现多态性的主将。为了实现多态性,c++编译器也革命性地提供了动态联编(或叫晚捆绑)这一特征。


  虚拟函数亦是mfc编程的关键所在,mfc编程主要有两种方法:一是响应各种消息,进行对应的消息处理。二就是重载并改写虚拟函数,来实现自己的某些要求或改变系统的某些默认处理。


  虚函数的地位是如此的重要,对它进行穷根究底,力求能知其然并知其所以然 对我们编程能力的提高大有好处。下面且听我道来。


  多态性和动态联编的实现过程分析


一、基础略提(限于篇幅,请参阅相应的c++书籍):


  1、多态性:使用基础类的指针动态调用其派生类中函数的特性。
  2、动态联编:在运行阶段,才将函数的调用与对应的函数体进行连接的方式,又叫运行时联编或晚捆绑。


二、过程描述:


  1、编译器发现一个类中有虚函数,编译器会立即为此类生成虚拟函数表 vtable(后面有对vtable的分析)。虚拟函数表的各表项为指向对应虚拟函数的指针。
  2、编译器在此类中隐含插入一个指针vptr(对vc编译器来说,它插在类的第一个位置上)。


  有一个办法可以让你感知这个隐含指针的存在,虽然你不能在类中直接看到它,但你可以比较一下含有虚拟函数时的类的尺寸和没有虚拟函数时的类的尺寸,你能够发现,这个指针确实存在。

class cnovirtualfun
{
   private:
    long lmember;
   public:
    long getmembervalue();
}

class chavevirtualfun
{
   private:
     long lmember;
   public:
     virtual long getmembervalue();
}

 cnovirtualfun obj;
 sizeof(obj) -> == 4;
 chavevirtualfun obj;
 sizeof(obj) -> == 8;


  
  3、在调用此类的构造函数时,在类的构造函数中,编译器会隐含执行vptr与vtable的关联代码,将vptr指向对应的vtable。这就将类与此类的vtable联系了起来。


  4、在调用类的构造函数时,指向基础类的指针此时已经变成指向具体的类的this指针,这样依靠此this指针即可得到正确的vtable,从而实现了多态性。在此时才能真正与函数体进行连接,这就是动态联编。


三、vtable 分析:


  分析1:虚拟函数表包含此类及其父类的所有虚拟函数的地址。如果它没有重载父类的虚拟函数,vtable中对应表项指向其父类的此函数。反之,指向重载后的此函数。
  分析2:虚拟函数被继承后仍旧是虚拟函数,虚拟函数非常严格地按出现的顺序在 vtable 中排序,所以确定的虚拟函数对应 vtable 中一个固定的位置n,n是一个在编译时就确定的常量。所以,使用vptr加上对应的n,就可得到对应函数的入口地址。


四、编译器调用虚拟函数的汇编码(参考think in c++):


push funparam ;先将函数参数压栈
  push si ;将this指针压栈,以确保在当前类上操作
  mov bx,word ptr[si] ;因为vc++编译器将vptr放在类的第一个位置上,所以bx内为vptr
  call word ptr[bx+n] ;调用虚拟函数。n = 所调用的虚拟函数在对应 vtable 中的位置
  


纯虚函数:


一、引入原因:


1、为了方便使用多态特性,我们常常需要在基类中定义虚拟函数。
2、在很多情况下,基类本身生成对象是不合情理的。例如,动物作为一个基类可以派生出老虎、孔雀等子类,但动物本身生成对象明显不合常理。
  为了解决上述问题,引入了纯虚函数的概念,将函数定义为纯虚函数(方法:virtual returntype function()= 0;),则编译器要求在派生类中必须予以重载以实现多态性。同时含有纯虚拟函数的类称为抽象类,它不能生成对象。这样就很好地解决了上述两个问题。
  二、纯虚函数实质:

  1、类中含有纯虚函数则它的vtable表不完全,有一个空位,所以,不能生成对象(编译器绝对不允许有调用一个不存在函数的可能)。在它的派生类中,除非重载这个函数,否则,此派生类的vtable表亦不完整,亦不能生成对象,即它也成为一个纯虚基类。
  虚函数与构造、析构函数:
  1、构造函数本身不能是虚拟函数;并且虚机制在构造函数中不起作用(在构造函数中的虚拟函数只会调用它的本地版本)。
  想一想,在基类构造函数中使用虚机制,则可能会调用到子类,此时子类尚未生成,有何后果!?。


  2、析构函数本身常常要求是虚拟函数;但虚机制在析构函数中不起作用。
  若类中使用了虚拟函数,析构函数一定要是虚拟函数,比如使用虚拟机制调用delete,没有虚拟的析构函数,怎能保证delete的是你希望delete的对象。
  虚机制也不能在析构函数中生效,因为可能会引起调用已经被delete掉的类的虚拟函数的问题。
  对象切片:
  向上映射(子类被映射到父类)的时候,会发生子类的vtable 完全变成父类的vtable的情况。这就是对象切片。
  原因:向上映射的时候,接口会变窄,而编译器绝对不允许有调用一个不存在函数的可能,所以,子类中新派生的虚拟函数的入口在vtable中会被强行“切”掉,从而出现上述情况。
  虚拟函数使用的缺点
  优点讲了一大堆,现在谈一下缺点,虚函数最主要的缺点是执行效率较低,看一看虚拟函数引发的多态性的实现过程,你就能体会到其中的原因
11.流

  流是既产生信息又消费信息的逻辑设备,通过C++系统和物理设备关联。C++的I/O系统是通过流操作的。有两种类型的流:文本流,二进制流。

  12.标准输入输出库

  它是C++标准库的组成部分,为C++语言提供了输入输出的能力。

  13.内置数据类型

  由C++直接提供的类型,包括int、float、double、char 、bool、指针、数组和引用
最佳答案
(1)auto
这个这个关键字用于声明变量的生存期为自动,即将不在任何类、结构、枚举、联合和函数中定义的变量视为全局变量,而在函数中定义的变量视为局部变量。这个关键字不怎么多写,因为所有的变量默认就是auto的。

(2)register
这个关键字命令编译器尽可能的将变量存在CPU内部寄存器中而不是通过内存寻址访问以提高效率。

(3)static
常见的两种用途:
1>统计函数被调用的次数;
2>减少局部数组建立和赋值的开销.变量的建立和赋值是需要一定的处理器开销的,特别是数组等含有较多元素的存储类型。在一些含有较多的变量并且被经常调用的函数中,可以将一些数组声明为static类型,以减少建立或者初始化这些变量的开销.

详细说明:
1>、变量会被放在程序的全局存储区中,这样可以在下一次调用的时候还可以保持原来的赋值。这一点是它与堆栈变量和堆变量的区别。
2>、变量用static告知编译器,自己仅仅在变量的作用范围内可见。这一点是它与全局变量的区别。
3>当static用来修饰全局变量时,它就改变了全局变量的作用域,使其不能被别的程序extern,限制在了当前文件里,但是没有改变其存放位置,还是在全局静态储存区。

使用注意:
1>若全局变量仅在单个C文件中访问,则可以将这个变量修改为静态全局变量,以降低模块间的耦合度;
2>若全局变量仅由单个函数访问,则可以将这个变量改为该函数的静态局部变量,以降低模块间的耦合度;
3>设计和使用访问动态全局变量、静态全局变量、静态局部变量的函数时,需要考虑重入问题(只要输入数据相同就应产生相同的输出)


(4)const
被const修饰的东西都受到强制保护,可以预防意外的变动,能提高程序的健壮性。它可以修饰函数的参数、返回值,甚至函数的定义体。
作用:
1>修饰输入参数
a.对于非内部数据类型的输入参数,应该将“值传递”的方式改为“const引用传递”,目的是提高效率。例如将void Func(A a) 改为void Func(const A &a)。
b.对于内部数据类型的输入参数,不要将“值传递”的方式改为“const引用传递”。否则既达不到提高效率的目的,又降低了函数的可理解性。例如void Func(int x) 不应该改为void Func(const int &x)。
2>用const修饰函数的返回值
a.如果给以“指针传递”方式的函数返回值加const修饰,那么函数返回值(即指针)的内容不能被修改,该返回值只能被赋给加const修饰的同类型指针
如对于: const char * GetString(void);
如下语句将出现编译错误:
char *str = GetString();//cannot convert from 'const char *' to 'char *';
正确的用法是:
const char *str = GetString();
b.如果函数返回值采用“值传递方式”,由于函数会把返回值复制到外部临时的存储单元中,加const修饰没有任何价值。 如不要把函数int GetInt(void) 写成const int GetInt(void)。
3>const成员函数的声明中,const关键字只能放在函数声明的尾部,表示该类成员不修改对象.

说明:
const type m; //修饰m为不可改变
示例:
typedef char * pStr; //新的类型pStr;
char string[4] = "abc";
const char *p1 = string;
p1++; //正确,上边修饰的是*p1,p1可变
const pStr p2 = string;
p2++; //错误,上边修饰的是p2,p2不可变,*p2可变
同理,const修饰指针时用此原则判断就不会混淆了。

const int *value; //*value不可变,value可变
int* const value; //value不可变,*value可变
const (int *) value; //(int *)是一种type,value不可变,*value可变
//逻辑上这样理解,编译不能通过,需要tydef int* NewType;
const int* const value;//*value,value都不可变

吐舌头

(5)volatile
表明某个变量的值可能在外部被改变,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。它可以适用于基础类型如:int,char,long......也适用于C的结构和C++的类。当对结构或者类对象使用volatile修饰的时候,结构或者类的所有成员都会被视为volatile.
该关键字在多线程环境下经常使用,因为在编写多线程的程序时,同一个变量可能被多个线程修改,而程序通过该变量同步各个线程。
简单示例:
DWORD __stdcall threadFunc(LPVOID signal)
{
int* intSignal=reinterpret_cast(signal);
*intSignal=2;
while(*intSignal!=1)
sleep(1000);
return 0;
}
该线程启动时将intSignal 置为2,然后循环等待直到intSignal 为1 时退出。显然intSignal的值必须在外部被改变,否则该线程不会退出。但是实际运行的时候该线程却不会退出,即使在外部将它的值改为1,看一下对应的伪汇编代码就明白了:
mov ax,signal
label:
if(ax!=1)
goto label
对于C编译器来说,它并不知道这个值会被其他线程修改。自然就把它cache在寄存器里面。C 编译器是没有线程概念的,这时候就需要用到volatile。volatile 的本意是指:这个值可能会在当前线程外部被改变。也就是说,我们要在threadFunc中的intSignal前面加上volatile关键字,这时候,编译器知道该变量的值会在外部改变,因此每次访问该变量时会重新读取,所作的循环变为如下面伪码所示:
label:
mov ax,signal
if(ax!=1)
goto label

注意:一个参数既可以是const同时是volatile,是volatile因为它可能被意想不到地改变。它是const因为程序不应该试图去修改它。

(6)extern
extern 意为“外来的”···它的作用在于告诉编译器:有这个变量,它可能不存在当前的文件中,但它肯定要存在于工程中的某一个源文件中或者一个Dll的输出中

参考http://hi.baidu.com/lzyu/blog/item/d328875189f3a62742a75b81.html

另外:C语言中的关键字
auto :声明自动变量 一般不使用
double :声明双精度变量或函数
int: 声明整型变量或函数
struct:声明结构体变量或函数
break:跳出当前循环
else :条件语句否定分支(与 if 连用)
long :声明长整型变量或函数
switch :用于开关语句
case:开关语句分支
enum :声明枚举类型
register:声明积存器变量
typedef:用以给数据类型取别名(当然还有其他作用)
char :声明字符型变量或函数
extern:声明变量是在其他文件正声明(也可以看做是引用变量)
return :子程序返回语句(可以带参数,也看不带参数)
union:声明联合数据类型
const :声明只读变量
float:声明浮点型变量或函数
short :声明短整型变量或函数
unsigned:声明无符号类型变量或函数
continue:结束当前循环,开始下一轮循环
for:一种循环语句(可意会不可言传)
signed:生命有符号类型变量或函数
void :声明函数无返回值或无参数,声明无类型指针(基本上就这三个作用)
default:开关语句中的“其他”分支
goto:无条件跳转语句
sizeof:计算数据类型长度
volatile:说明变量在程序执行中可被隐含地改变
do :循环语句的循环体
while :循环语句的循环条件
static :声明静态变量
if:条件语句
bad_cast、bad_typeid不是c++关键字


C/C++ Keywords
asm 插入一个汇编指令.
auto 声明一个本地变量.
bool 声明一个布尔型变量.
break 结束一个循环.
case 一个switch语句的一部分.
catch 处理 thrown 产生的异常.
char 声明一个字符型变量.
class 声明一个类.
const 声明一个常量.
const_cast 从一个const变量中抛出.
continue 在循环中间断循环.
default 是一个case语句中的缺省项.
delete 释放内存.
do 构造循环体.
double 声明一个双精度浮点变量.
dynamic_cast 动态投射.
else 是一个if语句中的预备条件.
enum 创建列举类型.
explicit 仅用在构造器的正确匹配.
extern 告诉编译器在别的地方变量已经被定义过了.
false 属于布尔值.
float 声明一个浮点型变量.
for 构造循环.
friend 允许非函数成员使用私有数据.
goto 跳转到程序的其它地方.
if 从一次判断的结果处执行代码.
inline 定义一个函数为内联.
int 声明一个整型变量.
long 声明一个长整型变量.
mutable 忽略const变量.
namespace 用一个定义的范围划分命名空间.
new 允许动态存储一个新变量.
operator 创建重载函数.
private 在一个类中声明私有成员.
protected 在一个类中声明被保护成员.
public 在一个类中声明公共成员.
register 定义一个寄存器变量.
reinterpret_cast 改变一个变量的类型.
return 从一个函数中返回.
short 声明一个短整型变量.
signed 修改变量类型声明.
sizeof 返回一个变量或类型的长度.
static 给一个变量创建永久的存储空间.
static_cast 执行一个非多态性cast.
struct 创建一个新结构体.
switch 让一个变量在不同的判断下执行不同的代码.
template 创建一个给特殊函数.
this 指向当前对象.
throw 抛出一个异常.
true 布尔类型的一个值.
try 执行一个被throw 抛出的异常.
typedef 从现有的类型中创建一个新类型.
typeid 描述一个对象.
typename 声明一个类或未定义的类型.
union 一个结构体在当前位置分配给多个变量相同的内存.
unsigned 声明一个无符号整型变量.
using 用来输入一个namespace.
virtual 创建一个不被已构成类有限考虑的函数.
void 声明函数或数据是无关联数据类型.
volatile 警告编译器有关的变量可能被出乎意料的修改.
wchar_t 声明一个带有宽度的字符型变量.
while 用来构成循环.
protected是受保护的,这个和public,private都是说明类里成员的访问属性或是继承方式的.
try是异常处理,那块的,和throw(是抛出异常的)及case(处理抛出的异常)
const是常变量声明的,
asm//这个不知道.
template这个是模版,如函数模版或是类模板
virtual这个是虚函数或是虚基类的,
delete这个是相当于C的free函数的,但是,这个是关键字,是用来撤消动态分配的空间的,和new对应的new是动态分配空间的.
friend是友元的意思,例如友元函数,友元类等.
operator这个重载运算符的时候要用到,重载运算符的时候是用这个加上运算符名当成函数名的,例如Complex operator+(Complex &c1,Complex &c2);声明复数的加法重载(+)这个运算符,可以这样写声明.
this是类里面的this指针,表示当前用到的对象的那个位置.
余下的二个在上面说别的时候提到过了
最佳答案
wchar_t:16位字符(char 是8位,对于中文和其他一些语言不够用)
register:建议编译器将所指示的变量放入寄存器(大多数编译器在管理寄存器上都很好很强大,所以并不需要)
auto:每当函数开始执行时,函数内声明为auto的变量自动分配内存(所有局部变量的默认行为,所以不需要)
mutable:声明的数据域允许修改,即使是常量(此特性不推荐使用)
static_cast,dynamic_cast,const-_ast,reinterpret_cast:强制类型转换.其中const_cast允许取消变量的const保护,不推荐使用;reinterpret_cast不更改实际的表示就能改变类型,属于危险操作同样不推荐使用.
unin:类似struct,一组数据域的集合,不过是按层次组织的;是一种非常危险和不可靠的机制(为什么?我也不知道),不推荐使用.
goto:无条件转移到指定标号的语句.由于使程序难以理解所以不推荐使用.
asm:asm函数使用字符串作为实参,编译器将此文本插入生成的汇编语句中该语句出现的位置.asm的使用使程序不可移植,而且插入汇编对专业的程序员也是极难把握,所以不推荐使用.
volatile:变量的值可由未在函数中定义的动作改变.危险操作,不推荐使用
sizeof:计算数据类型长度
volatile:说明变量在程序执行中可被隐含地改变
do :循环语句的循环体
while :循环语句的循环条件
static :声明静态变量
C++中不允许使用宏定义的方法定义符号常量,只能使用关键字const来定义

这个条款最好称为:“尽量用编译器而不用预处理”,因为#define经常被认为好象不是语言本身的一部分。这是问题之一。再看下面的语句:
#define ASPECT_RATIO 1.653
编译器会永远也看不到ASPECT_RATIO这个符号名,因为在源码进入编译器之前,它会被预处理程序去掉,于是ASPECT_RATIO不会加入到符号列表中。如果涉及到这个常量的代码在编译时报错,就会很令人费解,因为报错信息指的是1.653,而不是ASPECT_RATIO。如果ASPECT_RATIO不是在你自己写的头文件中定义的,你就会奇怪1.653是从哪里来的,甚至会花时间跟踪下去。这个问题也会出现在符号调试器中,因为同样地,你所写的符号名不会出现在符号列表中。
解决这个问题的方案很简单:不用预处理宏,定义一个常量:

const double ASPECT_RATIO = 1.653;

这种方法很有效。但有两个特殊情况要注意。
首先,定义指针常量时会有点不同。因为常量定义一般是放在头文件中(许多源文件会包含它),除了指针所指的类型要定义成const外,重要的是指针也经常要定义成const。例如,要在头文件中定义一个基于char*的字符串常量,你要写两次const:

const char * const authorName = "Scott Meyers";

另外,定义某个类(class)的常量一般也很方便,只有一点点不同。要把常量限制在类中,首先要使它成为类的成员;为了保证常量最多只有一份拷贝,还要把它定义为静态成员:


class GamePlayer {
private:
static const int NUM_TURNS = 5; // constant eclaration
int scores[NUM_TURNS]; // use of constant
...
};

还有一点,正如你看到的,上面的语句是NUM_TURNS的声明,而不是定义,所以你还必须在类的实现代码文件中定义类的静态成员:

const int GamePlayer::NUM_TURNS; // mandatory definition;
// goes in class impl.file

你不必过于担心这种小事。如果你忘了定义,链接器会提醒你。

旧一点的编译器会不接受这种语法,因为它认为类的静态成员在声明时定义初始值是非法的;而且,类内只允许初始化整数类型(如:int, bool, char 等),还只能是常量。
在上面的语法不能使用的情况下,可以在定义时赋初值:


class EngineeringConstants { // this goes in the class
private: // header file
static const double FUDGE_FACTOR;
...
};
// this goes in the class implementation file
const double EngineeringConstants::FUDGE_FACTOR = 1.35;

大多数情况下你只要做这么多。唯一例外的是当你的类在编译时需要用到这个类的常量的情况,例如上面GamePlayer::scores数组的声明(编译过程中编译器一定要知道数组的大小)。所以,为了弥补那些(不正确地)禁止类内进行整型类常量初始化的编译器的不足,可以采用称之为“借用enum”的方法来解决。这种技术很好地利用了当需要int类型时可以使用枚举类型的原则,所以GamePlayer也可以象这样来定义:


class GamePlayer {
private:
enum { NUM_TURNS = 5 } // "the enum hack" — makes
// NUM_TURNS a symbolic name
// for 5
int scores[NUM_TURNS];// fine
};

除非你正在用老的编译器(即写于1995年之前),你不必借用enum。当然,知道有这种方法还是值得的,因为这种可以追溯到很久以前的时代的代码可是不常见的哟。

回到预处理的话题上来。另一个普遍的#define指令的用法是用它来实现那些看起来象函数而又不会导致函数调用的宏。典型的例子是计算两个对象的最大值:


#define max(a,b) ((a) > (b) ? (a) : (b))

这个语句有很多缺陷,光想想都让人头疼,甚至比在高峰时间到高速公路去开车还让人痛苦。
无论什么时候你写了象这样的宏,你必须记住在写宏体时对每个参数都要加上括号;否则,别人调用你的宏时如果用了表达式就会造成很大的麻烦。但是即使你象这样做了,还会有象下面这样奇怪的事发生:

int a = 5, b = 0;
max(++a, b);// a 的值增加了2次
max(++a, b+10); // a 的值只增加了1次

这种情况下,max内部发生些什么取决于它比较的是什么值!
幸运的是你不必再忍受这样愚笨的语句了。你可以用普通函数实现宏的效率,再加上可预计的行为和类型安全,这就是内联函数
inline int max(int a, int b) { return a > b ? a : b; }
不过这和上面的宏不大一样,因为这个版本的max只能处理int类型。但模板可以很轻巧地解决这个问题:


template<class T>
inline const T& max(const T& a, const T& b)
{ return a > b ? a : b; }

这个模板产生了一整套函数,每个函数拿两个可以转换成同种类型的对象进行比较然后返回较大的(常量)对象的引用。因为不知道T的类型,返回时传递引用可以提高效率
有了const和inline,你对预处理的需要减少了,但也不能完全没有它。抛弃#include的日子还很远,#ifdef/#ifndef在控制编译的过程中还扮演重要角色。预处理还不能退休,但你一定要计划给它经常放长假


原创粉丝点击