函数重载就是有相同的函数名但参数的个数或类型不同从而根据不同的参数个数和参数类型来调用相应的方法。

   我们发现函数重载只是解决了函数命名的问题,但函数体虽然相同我们还是要重复的写,为了解决这一问题c++中有了函数模板。

函数模板的定义:

模板提供了一种机制,通过它我们可以保留函数定义和函数调用的语义(在一个程序位置上封装了一段代码,确保在函数调用之前实参只被计算一次)而无需像宏方案那样绕过C++的强类型检查。

关键字template 总是放在模板的定义与声明的最前面。关键字后面是用逗号分隔的模板参数表,它用尖括号<> 括起来。该列表是模板参数表,不能为空。模板参数可以是一个模板类型参数( template type parameter),它代表了一种类型,也可以是一个模板非类型参数( template nontype parameter),它代表了一个常量表达式。

模板类型参数由关键字class 或typename 后加一个标识符构成。在函数的模板参数表中,这两个关键字的意义相同。它们表示后面的参数名代表一个潜在的内置或用户定义的类型。模板参数名由程序员选择,在本例中,我们用Type 来命名min()的模板参数。但实际上可以是任何名字。譬如:

template <class Glorp> 
        Glorp min( Glorp a, Glorp b )

{
               return a < b ? a : b;
         }

当调用模板函数时首先是使用用户定义的类型来替换模板的类型,其次再是传值。

当模板被实例化时,实际的内置或用户定义类型将替换模板的类型参数。

非类型参数由一个普通的参数声明构成。模板非类型参数表示该参数名代表了一个潜在的值,而该值代表了模板定义中的一个常量。例如,size 是一个模板非类型参数,它代表arr 指向的数组的长度。

template <class Type, int size> 
        Type min( Type (&arr) [size] );

当函数模板min()被实例化时,size 的值会被一个编译时刻已知的常量值代替。

函定义或声明跟在模板参数表后,除了模板参数是类型指示符或常量值外,函数模板的定义看起来与非模板函数的定义相同。

在程序的运行过程中,Type 会被各种内置类型和用户定义的类型所代替。而size 会被各种常量值所取代,这些常量值是由实际使用的min()决定的(记住,一个函数的两种用法是调用它和取它的地址 )。类型和值的替换过程被称为模板实例化(template instantiation)。

当一个名字被声明为模板参数之后,它就可以被使用了,一直到模板声明或定义结束为止。模板类型参数被用作一个类型指示符,可以出现在模板定义的余下部分,它的使用方式与内置或用户定义的类型完全一样,比如用来声明变量和强制类型转换。模扳非类型参数被用作一个常量值,可以出现在模板定义的余下部分,它可以用在要求常量的地方,或许是在数组声明中指定数组的大小或作为枚举常量的初始值。

// size 指定数组参数的大小并初始化一个 const int 值
        template <class Type, int size>
        Type min( const Type (&r_array)[size] )
        {
              const int loc_size = size;
              Type loc_array[loc_size];
              // ...
        }


函数模板的异常处理

函数模板中的模板形参可实例化为各种类型,但当实例化模板形参的各模板实参之间不完全一致时,就可能发生错误,如:

template<typename T>

void min(T &x, T &y)

{  return (x<y)?x:y;  }

void func(int i, char j)

{

min(i, i);

min(j, j);

min(i, j);

min(j, i);

}

例子中的后两个调用是错误的,出现错误的原因是,在调用时,编译器按最先遇到的实参的类型隐含地生成一个模板函数,并用它对所有模板函数进行一致性检查,例如对语句

min(i, j);

先遇到的实参i是整型的,编译器就将模板形参解释为整型,此后出现的模板实参j不能解释为整型而产生错误,此时没有隐含的类型转换功能。解决此种异常的方法有两种:

⑴采用强制类型转换,如将语句min(i, j);改写为min(i,int( j));

⑵用非模板函数重载函数模板

方法有两种:

① 借用函数模板的函数体

此时只声明非模板函数的原型,它的函数体借用函数模板的函数体。如改写上面的例子如下:

template<typename T>

void min(T &x, T &y)

{  return (x<y)?x:y;  }

int min(int,int);

void func(int i, char j)

{

min(i, i);

min(j, j);

min(i, j);

min(j, i);

}

执行该程序就不会出错了,因为重载函数支持数据间的隐式类型转换。

② 重新定义函数体

就像一般的重载函数一样,重新定义一个完整的非模板函数,它所带的参数可以随意。C++中,函数模板与同名的非模板函数重载时,应遵循下列调用原则:

  1. 寻找一个参数完全匹配的函数,若找到就调用它。若参数完全匹配的函数多于一个,则这个调用是一个错误的调用。
  2. 寻找一个函数模板,若找到就将其实例化生成一个匹配的模板函数并调用它。
  3. 若上面两条都失败,则使用函数重载的方法,通过类型转换产生参数匹配,若找到就调用它。
  4. 若上面三条都失败,还没有找都匹配的函数,则这个调用是一个错误的调用。  

几个需要注意的地方:

  • 如果在全局域中声明了与模板参数同名的对象、函数、或类型,则该全局名将被隐藏。在下面的例子中tmp的类型不是double ,而是模板参数Type:

typedef double Type;

template <class Type>

Type min( Type a, Type b )

{

// tmp类型为模板参数Type,不是全局typedef

Type tmp = a < b ? a : b;

return tmp;

}

  • 在函数模板定义中声明的对象或类型不能与模板参数同名。

template <class Type>

Type min( Type a, Type b )

{

//错误:重新声明模板参数Type

typedef double Type;

Type tmp = a < b ? a : b;

return tmp;

}

  • 模板类型参数名可以被用来指定函数模板的返回值。

// ok: T1表示min() 的返回类型,T2和T3 表示参数类型

template <class T1, class T2, class T3>

T1 min( T2, T3 );

  • 模板参数名在同一模板参数表中只能被使用一次,但是模板参数名可以在多个函数模板声明或定义之间被重复使用。

//错误:模板参数名Type 的非法重复使用

template <class Type, class Type>

Type min( Type, Type );

// ok:名字Type在不同模板之间重复使用

template <class Type>

Type min( Type, Type );

template <class Type>

Type max( Type, Type );

  • 如果一个函数模板有一个以上的模板类型参数,则每个模板类型参数前面都必须有关键字class或typename。

// ok:关键字typename和class可以混用

template <typename T, class U>

T minus( T*, U );

//错误:必须是<typename T, class U>或<typename T, typename U>

template <typename T, U>

T sum( T*, U );

  • 为了分析模板定义,编译器必须能够区分出是类型以及不是类型的表达式。对于编译器来说,它并不总是能够区分出模板定义中的哪些表达式是类型。例如,如果编译器在模板定义中遇到表达式Parm::name ,且Parm这个模板类型参数代表了一个类。那么name引用的是Parm的一个类型成员吗.

template <class Parm, class U>

Parm minus( Parm* array, U value )

{

Parm::name * p;//这是一个指针声明还是乘法?

}

编译器不知道name是否为一个类型,因为它只有在模板被实例化之后才能找到Parm表示的类的定义。为了让编译器能够分析模板定义,用户必须指示编译器哪些表达式是类型表达式,告诉编译器一个表达式是类型表达式的机制是在表达式前加上关键字typename。例如,如果我们想让函数模板minus()的表达式Parm::name是个类型名,因而使整个表达式是一个指针声明。我们应如下修改:

template <class Parm, class U>

Parm minus( Parm* array, U value )

{

typename Parm::name * p;// ok:指针声明

}

关键字typename也可以被用在模板参数表中,以指示一个模板参数是一个类型。

  • 如同非模板函数一样,函数模板也可以被声明为inline或extern。应该把指示符放在模板参数表后面,而不是在关键字template前面。

// ok: 关键字跟在模板参数表之后

template <typename Type>

inline Type min( Type, Type );

内联函数:

 (1)什么是内联函数?
内联函数是指那些定义在类体内的成员函数,即该函数的函数体放在类体内。

(2)为什么要引入内联函数?
当然,引入内联函数的主要目的是:解决程序中函数调用的效率问题。另外,前面我们讲到了宏,里面有这么一个例子:
#define ABS(x) ((x)>0? (x):-(x))
当++i出现时,宏就会歪曲我们的意思,换句话说就是:宏的定义很容易产生二意性。
  
我们可以看到宏有一些难以避免的问题,怎么解决呢?前面我们已经尽力替换了。

下面我们用内联函数来解决这些问题。

(3)为什么inline能取代宏?
1、 inline 定义的类的内联函数,函数的代码被放入符号表中,在使用时直接进行替换,(像宏一样展开),没有了调用的开销,效率也很高。
2、 很明显,类的内联函数也是一个真正的函数,编译器在调用一个内联函数时,会首先检查它的参数的类型,保证调用正确。然后进行一系列的相关检查,就像对待任何一个真正的函数一样。这样就消除了它的隐患和局限性。
3、 inline 可以作为某个类的成员函数,当然就可以在其中使用所在类的保护成员及私有成员。

(4)内联函数和宏的区别?
内联函数和宏的区别在于,宏是由预处理器对宏进行替代,而内联函数是通过编译器控制来实现的。而且内联函数是真正的函数,只是在需要用到的时候,内联函数像宏一样的展开,所以取消了函数的参数压栈,减少了调用的开销。你可以象调用函数一样来调用内联函数,而不必担心会产生于处理宏的一些问题。内联函数与带参数的宏定义进行下比较,它们的代码效率是一样,但是内联欢函数要优于宏定义,因为内联函数遵循的类型和作用域规则,它与一般函数更相近,在一些编译器中,一旦关上内联扩展,将与一般函数一样进行调用,比较方便。

(5)什么时候用内联函数?
内联函数在C++类中,应用最广的,应该是用来定义存取函数。我们定义的类中一般会把数据成员定义成私有的或者保护的,这样,外界就不能直接读写我们类成员的数据了。对于私有或者保护成员的读写就必须使用成员接口函数来进行。如果我们把这些读写成
员函数定义成内联函数的话,将会获得比较好的效率。
Class A
{
Private:
int nTest;
 Public:
int readtest() { return nTest;}
void settest(int I) { nTest=I; }
}

(6)如何使用内联函数?
我们可以用inline来定义内联函数。
inline int A (int x) { return 2*x; }
不过,任何在类的说明部分定义的函数都会被自动的认为是内联函数。

(7)如何禁止函数进行内联?
如果使用VC++,可以使用/Ob命令行参数。当然,也可以在程序中使用 #pragma auto_inline达到相同的目的。

内联函数的优缺点:

   我们可以把它作为一般的函数一样调用,但是由于内联函数在需要的时候,会像宏一样展开,所以执行速度确比一般函数的执行速度要快。当然,内联函数也有一定的局限性。就是函数中的执行代码不能太多了,如果,内联函数的函数体过大,一般的编译器会放弃内联方式,而采用普通的方式调用函数。(换句话说就是,你使用内联函数,只不过是向编译器提出一个申请,编译器可以拒绝你的申请)这样,内联函数就和普通函数执行效率一样了。


注意事项:
1.在内联函数内不允许用循环语句和开关语句。
2.内联函数的定义必须出现在内联函数第一次被调用之前。










函数模板、 内联函数相关推荐

  1. 拷贝构造,深度拷贝,关于delete和default相关的操作,explicit,类赋初值,构造函数和析构函数,成员函数和内联函数,关于内存存储,默认参数,静态函数和普通函数,const函数,友元

     1.拷贝构造 //拷贝构造的规则,有两种方式实现初始化. //1.一个是通过在后面:a(x),b(y)的方式实现初始化. //2.第二种初始化的方式是直接在构造方法里面实现初始化. 案例如下: ...

  2. mysql 内联函数_内联函数 - freeboy小亮 - 博客园

    (1)什么是内联函数? 内联函数是指那些定义在类体内的成员函数,即该函数的函数体放在类体内. (2)为什么要引入内联函数? 当然,引入内联函数的主要目的是:解决程序中函数调用的效率问题.另外,前面我们 ...

  3. 宏定义,宏函数和内联函数

    宏定义,宏函数和内联函数 宏是什么: 简单宏替换出现的问题: 带参数的宏定义(宏函数): 宏的优点: 宏的缺陷,内联函数的引入 内联函数(空间换时间) 宏是什么: 宏(#define)命令是C语言中的 ...

  4. C++宏函数和内联函数

    C++宏函数和内联函数 1. 宏常量&宏函数 1.1 定义 // a. 定义一个宏常量 #define MAX 1024 // 宏常量 MAX称为符号常量// b. 定义一个宏函数 // 宏函 ...

  5. 【C/C++】宏函数与内联函数的区别

    [C/C++]宏函数与内联函数的区别 文章目录 [C/C++]宏函数与内联函数的区别 一.概念 二.特性 三.优缺点 3.1 内联函数的优缺点? 3.2 宏的优缺点? 四.笔试题 4.1 为什么要是用 ...

  6. java内联函数_Java之内联函数_内联函数的优缺点

    描述 内联函数 1.内联函数就是指函数在被调用的地方直接展开,编译器在调用时不用像一般函数那样,参数压栈,返回时参数出栈以及资源释放等,这样提高了程序执行速度. 2.Java语言中有一个关键字fina ...

  7. 对普通函数、宏函数、内联函数的作用机制的探索

    这次我们来分析的是C/C++程序员经常遇到的问题,如何在普通函数.宏函数.内联函数之间做取舍,其实它们三者之间并没有什么绝对的你好我差的说法,只要掌握了三者的作用机制的话,结合实际情况一般都能做出正确 ...

  8. c++中内敛函数_C++ 内联函数 | 菜鸟教程

    内联函数: Tip: 只有当函数只有 10 行甚至更少时才将其定义为内联函数. 定义: 当函数被声明为内联函数之后, 编译器会将其内联展开, 而不是按通常的函数调用机制进行调用. 优点: 当函数体比较 ...

  9. C++类里面的哪些成员函数是内联函数?

    类定义的内联函数分为以下3种: 1.隐式内联 class Person { public:Person(const string &name){Name = name;}void printN ...

  10. C++:构造函数重载类内定义函数(内联函数)

    构造函数的重载 构造函数是可以重载的,即写多个构造函数,它们具有不同的参数表和相同的名称,如果没有参数信息,编译器就认为调用默认构造函数. 特点 重载构造函数具有不同的参数表和相同的名称 根据传参个数 ...

最新文章

  1. 牛客网练习赛44-B(快速幂+模拟)
  2. 再也不用担心过拟合的问题了
  3. 计算机网络谢希仁第七版课后答案第三章 数据链路层
  4. 文本分类模型_文本分类模型之TextCNN
  5. c语言实战1200例 pdf6,C语言程序设计6.6.2.pdf
  6. Linq之隐式类型、自动属性、初始化器、匿名类
  7. 【TensorFlow】:解决TensorFlow的ImportError: DLL load failed: 动态链接库(DLL)初始化例程失败...
  8. Python 基础知识学习笔记——OpenCV(1)
  9. python 功能代码是什么_Python功能代码
  10. 飘逸的python - 实现控制台进度条效果
  11. Xshell 6的 InstallShield: 1628 完成基于脚本的安装失败
  12. js设置弹出式独立窗口页面,和 window 的方法
  13. 简记_BISS通信协议简介
  14. 网络购书挑战书业传统营销模式
  15. java 架构师课程体系
  16. 【厚积薄发系列】读书笔记2—《洞察力的秘密》小记
  17. 实训一 思科交换机基础配置
  18. 无盘服务器要开ahci,开启硬盘的ahci模式提升磁盘性能教程
  19. 【转载】中国特色的免费游戏:下流下贱下作!
  20. CentOS 安装相关

热门文章

  1. 爬取的是最好大学网软科中国最好大学排名2019
  2. 基于微信小程序视频点播系统、电影播放系统、在线教育视频系统 系统的设计与实现 开题报告和效果图
  3. vue - 判断终端以及浏览器
  4. Android 短信验证 SDK 接入(Mob SMSSDK)
  5. 配电自动化“三遥”具体指什么?
  6. local variable 'sum_size' referenced before assignment报错
  7. Linux文件类型之 管道
  8. Mysql:Mybatis在xml文件中处理大于号小于号的方法
  9. 生命游戏(Life game)
  10. 一个友好的扫雷程序————C初学者都能会的简单扫雷(一)