day_12_模板和标准模板库

来源:互联网 发布:北信源软件 编辑:程序博客网 时间:2024/05/15 13:41

    • 模板和标准模板库
    • 一模板的起源
      • 1何为数据类型
      • 2何为静态类型
        • 1静态类型特点
    • 二函数模板
      • 1定义
      • 2使用
      • 3类型参数
      • 4延迟编译
      • 5隐式推断
      • 6重载
    • 三类模板
      • 1定义
      • 2使用

模板和标准模板库

一、模板的起源

1、何为数据类型?

数据的存储形式、计算规则、访问方式

/* 存储形式 */int a = 1;char* d = NULL;/* 计算规则 */++a;  // a = 2++d;  // d = 4(32位机器)/* 访问方式 */Student e("张飞", 20);cout << e.m_age << endl;

2、何为静态类型?

由语言编译器处理类型,生成与之相一致的机器指令,在运行阶段不可改变;

/* 动态类型 */a = 100 // a:inta = 3.14 // a:floata = "hello,world!" // a:string

1)静态类型特点

优点:

a. 安 全 性:更细致的数据类型检查;
b. 运行性能:运行时不需要考虑变量的类型和处理方式;(牺牲编译时间)

缺点:

不够灵活(缺乏泛型性):不适合于表达与数据类型无关的算法;

参见:typed.cpp

int sum(int a, int b){    return a+b;}cout << sum(100, 200) << endl;  // 300cout << sum("100", "200") << endl;  // Error!def sum(a, b):    return a+bprintf(sum(100,200))  // 300printf(sum("100", "200"))  // 100200

借助于参数宏(宏函数),可以在某种程度上摆脱源自静态类型的约束,但同时也带来由于缺乏类型安全机制的风险;

参见:untype.cpp

  利用宏定义以类型无关的方式描述泛型化的算法函数框架,通过预处理器将其扩展为针对具体类型的函数实现,既保证逻辑的类型无关性,同时兼顾源自静态类型的安全性;

参见:macro.cpp

二、函数模板

1、定义

template<typename 类型形参1, typename 类型形参2, ..>返回类型 函数模板名(调用形参表){    函数体}

功能:在函数模板的返回类型、调用形参表和函数体中都可以引用该模板的类型形参作为标识符的声明;

template<typename A, typename b, typename _C>A func(b arg){    _C var;    ..}

注意:
  一个函数模板本身并不表示函数,其意义在于为编译器提供一种生成函数代码的框架,在编译器看到具体类型实参以后,结合模板定义中的类型形参,才能得到真正意义上的具体函数;

2、使用

函数模板名<类型实参1, 类型实参2, ..>(调用实参表);

实例化:
  编译器将函数模板变成具体函数的过程一般被称为函数模板的实例化;

参见:ftmpl.cpp

3、类型参数

 1)类型形参只要是合法的标识符即可,没有其它限制;习惯上常用"T"或者"XX_t"的形式表示;
 2)类型实参既可以是基本类型,也可以是类类型,但是无论怎样,该类型必须满足模板内部的实现要求;

参见:targ.cpp

4、延迟编译

int add(int, int);  // 函数声明void foo(void){    int a = 123, b = 456;    int c = add(a,b);  // 函数调用    ..    int d = add(b,c);  // 函数调用    ..}int add(int x, int y){  // 函数定义(仅在此处编译)    return x+y;}

1)普通函数

  非模板化的普通函数,在整个代码的生成(build)过程中只在定义时被编译一次,由链接器负责在调用语句和编译后的函数指令集之间建立关联;

2)模板函数

  函数模板则至少要被编译两次,一次是在实例化之前,编译器会做与类型无关的语法检查,若无误则生成该函数模板的内部表示:只有在编译器看到对该函数模板的调用语句时,才会根据所提供的类型实参与内部表示中的类型形参相结合,完成二次编译,并在类型相关的语法检查通过之后生成二进制机器指令。

参见:typename.cpp

5、隐式推断

  如果函数模板调用参数 (圆括号里的参数)  的类型相关于该模板的模板参数 <尖括号里的参数> ,那么即使不显式指明模板的类型实参,编译器也可以依据函数调用参数类型一致的原则,隐式推断出模板类型参数的具体类型。但是,隐式推断也存在一定风险,特别是在推断类型和代码设计者期望类型不一致的情况下。

参见:deduction.cpp

6、重载

  和普通函数一样,在同一个作用域中函数名相同,参数表不同的函数模板之间,以及函数模板和普通函数之间,也可以构成重载关系;重载匹配的条件,除了要满足类型一致的要求外,针对类型的约束性越强的版本也会被编译器优先选择;

普通函数>半模板>全模板
参见:overload.cpp

三、类模板

如果一个类的成员变量、成员函数、成员类型,甚至基类包含参数化的类型,那么该类就是一个类模板;

1、定义

template<typename 类型形参1, typename 类型形参2, ..>class 类模板名[:继承方式 基类1, ..]{    成员变量;    成员函数;    成员类型;};
template<typename M, typename R, typename A, typename V, typename T, typename B>class MyClass:public B{    M m_mem;    R fun(A arg){..V var..}    typedef T* tp;};

2、使用

类模板名<类型实参1, 类型实参2, ..> 对象(构造实参);类模板名<类型实参1, 类型实参2, ..> &引用 = 对象;类模板名<类型实参1, 类型实参2, ..> *指针 = &引用;

两步实例化:
  类模板 - 实例化(编译期间:编译期) >> 类 - 实例化(运行期间:处理器) >> 对象

注意:
  类模板不能隐式推断,而必须显示实例化;原则上,任何时候类模板本身都不能当做类型看待,只有它的某个实例化类才具备类型语义;

参见:ctmpl.cpp

原创粉丝点击