C++模板代码的组织方式:包含模式(Inclusion Model)
转自:http://blog.csdn.net/sam1111/
说明
:本文译自《
C++ Template: The Complete Guide
》一书的第
6
章中的部分内容。最近看到
C++
论坛上常有关于模板的包含模式的帖子,联想到自己初学模板时,也为类似的问题困惑过,因此翻译此文,希望对初学者有所帮助。
模板代码有几种不同的组织方式,本文介绍其中最流行的一种方式:包含模式。
链接错误
大多数
C/C++
程序员向下面这样组织他们的非模板代码:
·
类和其他类型全部放在头文件中,这些头文件具有
.hpp
(或者
.H, .h, .hh, .hxx
)扩展名。
·对于全局变量和(非内联)函数,只有声明放在头文件中,而定义放在点C文件中,这些文件具有.cpp(或者.C, .c, .cc, .cxx)扩展名。
这种组织方式工作的很好:它使得在编程时可以方便地访问所需的类型定义,并且避免了来自链接器的“变量或函数重复定义”的错误。
由于以上组织方式约定的影响,模板编程新手往往会犯一个同样的错误。下面这一小段程序反映了这种错误。就像对待“普通代码”那样,我们在头文件中定义模板:
// basics/myfirst.hpp
#ifndef MYFIRST_HPP
#define MYFIRST_HPP
// declaration of template
template <typename T>
void print_typeof (T const&);
#endif // MYFIRST_HPP
print_typeof()
声明了一个简单的辅助函数用来打印一些类型信息。函数的定义放在点
C
文件中:
// basics/myfirst.cpp
#include <iostream>
#include <typeinfo>
#include "myfirst.hpp"
// implementation/definition of template
template <typename T>
void print_typeof (T const& x)
{
std::cout << typeid(x).name() << std::endl;
}
这个例子使用
typeid
操作符来打印一个字符串,这个字符串描述了传入的参数的类型信息。
最后,我们在另外一个点
C
文件中使用我们的模板,在这个文件中模板声明被
#include
:
// basics/myfirstmain.cpp
#include "myfirst.hpp"
// use of the template
int main()
{
double ice = 3.0;
print_typeof(ice);
// call function template for type double
}
大部分
C++
编译器(
Compiler
)很可能会接受这个程序,没有任何问题,但是链接器(
Linker
)大概会报告一个错误,指出缺少函数
print_typeof()
的定义。
这个错误的原因在于,模板函数
print_typeof()
的定义还没有被具现化(
instantiate
)。为了具现化一个模板,编译器必须知道哪一个定义应该被具现化,以及使用什么样的模板参数来具现化。不幸的是,在前面的例子中,这两组信息存在于分开编译的不同文件中。因此,当我们的编译器看到对
print_typeof()
的调用,但是没有看到此函数为
double
类型具现化的定义时,它只是假设这样的定义在别处提供,并且创建一个那个定义的引用(链接器使用此引用解析)。另一方面,当编译器处理
myfirst.cpp
时,该文件并没有任何指示表明它必须为它所包含的特殊参数具现化模板定义。
头文件中的模板
解决上面这个问题的通用解法是,采用与我们使用宏或者内联函数相同的方法:我们将模板的定义包含进声明模板的头文件中。对于我们的例子,我们可以通过将
#include "myfirst.cpp"
添加到
myfirst.hpp
文件尾部,或者在每一个使用我们的模板的点
C
文件中包含
myfirst.cpp
文件,来达到目的。当然,还有第三种方法,就是删掉
myfirst.cpp
文件,并重写
myfirst.hpp
文件,使它包含所有的模板声明与定义:
// basics/myfirst2.hpp
#ifndef MYFIRST_HPP
#define MYFIRST_HPP
#include <iostream>
#include <typeinfo>
// declaration of template
template <typename T>
void print_typeof (T const&);
// implementation/definition of template
template <typename T>
void print_typeof (T const& x)
{
std::cout << typeid(x).name() << std::endl;
}
#endif // MYFIRST_HPP
这种组织模板代码的方式就称作包含模式。经过这样的调整,你会发现我们的程序已经能够正确编译、链接、执行了。
从这个方法中我们可以得到一些观察结果。最值得注意的一点是,这个方法在相当程度上增加了包含
myfirst.hpp
的开销。在这个例子中,这种开销并不是由模板定义自身的尺寸引起的,而是由这样一个事实引起的,即我们必须包含我们的模板用到的头文件,在这个例子中是
<iostream>
和
<typeinfo>
。你会发现这最终导致了成千上万行的代码,因为诸如
<iostream>
这样的头文件也包含了和我们类似的模板定义。
这在实践中确实是一个问题,因为它增加了编译器在编译一个实际程序时所需的时间。我们因此会在以后的章节中验证其他一些可能的方法来解决这个问题。但无论如何,现实世界中的程序花一小时来编译链接已经是快的了(我们曾经遇到过花费数天时间来从源码编译的程序)。
抛开编译时间不谈,我们强烈建议如果可能尽量按照包含模式组织模板代码。
另
一个观察结果是,非内联模板函数与内联函数和宏的最重要的不同在于:它并不会在调用端展开。相反,当模板函数被具现化时,会产生此函数的一个新的拷贝。由
于这是一个自动的过程,编译器也许会在不同的文件中产生两个相同的拷贝,从而引起链接器报告一个错误。理论上,我们并不关心这一点:这是编译器设计者应当
关心的事情。实际上,大多数时候一切都运转正常,我们根本就不用处理这种状况。然而,对于那些需要创建自己的库的大型项目,这个问题偶尔会显现出来。
最后,需要指出的是,在我们的例子中,应用于普通模板函数的方法同样适用于模板类的成员函数和静态数据成员,以及模板成员函数。
分享到:
相关推荐
C++模板元编程是一种在编译时执行计算和构建类型的技术,它利用了C++模板的强大功能,将元数据(即关于数据的数据)处理提升到了一个新的层次。在C++中,模板不仅仅是用来创建泛型代码的工具,它们也可以被用作一种...
《数据结构:基于C++模板类的实现》是一本...总的来说,《数据结构:基于C++模板类的实现》是一本帮助开发者掌握C++中数据结构实现的宝贵资源,通过实际代码学习,可以提升编程技能,并为解决实际问题提供强有力的支持。
9. **命名空间**:命名空间用于避免命名冲突,使代码更易于管理和组织。 10. **输入/输出流**:C++的iostream库提供了流的概念,用于读写标准输入(cin)和输出(cout),以及文件操作。 通过《Essential C++》源...
C++模板是C++编程语言中的一个强大特性,它允许程序员编写泛型代码,即可以处理多种数据类型的代码。通过使用模板,开发者能够减少代码量、提高代码复用性,并且增强程序的灵活性和可维护性。 ### 模板的基本分类 ...
8. **40_詳細設計書**:这个文件可能是关于如何在项目中有效使用C++模板的详细设计文档,包括如何设计和组织模板代码,以及如何与其他系统组件集成。 综合以上内容,这个资源包提供了一种深入理解C++模板的机会,...
使用C++模板,可以实现代码复用和泛型编程,这对于解决复杂问题尤其有帮助。ACM-ICPC模板则是在此基础上,结合比赛特点,进一步优化和封装的代码库。 总的来说,这个基于C++的ACM-ICPC模板是参赛者在备赛过程中必不...
在C++编程中,模板是一种强大的工具,它...总结来说,C++模板是实现泛型编程的关键,它允许我们编写高度可重用和灵活的代码,同时减少了冗余的代码量。通过理解和熟练运用模板,可以极大地提升C++程序的效率和质量。
通过源代码,读者可以学习如何组织和使用命名空间。 8. **内存管理**:C++提供了动态内存分配(new和delete)和智能指针(如unique_ptr、shared_ptr)等功能,源代码将展示如何正确管理和释放内存,避免内存泄漏。 ...
总之,理解和熟练掌握C++模板是提高编程效率和代码质量的关键。遇到模板编译问题时,不要急于求成,而是要深入理解问题背后的原因,并采取相应的解决策略。通过不断实践和学习,你将能够更好地驾驭这个强大的工具。
总的来说,这个“Dev-C++模板”压缩包提供了一个方便的起点,使得C语言的新手可以在VS或Dev-C++环境下快速开始编程实践,通过观察和修改模板代码,学习C语言的基本语法和编程思路。它不仅减少了设置新项目的时间,还...
2. **函数**:函数是C++中的模块化工具,允许我们将代码组织成可重用的部分。了解如何定义、调用和传递参数给函数对于编写大型程序至关重要。 3. **指针**:C++的指针是其强大的特性之一,它允许直接操作内存地址。...
通用C++模板通常是指一个基本的项目结构,包含必要的文件和配置,以便开发者可以专注于编写实际的代码,而不是设置构建环境。这样的模板可能包括以下元素: 1. **Makefile**:这是一个脚本文件,用于自动化编译和...
7. **实践编程技巧**:除了理论知识,源代码还展示了良好的编程实践,如代码注释、错误检查、代码组织和调试技巧,这些都是成为一个优秀程序员不可或缺的技能。 通过深入研究《Accelerated C++》的源代码,读者不仅...
多态是对象可以根据其实际类型以不同的方式响应同一消息的能力,C++中通过虚函数和纯虚函数实现。 4. **模板**:模板是C++中的泛型编程工具,可以创建通用的函数和类,以处理不同类型的数据。这使得代码更加灵活且...
5. **模板**:C++模板允许创建泛型代码,可以用于处理不同类型的数据。函数模板和类模板是两种常见的模板形式,它们提高了代码的灵活性和重用性。 6. **STL(标准模板库)**:STL是C++的一个重要组成部分,包括容器...
C++模板允许创建泛型代码,可用于处理不同数据类型。了解函数模板和类模板的用法,可以编写更高效、更具复用性的代码。 5. **异常处理** 异常处理是C++中处理运行时错误的方式,通过try、catch和throw关键字来...
通过源代码,我们可以了解如何组织和编写有效的C++语句。 2. **面向对象编程**:C++支持面向对象编程(OOP),包括类、对象、封装、继承和多态。源代码中可能包含示例类,展示了如何定义属性和方法,以及如何通过...
4. **模板**:C++模板可以创建泛型代码,使得函数和类能够处理不同类型的参数。这提高了代码的复用性,降低了维护成本。 5. **异常处理**:通过try-catch块,C++提供了异常处理机制,用于捕获和处理程序运行时可能...
总的来说,这个客户资源管理系统C++源代码涵盖了数据管理、面向对象编程、事件处理、异常处理、代码组织等多个核心知识点,是学习C++在实际项目中应用的宝贵实例。深入研究这个系统,不仅可以提升C++编程技能,还能...
在学习过程中,你可能会接触到STL(Standard Template Library,标准模板库),这是C++库的一部分,提供了包括容器(如vector、list、set)、迭代器、算法和函数对象等丰富的工具,极大地提高了代码的效率和可读性。...