《C++高级编程》之--利用模板编写通用代码

来源:互联网 发布:数据泄密安全 编辑:程序博客网 时间:2024/05/21 21:51
第一部分、利用模板编写通用代码
   
    本文章基址:http://blog.csdn.net/cugxueyu/archive/2007/12/07/1922459.aspx
   
    1、C++支持通用程序设计(编写可重用的代码),工具:模板
 
    2、模板概述
①、函数是对值得参数化方式。
②、模板扩展了参数化的概念,使用模板可以对数据类型以及参数化。使用模板编写的代码可以独立于传递给代码的值,而且独立于这些值得数据类型。
③、C++标准模板库是用模板建立的,要充分利用C++标准模板库,就必须懂得模板的基础知识。
 
   3、类模板(class template)
①、类模板主要是用作存储对象的容器和数据结构,来表现类模板更通用的本质。
②、采用模板可以在不指定数据类型的情况下编写类,这样一来,客户代码可以指定自己需要的数据类型来实例化模板。
③、类模板定义说明
    Itemplate <typename T> (注:蓝色为C++关键字,以后的学习中,默认表示)。
       对数据类型的参数化表现。
    II、Grid(const Grid<T>& src)
    III、void setElementAt(int x, int y, const T& inElem);
 
④、模板类的方法定义:必须以template <typename T>开始。模板可以指定任何类型,如:指针类型,类,基本类型,甚至可以是另一个模板类型。
    Grid<vector<int> > ※必须在两个相邻的尖括号之间留一个空格,防止和IO流的>>冲突。
    在栈上动态的分配Grid模板也是可以的:Grid<int>* myGrid = new Grid<int>();
4、编译器如何处理模板
 ①、选择性实例化
   在为多种不同的数据类型实例化模板时,由于编译器会给每种数据类型都生成一个模板代码的副本,所以为多种不同的数据类型实例化模板可能导致代码膨胀,使用模板时,最后可能得到极其庞大的可执行文件。不过编译器只会针对特定类型实际调用的类方法生成代码,所以这个问题有所缓解。
 ②、类型方面的模板要求
   在编写独立于类型的代码时,必须对这些类型做一定的假设。如果要使用某种数据类型对模板实例化,而对于特定程序的模板来说,该数据类型并不支持模板中定义的全部操作,则代码不能编译成功。然而,即使要使用的数据类型不支持模板代码所需的全部操作,也可以采用选择性实例化来使用其中的一部分方法。
 
5、模板代码在文件之间的分布
 可以采用一下机制来实现模板文件的包含:
①、在头文件中定义模板
   模板方法定义放在一个单独的头文件中,并在类定义所在的头文件中用#include包含这个头文件,但是要确保包含方法定义的#include语句在类定义的后面,否则代码不能编译。
    例:
    //Grid.h
    Template <typename T>
    Class Grid
    {
           //Class definition omitted for brevity
    }
    #include “GridFunctionDef.h”
  
②、在原文件中定义模板(推荐)
  可以把方法的定义放到源文件中,但是仍然需要保证该方法定义对于使用模板的代码是可用的。为此,可以在模板类定义头文件中用#include包含方法实现源文件。
   C++标准实际定义了一种方法,可以把模板方法定义放在源文件中,而不需要通过#include包含在头文件中。这个关键字是[export].但是很多的编译器还不支持。
6、模板参数
   在编写类模板时,要用尖括号指定参数列表,如:
   template <typename T>,可比编写多个模板参数的类。
①、无类型模板参数template <typename T, int W, int H>
  模板允许无类型参数取“简单”类型的值:int、enum、指针,引用。
   在模板列表指定无类型参数的主要优点:代码在编译之前就知道参数的值了。
②、Grid<typename T, int W, int H>::函数名。。。
③、Grid<int, 10, height(非常量)> //does not compile…
    const int height ---编译OK。  
   无类型模板参数变成了实例化对象类型规范的一部分。
 
④、整数无类型参数的默认值
 template <typename T, int W = 10, int H = 10>
 但是,并不需要在方法定义的模板规范中为W、H指定默认值。
 例:template <typename T, int W, int H>
      void Grid<T, W, H>::setElement(int x, int y, const T& inElem) { }
 
7、方法模板(很多的编译器并没有实现。)
  C++允许对类的单个方法进行模板化。这个方法可以放在类模板中,也可以放在非类模板中。方法模板对于类模板中的赋值操作符和复制构造函数用处较大。
  不能模板化虚函数和析构函数
  可以在类Grid增加赋值构造函数和operator=的模板化版本,从而可以由一种类型转换到另一种类型。
    模板化构造函数签名:templae <typename E>
                        Grid(const Grid<E>& src);
    模板构造函数的定义:temlate <typename T>
                                       temlate <typename E>
            Grid<T>::Grid(const Grid<E>& src)
    可以看到,在声明成员模板的代码行(E)之前,必须有声明类模板的代码行(T)。
 
8、模板类的特殊化
   可以为特定类型提供其他的类模板实现,在特殊化模板类时,必须指定它是个模板,而且要指定你正在为这个特定类型编写模板
   template <>
   class Grid<char*> { }
   <char*>的特殊化方法实现,不同于原来的模板定义,在此每个方法或静态方法定义之前不用重复写上template<>
   例:Grid<char*>::Grid(int W, int H);
 
9、从模板类派生子类
   可以编写模板类的子类
  如果子类从模板本身继承,它必须也是模板。或者,可以编写从模板类特定实例化继承的子类。
   例:template <typename T>
       class GameBoard : public Grid<T> { class def };
   GameBoard模板实际上并不是从通用Grid模板派生的子类,更合适的说法应该是,GameBoard模板针对特定类型的各个实例化是派生自Grid模板针对该类型实例化的子类。
 
10、模板继承和模板特殊化
   要采用继承来扩展实现以及实现多态性,而使用特殊化为特定类型建立定制实现。
 ①、继承
    重用代码:子类包含了超类的所有数据成员和方法。
    重用名字:子类名必须不同于超类名。
    支持多态性:子类的对象可以代替超类的对象。
 ②、特殊化
    重用代码:在特殊化中必须重写所有的代码。
    重用名字:特殊化必须使用与原模板相同的名字。
    支持多态性:模板针对一种类型的每个实例化都是一个不同的类型。
 
11、函数模板
   除了编写类模板类方法模板之外,也可以为独立的函数编写模板
   例:template <typename T>
       int find_index(T& value, T* arr, int size) { code }
   调用该函数有两个方法:用尖括号明确指定类型。或者省略类型,让编译器自己根据实参来推导参数是什么类型。
   例:find_index<int>(x, arr, 6) 或 find_index(x,arr, 8);
   象类模板一样,函数模板也可以使用无类型参数
 
12、函数模板的特殊化
   就像特殊化类模板一样,也可以特殊化函数模板
   例:template<>
       int find_index<char*>(char*&value, char** arr, int size) { code }
 
13、函数模板的重载
   也可以使用非模板函数来重载模板函数。
   与模板化函数相比,编译器更喜欢非模板化函数,但是,如果明确指定了模板实例化,编译器会被迫使用模板。
 
14、类模板的友元函数模板
15、高级模板
   实际上有三类模板参数:类型模板参数,无类型模板参数和模板模板参数
。。。