C++--第21课 - 类模板 - 上

醉酒当歌 提交于 2019-11-27 00:13:26

第21课 - 类模板 - 上

思考:类是C++的核心,那是否能够将模板的思想应用于类呢?

 

1. 类模板

一些类主要用于存储和组织数据元素。如:数组类,链表类,Stack类,Queue类等等。

C++中可以将模板的思想应用于类,使得类可以不关注具体所操作的数据类型,而是只关注所需要实现的功能。

C++中的类模板,提供一种特殊的类似相同的行为处理不同的类型,在声明前使用template进行标识。

<typename T>用于说明类中使用的泛指类型 T

template<typename T>

class Operator

{

public:

T add(T a, T b)

{

return a + b;

}

T minus(T a, T b)

{

return a - b;

}

}

声明的泛指类型T可用于声明成员变量和成员函数。

编译器对类模板的处理方式和函数模板相同:编译器从类模板通过具体类型产生不同的类;编译器在声明的地方对类模板代码本身进行编译;编译器在使用的地方对参数替换后的代码进行编译。

 

类模板的应用--使用具体类型定义对象

Operator<int> op1; //对象op1用于处理int类型的加减法

Operator<double> op2; //对象op2用于处理double类型的加减法

cout<<op1.add(5, 4)<<endl;  //返回值也是int

cout<<op2.minus(1.5, 0.01)<<endl;  //返回值也是double

如果不看头两行代码,只看后面两行代码,我们无法知道,op1和op2究竟是处理什么的代码,会出现二义性,所以前两行的声明时必须的。

 

类模板的工程应用

由于类模板的编译机制不同,所以不能像普通类一样分开实现后再使用时包含头文件。在工程实践上,一般会把类模板的定义直接放到头文件中。只有被调用的类模板成员函数才会被编译器生成可执行代码。

在模板类外部定义成员函数的实现上,需要加上template<typename T>的声明。

 

#include <cstdlib>

#include <iostream>

using namespace std;

template<typename T>

class Operator

{

public:

    T add(T a, T b);

    T minus(T a, T b);

};

template<typename T>

T Operator<T>::add(T a, T b)

{

    return a + b;

}

template<typename T>

T Operator<T>::minus(T a, T b)

{

    return a - b;

}

 

int main(int argc, char *argv[])

{

    Operator<int> op1;

    Operator<double> op2;

    cout<<op1.add(5, 4)<<endl;

    cout<<op1.minus(4, 5)<<endl;

    cout<<op2.add(1.3, 0.01)<<endl;

    cout<<op2.minus(0.01, 1.3)<<endl;

    cout << "Press the enter key to continue ...";

    cin.get();

    return EXIT_SUCCESS;

}

运行结果:

9

-1

1.31

-1.29

 

2. 真正的数组类

Array.h    

#ifndef _ARRAY_H_

#define _ARRAY_H_

template<typename T>

class Array

{

private:

    int mLength;

    T* mSpace;  //int* mSpace

public:

    Array(int length);

    Array(const Array& obj);

    int length();

    ~Array();

    T& operator[](int i); // int& operator[](int i);

    Array& operator= (const Array& obj);

    bool operator== (const Array& obj);

    bool operator!= (const Array& obj);

};

#endifArray.hpp

#ifndef _ARRAY_DEF_H_

#define _ARRAY_DEF_H_

#include "Array.h"

template<typename T> //必须有 ,每个函数开头都有 

Array<T>::Array(int length)//每个开头都有 Array<T>

{

    if( length < 0 )

    {

        length = 0;

    } 

    mLength = length;

    mSpace = new T[mLength];

}

template<typename T>

Array<T>::Array(const Array& obj)

{

    mLength = obj.mLength;  

    mSpace = new int[mLength]; 

    for(int i=0; i<mLength; i++)

    {

        mSpace[i] = obj.mSpace[i];

    }

}

template<typename T>

int Array<T>::length()

{

    return mLength;

}

template<typename T>

Array<T>::~Array()

{

    mLength = -1;

   

    delete[] mSpace;

}

template<typename T>

T& Array<T>::operator[](int i)

{

    return mSpace[i];

}

template<typename T>

Array<T>& Array<T>::operator= (const Array<T>& obj)

{

    delete[] mSpace; 

    mLength = obj.mLength;

    mSpace = new int[mLength];

    for(int i=0; i<mLength; i++)

    {

        mSpace[i] = obj.mSpace[i];

    }

    return *this;

}

template<typename T>

bool Array<T>::operator== (const Array<T>& obj)

{

    bool ret = true; 

    if( mLength == obj.mLength )

    {

        for(int i=0; i<mLength; i++)

        {

            if( mSpace[i] != obj.mSpace[i] )

            {

                ret = false;

                break;

            }

        }

    }

    else

    {

        ret = false;

    }

    return ret;

}

template<typename T>

bool Array<T>::operator!= (const Array& obj)

{

    return !(*this == obj);

}

#endif

main.cpp

#include <cstdlib>

#include <iostream>

#include "Array.hpp"

using namespace std;

int main(int argc, char *argv[])

{

    Array<int> ai(5);

    for(int i=0; i<ai.length(); i++)

    {

        ai[i] = i + 1;

    }

    for(int i=0; i<ai.length(); i++)

    {

        cout<<ai[i]<<endl;

    }

    Array<double> ad(10);

    for(int i=0; i<ad.length(); i++)

    {

        ad[i] = (i + 1) / 100.0;

    }

    for(int i=0; i<ad.length(); i++)

    {

        cout<<ad[i]<<endl;

    }

    cout << "Press the enter key to continue ...";

    cin.get();

    return EXIT_SUCCESS;

}

运行结果:

1

2

3

4

5

0.01

0.02

0.03

0.04

0.05

0.06

0.07

0.08

0.09

0.1

分析:

我们要是像以前一样包含的头文件是#include<Array.h>时。对于普通的类,编译函数的时候,会对main,.h,.cpp文件一起编译,并且生成代码。对于类模板,编译器遇到这三个文件的时候,发现是模板,就会去检查是否错误,而不会去生成代码,这里指的是Operator[]的代码。当编译到a[i] = i +1的时候,编译器就会想生成相应的操作符重载的代码,但是在main函数中,是没有这个函数的,所以此时语法会出错。所以我们采用的方法是加入头文件#include<Array.hpp>。我们这里为了防止重复包含,也是用了宏定义。#idndef #define #endif

 

 

3. 类模板的特化

类模板可以被特化,用template<>声明一个类时,表示这是一个特化类。

#include <cstdlib>

#include <iostream>

 

using namespace std;

 

template<typename T>  //Test 类模板

class Test

{

public:

    T test(T v)

    {

        cout<<"T test(T v)"<<endl;

        cout<<"sizeof(T) = "<<sizeof(T)<<endl;

        return v;

    }

};

template<>  //Test 类模板的 int化,也算是一种模板,只是只能是int

class Test<int>

{

public:

    int test(int v)

    {

        cout<<"int test(int v)"<<endl;

        cout<<"sizeof(int) = "<<sizeof(int)<<endl;

       

        return v;

    }

};

class MyTest : public Test<int> //通过继承成较简单的特化类,也就是类模板特化的意义。

{

};

int main(int argc, char *argv[])

{

    Test<int> t1;

    Test<double> t2 ;

    cout<<t1.test(1)<<endl;

    cout<<t2.test(0.1)<<endl;

    cout << "Press the enter key to continue ...";

    cin.get();

    return EXIT_SUCCESS;

}

运行结果:

int test(int v)

sizeof(int) = 4

1

T test(T v)

sizeof(int) = 8

0.1

特化类模板的意义:

当类模板在处理某种特定类型有缺陷时,可以通过类模板的特化来克服处理特定类型带来的不足。

注意:编译器优先选择特化类生成对象。

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!