C++ Primer 学习笔记_79_模板与泛型编程 --模板编译模型
模板与泛型编程
--模板编译模型
引言:
当编译器看到模板定义的时候,它不马上产生代码。仅仅有在用到模板时,假设调用了函数模板或定义了模板的对象的时候,编译器才产生特定类型的模板实例。
一般而言,当调用函数时[不是模板],编译器仅仅需看到函数的声明。相似的,定义类类型的对象时,类定义必须可用,但成员函数的定义不是必须存在的。因此,应该将类定义和函数声明放在头文件中,而普通函数和类成员函数的定义放在源文件中。
模板则不同:要进行实例化,编译器必须能够訪问定义模板的源码。当调用函数模板或类模板的成员函数的时候,编译器须要函数定义,须要那些通常放在源文件里的代码。
标准C++为编译模板代码定义了两种模型。在两种模型中,构造程序的方式非常大程度上是同样的:类定义和函数声明放在头文件里,而函数定义和成员定义放在源文件里。两种模型的不同在于,编译器如何使用来自源文件的定义。如本书所述,全部编译器都支持第一种模型,成为“包括”模型,仅仅有一些编译器支持另外一种模型,“分别编译”模型。
一、包括编译模型
在包括编译模型中,编译器必须看到用到的全部模板的定义。一般而言,能够通过在声明函数模板或类模板的头文件里加入一条#include指示使定义可用,该#include引入了包括相关定义的源文件:
- //in utilities.h
- #ifndef UTILITIES_H_INCLUDED
- #define UTILITIES_H_INCLUDED
- #include "utilities.cpp"
- template <class T>
- int compare(const T &,const T &);
- #endif // UTILITIES_H_INCLUDED
- //in utilities.cpp
- #include "utilities.h"
- template <class T>
- int compare(const T &val1,const T &val2)
- {
- if (val1 < val2)
- return -1;
- if (val2 < val1)
- return 1;
- return 0;
- }
这一策略使我们能够保持头文件和实现文件的分离,可是须要保证编译器在使用模板的代码时能看到两种文件。
某些使用包括模型的编译器,特别是较老的编译器,能够产生多个实例。假设两个或多个单独编译的源文件使用同一模板,这些编译器将为每一个文件里的模板产生一个实例。通常,这样的方法意味着给定模板将实例化超过一次。在链接的时候,或者在预链接阶段,编译器会选择一个实例化而丢弃其它的。在这样的情况下,假设有很多实例化同一模板的文件,编译时性能会显著减少。对很多应用程序而言,这样的编译时性能减少不大可能在现代计算机上成为问题,可是,在大系统环境中,编译时选择问题可能变得非常重要。
这样的编译器通常支持某些机制,避免同一模板的多个实例化中隐含的编译进开销。编译器优化编译时性能的方法各不同样。假设使用模板的程序的编译时间难于承担,请查阅编译器的用户指南,看看你的编译器能提供什么支持以避免多余的实例化。
二、分别编译模型
在分别编译模型中,编译器会为我们跟踪相关的模板定义。可是,我们必须让编译器知道要记住给定的模板定义,能够使用exportkeyword来做这件事。
exportkeyword能够指明给定的定义可能会须要在其它文件里产生实例化。在一个程序中,一个模板仅仅能定义为导出一次。编译器在须要产生这些实例化时计算出如何定位模板定义。exportkeyword不必在模板声明中出现。
一般我们在函数模板的定义中指明函数模板为导出的,这是通过在keywordtemplate之前包括exportkeyword而实现的:
- //in utilities.h
- export template <typename Type>
- int compare(const Type &val1,const Type &val2)
- /***/
这个函数模板的声明像通常一样应放在头文件里,声明不必指定export。
对类模板使用export更复杂一些。通常,类声明必须放在头文件中,头文件中的类定义体不应该使用keywordexport,假设在头文件里使用了export,则该头文件仅仅能被程序中的一个源文件使用。
相反,应该在类的实现文件里使用export:
- // in header file
- template <class Type> class Queue { ... };
- // in implementation file
- export template <class Type> class Queue;
- #include "Queue.h"
- //...
导出类的成员将自己主动声明为导出的。也能够将类模板的个别成员声明为导出的,在这样的情况下,keywordexport不在类模板本身指定,而是仅仅在被导出的特定成员定义上指定。导出成员函数的定义不必在使用成员时可见。随意非导出成员的定义必须像在包括模型中一样对待:定义应放在定义类模板的头文件里。
- //P544 习题16.27
- //in middle.h
- #ifndef MIDDLE_H_INCLUDED
- #define MIDDLE_H_INCLUDED
- #include <vector>
- #include <algorithm>
- using namespace std;
- template <typename Type>
- bool middle(const vector<Type> &,Type &);
- #include "middle.cpp"
- #endif // MIDDLE_H_INCLUDED
- //in middle.cpp
- #include "middle.h"
- template <typename Type>
- bool middle(const vector<Type> &vec,Type &val)
- {
- vector<Type> tmp(vec);
- sort(tmp.begin(),tmp.end());
- if (tmp.size() % 2 == 0)
- {
- return false;
- }
- typename vector<Type>::iterator index =
- tmp.begin() + tmp.size()/2;
- if (*index > *(index -1) && *(index) < *(index + 1))
- {
- val = *index;
- return true;
- }
- return false;
- }
- //in main.cpp
- #include <iostream>
- #include "middle.h"
- using namespace std;
- int main()
- {
- int ia[] = {1,2,3,4,5,6,7};
- int ai[] = {1,2,3,4,5,6};
- vector<int> ivec1(ia,ia + 7),ivec2(ai,ai + 6);
- int val;
- if (middle(ivec1,val))
- {
- cout << "Middle: " << val << endl;
- }
- else
- {
- cout << "No Middle!" << endl;
- }
- if (middle(ivec2,val))
- {
- cout << "Middle: " << val << endl;
- }
- else
- {
- cout << "No Middle!" << endl;
- }
- }
- /**注意:g++编译器支持包括模型
- *可是不能将模板的实现文件包括到project中,
- *否则会引起编译错误!
- */
C++ Primer 学习笔记_79_模板与泛型编程 --模板编译模型的更多相关文章
- C++ Primer 学习笔记_76_模板与泛型编程 --模板定义[续]
模板与泛型编程 --模板定义[续] 四.模板类型形參 类型形參由keywordclass或 typename后接说明符构成.在模板形參表中,这两个keyword具有同样的含义,都指出后面所接的名字表示 ...
- C++ Primer 学习笔记_84_模板与泛型编程 --模板特化
模板与泛型编程 --模板特化 引言: 我们并不总是能够写出对全部可能被实例化的类型都最合适的模板.某些情况下,通用模板定义对于某个类型可能是全然错误的,通用模板定义或许不能编译或者做错误的事情;另外一 ...
- C++ Primer 学习笔记_85_模板与泛型编程 --模板特化[续]
模板与泛型编程 --模板特化[续] 三.特化成员而不特化类 除了特化整个模板之外,还能够仅仅特化push和pop成员.我们将特化push成员以复制字符数组,而且特化pop成员以释放该副本使用的内存: ...
- C++ Primer 学习笔记_75_模板与泛型编程 --模板定义
模板与泛型编程 --模板定义 引言: 所谓泛型程序就是以独立于不论什么特定类型的方式编写代码.使用泛型程序时,我们须要提供详细程序实例所操作的类型或值. 模板是泛型编程的基础.使用模板时能够无须了解模 ...
- C++ Primer 学习笔记_76_模板和泛型编程 --模板定义[继续]
模板和泛型编程 --模板定义[续] 四.模板类型形參 类型形參由keywordclass或 typename后接说明符构成.在模板形參表中,这两个keyword具有同样的含义,都指出后面所接的名字表示 ...
- C++ Primer学习笔记(三) C++中函数是一种类型!!!
C++中函数是一种类型!C++中函数是一种类型!C++中函数是一种类型! 函数名就是变量!函数名就是变量!函数名就是变量! (---20160618最新消息,函数名不是变量名...囧) (---201 ...
- C++ Primer学习笔记(二)
题外话:一工作起来就没有大段的时间学习了,如何充分利用碎片时间是个好问题. 接 C++ Primer学习笔记(一) 27.与 vector 类型相比,数组的显著缺陷在于:数组的长度是固定的,无法 ...
- C++ Primer 学习笔记_77_模板与泛型编程 --实例化
模板与泛型编程 --实例化 引言: 模板是一个蓝图,它本身不是类或函数.编译器使用模板产生指定的类或函数的特定版本号.产生模板的特定类型实例的过程称为实例化. 模板在使用时将进行实例化,类模板在引用实 ...
- 【c++ Prime 学习笔记】第16章 模板与泛型编程
面向对象编程(OOP)和泛型编程(GP)都能处理在编写程序时类型未知的情况 OOP能处理运行时获取类型的情况 GP能处理编译期可获取类型的情况 标准库的容器.迭代器.算法都是泛型编程 编写泛型程序时独 ...
随机推荐
- linux命令:find
先上例子: find ./*_src -type f | xargs grep -ils "date" 在指定的那些文件夹下面,递归寻找包含“date” 字符串的普通文件. fin ...
- ASP.NET Core 中文文档
ASP.NET Core 中文文档 翻译计划 五月中旬 .NET Core RC2 如期发布,我们遂决定翻译 ASP.NET Core 文档.我们在 何镇汐先生. 悲梦先生. 张仁建先生和 雷欧纳德先 ...
- 基于visual Studio2013解决面试题之0501上台阶
题目
- Godiva_百度百科
Godiva_百度百科 北京 三里屯 北京市朝阳区三里屯路19号院10号楼一层S10-13单元及二层S10-22单元 100027 北京朝阳大悦城北京市朝阳区朝阳北路101号朝阳大悦城1号商业楼1F- ...
- hdu 4812 D Tree(树的点分治)
D Tree Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 102400/102400 K (Java/Others) Total ...
- MSSQL - 通用存储过程
通用插入存储过程: -- ============================================= -- Author: HF_Ultrastrong -- Create date: ...
- 基于Spring提供支持不同设备的页面
基于Spring来检测访问Web页面的设备是很简单的,在这个经验中我们讲到过.通常不同的设备访问我们是通过响应式设计来统一处理各种设备的尺寸的.但是如果希望针对不同的设备,显示不同的内容呢? Spri ...
- 基于visual Studio2013解决面试题之0208二叉搜索树后序遍历序列
题目
- 【Visual C++】绘图函数BitBlt的使用方法
BitBlt 该函数对指定的源设备环境区域中的像素进行位块(bit_block)转换,以传送到目标设备环境. 原型: BOOL BitBlt( HDC hdcDest, int nXDest, ...
- 编写在浏览器中不弹出警告的ActiveX控件
我们在编写ActiveX控件时,如果用在浏览器中,经常都会弹出现在运行的脚本不安全的提示, 如果给客户使用,将会带来极大不便.按照MSDN的介绍通常有两种一种是实现IObjectSafe接口,一种是通 ...