STL(模板学习)day01 模板的史前时代 函数模板 类模板

本文深入探讨了C++中的模板概念,包括函数模板和类模板的定义与使用,讲解了模板的历史背景、模板的实例化过程、类型参数的隐式推断、缺省类型参数的设置以及模板与普通函数的重载解析。此外,还介绍了如何通过类模板实现数组容器,展示了模板在泛型编程中的强大功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

模板和STL
一、模板的史前时代
数据类型:数据在内存中的存储形式、编码方式、访问形式
静态类型系统
int i;
double f = (double)i;
i = 10
i = ‘hello’
i = Student()
优点:高性能、高安全性
缺点:灵活性差
代码:typed.cpp

#include <iostream>
using namespace std;
int min_int(int x, int y) {
    return x < y ? x : y;
}
double min_double(double x, double y) {
    return x < y ? x : y;
}
string min_string(string x, string y) {
    return x < y ? x : y;
}
int main(void) {
    int a = 123, b = 456;
    cout << min_int(a, b) << endl;
    double c = 1.23, d = 4.56;
    cout << min_double(c, d) << endl;
    string e = "hello", f = "world";
    cout << min_string(e, f) << endl;
    char g[] = "world", h[] = "hello";
    cout << min_string(g, h) << endl;
    return 0;
}

借助宏定义摆脱类型的限制
代码:untyped.cpp

#include <iostream>
using namespace std;
#define min(x, y) ((x) < (y) ? (x) : (y))
int main(void) {
    int a = 123, b = 456;
    cout << min(a, b) << endl;
    double c = 1.23, d = 4.56;
    cout << min(c, d) << endl;
    string e = "hello", f = "world";
    cout << min(e, f) << endl;
    char g[] = "world", h[] = "hello";
    cout << min(g, h) << endl;
    return 0;
}

既象函数一样类型安全,又象宏一样类型抽象
代码:macro.cpp

#include <iostream>
using namespace std;
#define MIN(T) \
T min_##T(T x, T y) { \
    return x < y ? x : y; \
}
MIN(int)
MIN(double)
MIN(string)
#define min(T) min_##T
int main(void) {
    int a = 123, b = 456;
    cout << min(int)(a, b) << endl;
    double c = 1.23, d = 4.56;
    cout << min(double)(c, d) << endl;
    string e = "hello", f = "world";
    cout << min(string)(e, f) << endl;
    char g[] = "world", h[] = "hello";
    cout << min(string)(g, h) << endl;
    return 0;
}

二、函数模板
1.函数模板的定义
template<typename 类型参数1, …>
返回类型 函数模板名(形参表) {
函数体
}
函数模板的类型参数可以出现在返回类型、形参表和函数体中。
通过参数化的类型,为函数赋予对数据类型抽象的能力,做到算法与类型无关,即泛型。
2.函数模板的使用
函数模板名<具体类型1, …>(实参表)
函数模板的实例化:由编译器在编译期,将函数模板调用语句中的具体类型与该函数模板定义中的类型参数,按先后顺序一一结合,此时函数模板就变成一个具体函数,编译之,以供调用。
代码:ft.cpp

#include <iostream>
using namespace std;
template<typename T>
T min(T x, T y) {
    return x < y ? x : y;
}
int main(void) {
    int a = 123, b = 456;
    //cout << ::min<int>(a, b) << endl;
    cout << ::min(a, b) << endl;
    double c = 1.23, d = 4.56;
    //cout << ::min<double>(c, d) << endl;
    cout << ::min(c, d) << endl;
    string e = "hello", f = "world";
    //cout << ::min<string>(e, f) << endl;
    cout << ::min(e, f) << endl;
    char g[] = "world", h[] = "hello";
    cout << ::min<string>(g, h) << endl;
    //cout << ::min(g, h) << endl;
    return 0;
}

3.二次编译
当编译器第一次编译函数模板时,并不知道其类型参数所代表的具体类型,这时只做一般性的语法检查,若通过会在编译器内部生成一个该函数模板的内部表示。当编译器看到调用该函数模板时,再将所提供的具体类型与内部表示中的类型参数加以结合,做与类型相关的语法检查,若通过再生成关于具体函数的二进制指令。因此如果在函数模板中,引用了某些依赖于类型参数的内部类型,会引发针对该函数模板第一次编译时的错误。
代码:err.cpp

#include <iostream>
using namespace std;
class A {
public:
    typedef unsigned int UINT;
};
template<typename T>
void foo(void) {
    T::UINT u;
}
int main(void) {
    A::UINT u;
    foo<A>();
    return 0;
}

4.隐式推断
如果函数模板调用参数的类型相关于该模板的类型参数,那么在调用该函数模板时,即使不显式指定模板参数,编译器也有能力根据调用参数的类型隐式推断出正确的模板参数,以获得与普通函数调用一致的语法表达。但是,要注意只有在编译器隐式推断的类型与程序编写者所期望的类型一致时,隐式推断才是安全的。
将数组类型的实参传递给引用类型的形参,所结合的时数组整体,而非首地址,因此模板类型参数的隐式推断也是根据数组整体进行的。
隐式推断和隐式类型转换不能同时发生。
函数模板返回值的类型,以及函数体内部使用局部类型,不参与隐式推断。
代码:ft.cpp

#include <iostream>
using namespace std;
template<typename T>
T min(T x, T y) {
    return x < y ? x : y;
}
int main(void) {
    int a = 123, b = 456;
    //cout << ::min<int>(a, b) << endl;
    cout << ::min(a, b) << endl;
    double c = 1.23, d = 4.56;
    //cout << ::min<double>(c, d) << endl;
    cout << ::min(c, d) << endl;
    string e = "hello", f = "world";
    //cout << ::min<string>(e, f) << endl;
    cout << ::min(e, f) << endl;
    char g[] = "world", h[] = "hello";
    cout << ::min<string>(g, h) << endl;
    //cout << ::min(g, h) << endl;
    return 0;
}

代码:dedc.cpp

#include <cstdlib>
#include <iostream>
#include <typeinfo>
using namespace std;
template<typename T>
void foo(T const& x, T const& y) {
    cout << typeid(x).name() << ' '
        << typeid(y).name() << endl;
}
template<typename R, typename T>
R bar(T const& x) {
    R y;
    cout << typeid(x).name() << ' '
        << typeid(y).name() << endl;
    return y;
}
int main(void) {
    int a, b;
    foo(a, b); // T=int, i i
    double c, d;
    foo(c, d); // T=double, d d
    char e[256], f[256];
    foo(e, f); // T=char[256], A256_c A256_c
    foo("hello", "world");
               // T=char[6], A6_c A6_c
    //foo("hello", "tarena");
    foo(string("hello"), string("tarena"));
               // T=string
    //foo(a, c);
    foo<int>(a, c); // T=int, c->int
    foo((double)a, c); // T=double, a->double
    int i;
    double j;
    //i = bar(j);
    i = bar<int, double>(j); // d i
    i = bar<int>(j); // d i
    return 0;
}

5.缺省类型参数
98标准中禁止为函数模板的类型参数指定缺省值,但是2011以上的标准放开了这个限制,但是当函数模板类型参数的缺省值与隐式推断的类型不一致时,以隐式推断的类型为准,忽略缺省值。
代码:def.cpp

#include <iostream>
using namespace std;
template<typename T = int>
T const& min(T const& x, T const& y) {
    return x < y ? x : y;
}
int main(void) {
    int a = 123, b = 456;
    cout << ::min(a, b) << endl;
    double c = 1.23, d = 4.56;
    cout << ::min(c, d) << endl;
    return 0;
}

6.函数模板和函数模板以及函数模板和普通函数同样可构成重载关系,只是附加了一个重载解析的条件:类型针对性越强,即越具体的版本越会被优先匹配。
代码:over.cpp

#include <cstdlib>
#include <cstring>
#include <iostream>
#include <typeinfo>
using namespace std;
// 两个任意类型值的最大值
template<typename T>
T const& max(T const& x, T const& y) {
    cout << "<1" << typeid(x).name() << ">"
        << flush;
    return x < y ? y : x;
}
// 两个任意类型指针所指向目标的最大值
template<typename T>
T* const& max(T* const& x, T* const& y) {
    cout << "<2" << typeid(x).name() << ">"
        << flush;
    return *x < *y ? y : x;
}
// 两个C字符串的最大值
char const* const& max(char const* const& x,
    char const* const& y) {
/*
char const* max(char const* x, char const* y){
*/
    cout << "<3" << typeid(x).name() << ">"
        << flush;
    return strcmp(x, y) < 0 ? y : x;
}
// 三个任意类型值的最大值
template<typename T>
T const& max(T const& x, T const& y,
    T const& z) {
    cout << "<4" << typeid(x).name() << ">"
        << flush;
    return ::max(::max(x, y), z);
}
/*
char const* const& max(char const* const& x,
    char const* const& y) {
    cout << "<3" << typeid(x).name() << ">"
        << flush;
    return strcmp(x, y) < 0 ? y : x;
}
*/
int main(void) {
    int a = 123, b = 456;
    cout << ::max(a, b) << endl;    // 1
    cout << *::max(&a, &b) << endl; // 2
    char const* c = "hello", *d = "hfrld";
    cout << ::max(c, d) << endl;    // 3
    cout << ::max<>(c, d) << endl;  // 2
    cout << ::max<char const*>(c, d) << endl;
                                    // 1
    char const* e = "heart";
    cout << ::max(c, d, e) << endl; // 4->3
    return 0;
}

三、类模板
1.类模板的定义
template<typename 类型参数1, …>
class 类模板名 : 基类 {
成员变量
成员函数
成员类型
};
类模板的类型参数可以出现在基类、成员变量、成员函数和成员类型中。
template<typename M, typename R, typename A,
typename V, typename T, typename B>
class MyClass : public B {
M m_mem;
R function(A arg) {
V var;
}
typedef T* pointer;
};
2.类模板的使用
类模板名<具体类型1, …> 对象(…);
类模板名<具体类型1, …>* 指针 = &对象;
类模板名<具体类型1, …>& 引用 = 对象;
_/
|

编译器 处理器
类模板-实例化->类-实例化->对象
编译期 运行期
_
________/
|
类模板的两步实例化
代码:ct.cpp

#include<iostream>
using namespace std;
template<typename T>
class Comparator {
public:
    Comparator(T x, T y) :
        m_x(x), m_y(y) {}
    T max(void) const {
        return m_x < m_y ? m_y : m_x;
    }
    /*
    char const* max(void) const {
        return strcmp(m_x, m_y) < 0 ?
            m_y : m_x;
    }
    */
    T min(void) const {
        return m_x < m_y ? m_x : m_y;
    }
private:
    T m_x;
    T m_y;
};
class Integer {
public:
    Integer(int const& data) : m_data(data) {}
    bool operator<(Integer const& rhs) const {
        return m_data < rhs.m_data;
    }
    int m_data;
};
int main(void) {
    int a = 123, b = 456;
    Comparator<int> ci(a, b);
    cout << ci.max() << ' '
        << ci.min() << endl;
    double c = 1.23, d = 4.56;
    Comparator<double> cd(c, d);
    cout << cd.max() << ' '
        << cd.min() << endl;
    string e = "hello", f = "world";
    Comparator<string> cs(e, f);
    cout << cs.max() << ' '
        << cs.min() << endl;
    Integer g(123), h(456);
    //cout << (g < h) << endl;
    Comparator<Integer> cn(g, h);
    cout << cn.max().m_data << ' '
        << cn.min().m_data << endl;
    char i[] = "world", j[] = "hello";
    Comparator<char const*> cp(i, j);
    cout << cp.max() << ' '
        << cp.min() << endl;
    return 0;
}

类模板必须显式实例化,不支持类型参数的隐式推断。
3.模板对类型的要求
一个具体类型能够支持模板的功能,取决于该类型是否能满足该模板对类型的需要,如果无法满足其中的某些需要,调用依赖于这些需要的成员函数将引发编译错误。因此,模板的使用者要尽可能提供模板所需要的功能。
一个良好设计的模板,对于实例化该模板的类型要求应该越少越好,以此提高模板的通用性。因此,模板的设计者要尽量避免对实例化该模板的类型做过多的假设。
4.类模板的静态成员变量,既不是一个模板一份实例,也不是一个对象一份实例,而是在该类模板的每个实例化类中都有一份独立的实例,且为该实例化类所创建的所有对象所共享。
代码:static.cpp

#include <iostream>
using namespace std;
template<typename T>
class A {
public:
    void print(void) const {
        cout << &m_v << ' ' << &m_s << endl;
    }
private:
    T m_v;
    static T m_s;
};
template<typename T>
T A<T>::m_s;
int main(void) {
    A<int> a, b;
    A<double> c, d;
    a.print();
    b.print();
    c.print();
    d.print();
    return 0;
}

5.通过类模板实现数组容器
1)作为数组容器是一个独立的对象,可被以副本形式赋值、传参和返回,而不象内置数组那样只能以指针的形式传递。
2)通过重载下表运算符,使数组模板也能和普通数组一样通过基零的下标访问其中的元素。
3)作为类模板的实例化类,也可以以具体类型的身份实例化该类模板自身,以此形成空间递归的嵌套结构,这种实例化方式称为类模板的递归实例化。
代码:array.cpp

#include <iostream>
using namespace std;
void foo(int a[3]) {
    cout << a[0] << ' ' << a[1]++ << ' '
        << a[2] << endl;
}
void bar (int b) {
    cout << b++ << endl;
}
int* hum(void) {
    int c[3] = {100, 200, 300};
    return c;
}
int main(void) {
    int a[3] = {10, 20, 30};
    foo(a);
    cout << a[1] << endl;
    int b = 20;
    bar(b);
    cout << b << endl;
    return 0;
}

代码:array2.cpp

#include <iostream>
using namespace std;
template<typename T>
class Array {
public:
    T& operator[](size_t i) {
        return m_a[i];
    }
    T const& operator[](size_t i) const {
        return const_cast<Array&>(*this)[i];
    }
    friend ostream& operator<<(ostream& os,
        Array const& a) {
        for (size_t i = 0; i < sizeof(a.m_a) /
            sizeof(a.m_a[0]); ++i)
            os << '(' << a.m_a[i] << ')';
        return os;
    }
private:
    T m_a[3];
};
template<typename T>
void foo(Array<T> a) {
    cout << a[0] << ' ' << a[1]++ << ' '
        << a[2] << endl;
}
Array<int> bar(void) {
    Array<int> c;
    c[0] = 100;
    c[1] = 200;
    c[2] = 300;
    return c;
}
int main(void) {
    Array<int> a;
    a[0] = 10;
    a[1] = 20;
    a[2] = 30;
    cout << a << endl;
    foo(a);
    cout << a[1] << endl;
    Array<int> c = bar();
    cout << c << endl;
    Array<Array<int> > d;
    for (int i = 0; i < 3; ++i)
        for (int j = 0; j < 3; ++j)
            d[i][j] = (i + 1) * 10 + j + 1;
//d.operator[](i).operator[](j)
    cout << d << endl;
    Array<Array<Array<int> > > e;
    return 0;
}

template<…> class Array;
template<…> class List;
Array<Array<…>>:二维数组
Array<List<…>>:链表数组
List<Array<…>>:数组链表
List<list<…>>:二维链表
List<Array<List<Array<…>>>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值