17. C++类模板

在C++中我们往往需要编写多个形式和功能都相似的类,于是 引人了类模板的概念,编译器从类模板可以自动生成多个类,避免了程序员的重复工作。

17.1 类模板基本格式

C++ 中类模板的写法如下:

template <类型参数表>
class 类模板名{
    成员函数和成员变量
};

类型参数表的写法如下:

class类塑参数1, class类型参数2, ...

类模板中的成员函数放到类模板定义外面写时的语法如下:

template <类型参数表>
返回值类型  类模板名<类型参数名列表>::成员函数名(参数表)
{
    ...
}

用类模板定义对象的写法如下:

类模板名<真实类型参数表> 对象名(构造函数实际参数表);

如果类模板有无参构造函数,那么也可以使用如下写法:

类模板名 <真实类型参数表> 对象名;

类模板看上去很像一个类。下面以 Pair 类模板为例来说明类模板的写法和用法。

实际中,某项数据记录由两部分组成,一部分是关键字,另一部分是值。关键字用来对记录进行排序和检索,根据关键字能查到值。如,学生记录由两部分组成,一部分是学号,另一部分是绩点。要能根据学号对学生进行排序,以便方便地检索绩点,则学号就是关键字,绩点就是值。
例如:

#include <iostream>
#include <string>
using namespace std;
template <class T1,class T2>
class Pair
{
public:
    T1 key;  //关键字
    T2 value;  //值
    Pair(T1 k,T2 v):key(k),value(v) { }
    bool operator < (const Pair<T1,T2> & p) const;
};
template<class T1,class T2>
bool Pair<T1,T2>::operator < (const Pair<T1,T2> & p) const
//Pair的成员函数 operator <
{ //"小"的意思就是关键字小
    return key < p.key;
}
int main()
{
    Pair<string,int> student("哈哈",23); //实例化出一个类 Pair<string,int>
    cout << student.key << " " << student.value;
    return 0;
}

image.png

17.2 函数模板作为类模板成员

类模板中的成员函数还可以是一个函数模板。成员函数模板只有在被调用时才会被实例化。例如下面的程序:

#include <iostream>
using namespace std;
template <class T>
class A
{
public:
    template <class T2>
    void func(T2 t) { cout << t; }  //成员函数模板
};
int main()
{
    A<int> a;
    a.func('K');  //成员函数模板Func被实例化
    a.func("hello");
    return 0;
}

17.3 C++用类模板实现可变长数组

#include <iostream>
#include <cstring>
using namespace std;
template <class T>
class Array
{
    int size; //数组元素的个数
    T *ptr; //指向动态分配的数组
public:
    Array(int s = 0);  //s代表数组元素的个数
    Array(Array & a);
    ~Array();
    void push_back(const T & v); //用于在数组尾部添加一个元素v
    Array & operator=(const Array & a); //用于数组对象间的赋值
    T length() { return size; }
    T & operator[](int i)
    {//用以支持根据下标访问数组元素,如a[i] = 4;和n = a[i]这样的语句
        return ptr[i];
    }
};
template<class T>
Array<T>::Array(int s):size(s)
{
     if(s == 0)
         ptr = NULL;
    else
        ptr = new T[s];
}
template<class T>
Array<T>::Array(Array & a)
{
    if(!a.ptr) {
        ptr = NULL;
        size = 0;
        return;
    }
    ptr = new T[a.size];
    memcpy(ptr, a.ptr, sizeof(T ) * a.size);
    size = a.size;
}
template <class T>
Array<T>::~Array()
{
     if(ptr) delete [] ptr;
}
template <class T>
Array<T> & Array<T>::operator=(const Array & a)
{ //赋值号的作用是使"="左边对象里存放的数组,大小和内容都和右边的对象一样
    if(this == & a) //防止a=a这样的赋值导致出错
    return * this;
    if(a.ptr == NULL) {  //如果a里面的数组是空的
        if( ptr )
            delete [] ptr;
        ptr = NULL;
        size = 0;
        return * this;
    }
     if(size < a.size) { //如果原有空间够大,就不用分配新的空间
         if(ptr)
            delete [] ptr;
        ptr = new T[a.size];
    }
    memcpy(ptr,a.ptr,sizeof(T)*a.size);
    size = a.size;
     return *this;
}
template <class T>
void Array<T>::push_back(const T & v)
{  //在数组尾部添加一个元素
    if(ptr) {
        T *tmpPtr = new T[size+1]; //重新分配空间
    memcpy(tmpPtr,ptr,sizeof(T)*size); //拷贝原数组内容
    delete []ptr;
    ptr = tmpPtr;
}
    else  //数组本来是空的
        ptr = new T[1];
    ptr[size++] = v; //加入新的数组元素
}
int main()
{
    Array<int> a;
    for(int i = 0;i < 5;++i)
        a.push_back(i);
    for(int i = 0; i < a.length(); ++i)
        cout << a[i] << " ";
    return 0;
}

image.png

注意:
1、类模板中可以定义静态成员,从该类模板实例化得到的所有类都包含同样的静态成员。

#include <iostream>
using namespace std;
template <class T>
class A
{
private:
    static int count;
public:
    A() { count ++; }
    ~A() { count -- ; }
    A(A &) { count ++ ; }
    static void PrintCount() { cout << count << endl; }
};

/*
下面也可以这样写
int A<int>::count = 0;
int A<double>::count = 0;

*/

template<> int A<int>::count = 0;
template<> int A<double>::count = 0;


int main()
{
    A<int> ia;
    A<double> da;
    ia.PrintCount();
    da.PrintCount();
    return 0;
}

2、类模板的“类型参数表”中可以出现非类型参数

#include <iostream>
using namespace std;
template<class T, int size>
class Array {
T array[size];
public:
    void Print()
    {
        for (int i = 0; i < size; ++i)
        cout << array[i] << endl;
    }
};

int main()
{

    return 0;
}

可以用 CArray 模板定义对象

Array<int, 40> a;

编译器自动生成名为 CArray<int, 40> 的类。该类是通过将 CArray 模板中的 T 换成 int、 size 换成 40 后得到的。

还可定义以下对象:

Array <double, 40> a2;
Array <int, 50> a3;

注意,Array<double, 40> 和 Array<int, 50> 完全是两个类,这两个类的对象之间不能互相赋值。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • 3. 类设计者工具 3.1 拷贝控制 五种函数拷贝构造函数拷贝赋值运算符移动构造函数移动赋值运算符析构函数拷贝和移...
    王侦阅读 2,083评论 0 1
  • 前言 把《C++ Primer》[https://book.douban.com/subject/25708312...
    尤汐Yogy阅读 9,698评论 1 51
  • 第一天 一.内联函数(inline) 函数调用的时候需要建立栈内存环境,进行参数传递,并产生程序执行转移,这些工作...
    陈果123阅读 1,233评论 0 1
  • 面向对象编程(OOP) 在前面的章节中,我们学习了Kotlin的语言基础知识、类型系统、集合类以及泛型相关的知识。...
    Tenderness4阅读 4,634评论 1 6
  • 【概念】点菜的时候要以一种礼貌的态度,尽量用please和thanks,当菜端上来时要给予服务生积极的回应来表达你...
    土管231聂仁华阅读 119评论 0 0

友情链接更多精彩内容