如何解决类模板的分离编译问题?

一模板:

模板不是数据类型,只能算是一种行为集合的表示。编译器在使用模板时,通过更换模板参数来创建数据类型。这个过程就是模板实例化(Instantiation), 从模板类创建得到的类型称之为特例(specialization),说白了就是创建了一个新类型。 模板实例化取决于编译器能够找到可用代码来创建特例(称之为实例化要素,point of instantiation),也就是说,编译器不但要看到模板的声明,还要看到模板的定义。

1. 为什么类模版不能分离编译,这要从模板的实例化过程说起:

    1)以分离形式写出的模版类(以tem.h和tem.cpp为例,另外还有主函数main.cpp),在编译main.cpp时由于只能看到模板声明而看不到实现,因此不会创建新的类型,但此时不会报错,因为编译器认为模板定义在其它文件中,就把问题留给链接程序处理。

    2)编译器在编译tem.cpp时可以解析模板定义并检查语法,但不能生成成员函数的代码。因为要生成代码,需要知道模板参数,即需要一个类型,而不是模板本身。

    3)这样,链接程序在main.cpp 或 tem.cpp中都找不到新类型的定义,于是报出无定义成员的错误。

另外,实例化是惰性的,只有用到该函数时才会去对模版中的定义进行实例化。


举一个明显的例子:

.h文件

template<class K, class V>
class RBTree
{
typedef RBTreeNode<K, V> Node;
public:
RBTree()
:_pRoot(NULL)
{}
~RBTree()
{}

void InOrder()
{
_InOrder(_pRoot);
cout<<endl;
}
void _InOrder(Node* _pRoot);

}

.cpp文件

#include "RBTree.hpp"


template<class K, class V>
void RBTree<K, V>::_InOrder(Node* pRoot)
{
if(pRoot)
{
_InOrder(pRoot->_pLeft);
cout<<pRoot->_key<<" ";
_InOrder(pRoot->_pRight);
}
}


这样就会产生上述问题:



2. 如何解决这个问题?有三种方式:

    1)在实例化要素中让编译器看到模板定义。(即写到一个文件里,声明和实现放在一起)

    2)把类的声明放在 .hpp 文件,类成员函数定义放在 .cpp 文件然后在测试文件 test.cpp 文件中包含连个头文件即:.hpp文件和.cpp 文件即可解决问题。

    3)使用export关键字。

    前二种方法通常称为包含模式,第三种方法则称为分离模式。第一种方法意味着在使用模板的转换文件中不但要包含模板声明文件,还要包含模板定义文件,这样编译器就能看到模板的声明和定义。这样做的缺点是编译文件会变得很大,显然要降低编译和链接速度。第二种方法,通过显式的模板实例化得到类型,也就是将所有的显式实例化过程安放在另外的文件中(比如tem_extend.cpp)。这样新类型不是在main.cpp中产生,而是在tem_extend.cpp中产生,链接器就能够找到它的定义。用这种方法,不会产生巨大的头文件,加快编译速度,而且头文件本身也显得更加“干净”和更具有可读性。但这个方法不能得到惰性实例化的好处,即它将显式地生成所有的成员函数。第三种方法是在模板定义中使用export关键字,剩下的事就让编译器去自行处理了。



本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/459399.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

结对项目 刘静 201303014059 计科高职13-2

结对&#xff1a;人&#xff1a;孙帅 博客地址&#xff1a; http://www.cnblogs.com/s3366181/p/4509260.html一、 题目简介 1.所选题目&#xff1a;输出圆的面积 2.编程工具&#xff1a;Eclipse 3、实现功能&#xff1a;用户给定一圆的半径运行程序系统会给出给定半径圆的面…

弹出div或者弹出新窗口的固定位置、固定大小

2019独角兽企业重金招聘Python工程师标准>>> js代码&#xff1a; //打开一个新窗口&#xff0c;固定的位置&#xff0c;固定的大小 //window.open("push_add.html",newwindow, height550, width1000, top200, left500, toolbarno, menubarno, scro…

Unix_03_文件系统介绍_2

目录命令&#xff1a;1 显示目录路径名: pwd (print working directory)login:cnyxj [return]...................................输入登录名(cnyxj)password.................................................. 输入口令Welcome to UNIX!$pwd [return] ................…

NAT(网络地址转换)技术与代理服务器原理

一、 Nat技术&#xff1a; NAT英文全称是“Network Address Translation”&#xff0c;中文意思是“网络地址转换”&#xff0c;它是一个IETF(Internet Engineering Task Force,Internet工程任务组)标准&#xff0c;允许一个整体机构以一个公用IP&#xff08;Internet Prot…

apache mod_xsendfile提高php文件下载速度的方法

说明&#xff1a;在apache服务器中提供一个文件下载&#xff0c;一般使用一个url指向服务器中的文件即可提供下载。缺点&#xff1a;不能进行统计&#xff0c;权限检测等操作。 1&#xff0c;一般使用php提供下载&#xff0c;例如&#xff1a; 复制代码代码示例:<?php $f…

原 Linux搭建SVN 服务器2

原 Linux搭建SVN 服务器 发表于1年前(2014-08-05 17:55) 阅读&#xff08;12257&#xff09; | 评论&#xff08;3&#xff09; 31人收藏此文章, 我要收藏赞3摘要 Linux搭建SVN 服务器目录[-] Linux搭建SVN 服务器1 安装SVN2 使用客户端连接2.1 使用…

网络层核心:路由和路由生成算法

一、路由和路由算法简介&#xff1a; 路由就是通过互连的网络把信息从源地址传送到目的地址的活动。路由发生在OSI网络参考模型的第三层即网络层。 路由引导封包转送&#xff0c;经过一些中间的节点后&#xff0c;到达目的地。把该功能做成硬件的话称为路由器。路由通常根据路…

LeetCode--Sum Root to Leaf Numbers

题目&#xff1a; Given a binary tree containing digits from 0-9 only, each root-to-leaf path could represent a number. An example is the root-to-leaf path 1->2->3 which represents the number 123. Find the total sum of all root-to-leaf numbers. For ex…

在树莓派2上折腾kali2.0小记(1)

在树莓派2上折腾kali2.0小记&#xff08;1&#xff09;本文主要是对自己在树莓派2上折腾kali2.0过程的一个记录&#xff0c;主要面向初学者&#xff0c;欢迎各位大神赐教。很久之前就听说过树莓派大名&#xff0c;刚好今年发布2.0版本&#xff0c;而且刚好最近有空&#xff0c;…

网络:常见的端口号及分类

一、端口号概念 在网络技术中&#xff0c;端口&#xff08;Port&#xff09;包括逻辑端口和物理端口两种类型。物理端口指的是物理存在的端口&#xff0c;如ADSL Modem、集线器、交换机、路由器上用 于连接其他网络设备的接口&#xff0c; 如RJ-45端口、SC端口等等。逻辑端口…

EditPlus 技巧大全:[1]怎么配置PHP编译环境

editplus是一款小巧但功能强大易扩展的文本编辑器&#xff0c;可以通过设置用户工具将其作为C,Java,Php等等语言的一个简单的IDE。 工具/原料 EditPlus v3.3.1 php 5.3.14 方法/步骤 1.打开editplus 2.点击菜单栏“工具” 3.选择下拉菜单的“配置用户工具”&#xff0c;进入配置…

grub加密

grub加密有明文加密码和密文加密命令&#xff1a;grub-md5-crypt[rootlnmp01 rc.d]# grub-md5-crypt Password: Retype password: $1$p9Y9a$Id5hMpbsHLNZFs0gPbRRI.密码加密后密文&#xff1a;$1$p9Y9a$Id5hMpbsHLNZFs0gPbRRI.配置/boot/grub/grub.…

网络:传输层 TCP报文格式解析

一、TCP报文格式 1、为了提供可靠的数据传输&#xff0c;TCP报文首部字段有较多的字段&#xff0c;TCP报文格式如下图&#xff1a; 图2 TCP报文格式 16位源和目标端口&#xff08;16位&#xff09;&#xff1a;用于多路复用/多路分解来自或送至上层应用的数据&#xff0c;可以…

sigsuspend sigprocmask函数的用法

一个进程的信号屏蔽字规定了当前堵塞而不能递送给该进程的信号集。调用函数sigprocmask能够检測或更改其信号屏蔽字&#xff0c;或者在一个步骤中同一时候运行这两个操作。 #include <signal.h> int sigprocmask( int how, const sigset_t *restrict set, sigset_t *rest…

MATLAB图像小波变换

为什么80%的码农都做不了架构师&#xff1f;>>> 小波变换与小波包变换 人脸图像f(x,y) 的一层小波变换如下图所示&#xff1a; 图中L 和H 分别表示低通滤波器和高通滤波器&#xff0c;l(n) 和h(n) 分别表示它们相应的脉冲响应&#xff0c;2↓1表示降2采样fLL和fHH分…

网络:TCP维护安全可靠机制提供的定时器

一、TCP为维护安全可靠机制提供了七大定时器 1、连接建立(connectionestablishment)”定时器&#xff1a; 在发送SYN报文段建立一条新连接时启动。如果在75秒内没有收到响应&#xff0c;连接建立将中止。 2、重传(retransmission)定时器&#xff1a; 在TCP发送某个数据段时设定…

grunt之Gruntfile(1)

grunt 执行的时候&#xff0c;他会找该目录下的Gruntfile文件&#xff0c;所以&#xff0c;要在目录下创建Gruntfile文件。 下面我demo一个copy任务&#xff1a; 执行copy&#xff0c;首先我们要一个copy的模块&#xff0c;那么我们先安装下copy模块 首先&#xff0c;我到H盘&a…

MyEclipse从数据库反向生成实体类之Hibernate方式 反向工程

2019独角兽企业重金招聘Python工程师标准>>> 开发项目涉及到的表太多&#xff0c;一个一个的写JAVA实体类很是费事。MyEclipse提供简便的方法&#xff1a;反向数据库 步骤大致如下: 第一步&#xff1a; window-->open Perspective-->MyEclipse Java Persisten…

TCP的定时器系列 — 超时重传定时器(有图有代码有真相!!!)

转载 主要内容&#xff1a;TCP定时器概述&#xff0c;超时重传定时器、ER延迟定时器、PTO定时器的实现。 内核版本&#xff1a;3.15.2 我的博客&#xff1a;http://blog.csdn.net/zhangskd Q&#xff1a;一条TCP连接会使用多少个定时器呢&#xff1f; A&#xff1a;目前的答案…

Python try/except/finally等

Python try/except/finally等 [代码块]x abc def fetcher(obj, index): return obj[index] fetcher(x, 4) 输出&#xff1a; File "test.py", line 6, in <module> fetcher(x, 4) File "test.py", line 4, in fetcher retur…