第21课 - 类模板 - 上

第21课 - 类模板 - 上

    一.类模板的引入

        1.1. 一些类主要用于存储和组织数据元素

            如:数组类,链表类,Stack类,Queue类等等

            

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

        

        1.2 C++中的类模板

            1.2.1 提供一种特殊的以相同的行为处理不同的类型

            1.2.2 在类声明前使用template进行标识

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

            
           template<typename T>
                class Operate {
                public:
                    T add(T a, T b)
                    {
                        return a + b;
                    }
                    
                    T minus(T a, T b)
                    {
                        return a - b;
                    }
                };

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

        1.4 编译器对类模板的处理方式和函数模板相同

            1.4.1 编译器从类模板通过具体类型产生不同的类

            1.4.2 编译器在声明的地方对类模板代码本身进行编译

            1.4.3 编译器在使用的地方对参数替换后的代码进行编译

            

    二.类模板的应用

        2.1 使用具体类型定义对象

          
            Operate<int> op1;        /* 对象op1用于处理Int型的加减法 */
            Operate<double> op2;    /* 对象op1用于处理double型的加减法 */
                
            cout<<op1.add(5,4)<<endl;
            cout<<op2.minus(1.5,0.01)<<endl;  

        2.2 类模板的工程应用

            2.2.1 由于类模板的编译机制不同,所以不能像普通类一样分开实现后,在使用时只包含头文件。

                在工程实践上,一般会把类模板的定义直接放到头文件中!!

           只有被调用的类模板成员函数才会被编译器生产可执行代码!!!

            

            2.2.2 类模板工程应用步骤

                a. 在模板类外部定义成员函数实现时,需要加上

                    template<typename T>的声明

      
     Source Example 2.2.2:
                #include <iostream>

                /* run this program using the console pauser or add your own getch, system("pause") or input loop */

                using namespace std;

                template<typename T>
                class Operate {
                public:
                    T add(T a, T b);
                    T minus(T a, T b);
                };

                template<typename T>
                T Operate<T> :: add(T a, T b)
                {
                    return a + b;
                }

                template<typename T>
                T Operate<T> :: minus(T a, T b)
                {
                    return a - b;
                }

                int main(int argc, char** argv) {
                    Operate<int> op1;        /* 对象op1用于处理Int型的加减法 */
                    Operate<double> op2;    /* 对象op1用于处理double型的加减法 */
                        
                    cout<<op1.add(5,4)<<endl;
                    cout<<op2.minus(1.5,0.01)<<endl;
                    
                    return 0;
                }

            b. 由于类模板的编译机制不同,所以不能像普通类一样分开实现后,在使用时只包含头文件。

          
  Source Example(修改数组类):
                /* array.h */
                #include <iostream>
                #include "array.h"
                /* run this program using the console pauser or add your own getch, system("pause") or input loop */


                int main(int argc, char** argv) {

                    Array<int> ia(10);
                    
                    for (int i = 0; i < 5; i++)
                    {
                        ia[i] = i;
                    }
                    
                    return 0;
                }
                
                /* array.c */
                #include "array.h"
                #include <iostream>

                template <typename T>
                Array<T> :: Array(int length)
                {
                    iLength = length;
                    if (length > 0)
                    {
                        pSpace = new int[length];
                    }
                    printf ("pSpace = %08x\n", pSpace);
                }

                template <typename T>
                int Array<T> :: getLength()
                {
                    return iLength;
                }

                template <typename T>
                Array<T> :: Array(const Array& obj)
                {
                    iLength = obj.iLength;
                    pSpace = new int[iLength];
                    printf ("pSpace = %08x\n", pSpace);
                    
                    for (int i = 0; i < iLength; i++)
                    {
                        pSpace[i] = obj.pSpace[i];
                    }
                }

                template <typename T>
                T& Array<T> :: operator[] (int i)
                {
                    return pSpace[i];
                }

                template <typename T>
                Array<T>& Array<T> :: operator= (const Array<T>& obj)
                {
                    delete[] pSpace;
                    
                    iLength = obj.iLength;
                    pSpace = new int[iLength];

                    for (int i = 0; i < iLength; i++)
                    {
                        pSpace[i] = obj.pSpace[i];
                    }
                    
                    return *this;
                }

                /* 重载比较操作符 */
                template <typename T>
                bool Array<T> :: operator== (const Array<T>& obj)
                {
                    bool ret = true;
                    
                    if (iLength == obj.iLength)
                    {
                        for (int i = 0; i < iLength; i++)
                        {
                            if (pSpace[i] != obj.pSpace[i])
                            {
                                ret = false;
                                break;
                            }
                        }
                    }
                    else
                        ret = false;
                    
                    return ret;
                    
                }
                template <typename T>
                bool Array<T> :: operator!= (const Array<T>& obj)
                {
                    return !(*this == obj);
                }
                template <typename T>
                Array<T> :: ~Array()
                {
                    delete[] pSpace;
                }
                
                /* main.c */
                #include <iostream>
                #include "array.h"
                /* run this program using the console pauser or add your own getch, system("pause") or input loop */


                int main(int argc, char** argv) {

                    Array<int> ia(10);
                    
                    /* 链接会出错 */
                    for (int i = 0; i < 5; i++)
                    {
                        ia[i] = i;
                    }
                    
                    return 0;
                }
        
            问题分析:
                1. 编译普通类是, main.cpp包含.h,整个工程会对所有文件进行编译
                    执行ia[i] = i + 1;时,首先进行语法检查,然后进行链接。
                    ia[i]是操作符重载,根据成员函数来实现,对三个文件进行编译, operate[]的代码被生成,就链接过了。
                
                2. 编译类模板时,第一次编译三个文件,进行语法检查
                    在第二次编译(第二次编译要生成代码)array.cpp时,发现都是函数模板,因此只检查语法错误,不生成代码。
                    编译ia[i] = i + 1;时,需要链接operate[]可执行代码,因此会链接出错。
                    
                因此,在main.cpp中需要#include <array.cpp>,而不是#include <array.h>
                也需要在array.cpp中添加 #ifndef _ARRAY_DEF_H_
                                        #define _ARRAY_DEF_H_
                                        #endif  _ARRAY_DEF_H_
                文件后缀名修改为.hpp
                
                注意:只有被调用的类模板成员函数才会被编译器生产可执行代码!!
                    (二次编译)

                    
      
  真正的数组类:
        /* array.h */
            #ifndef _ARRAY_H_
            #define _ARRAY_H_

            template <typename T>
            class Array{
                private:
                    int iLength;
                    T *pSpace;
                public:
                    Array(int length);
                    Array(const Array& a);
                    int getLength();
                    ~Array();
                    T& operator[] (int i);
                    Array& operator= (const Array& obj);
                    bool operator== (const Array& obj);
                    bool operator!= (const Array& obj);
            };

            #endif /* _ARRAY_H_ */
        
        /* array.hpp */
        #ifndef _ARRAY_DEF_H_
        #define _ARRAY_DEF_H_
        
        #include "array.h"
        #include <iostream>

        template <typename T>
        Array<T> :: Array(int length)
        {
            iLength = length;
            if (length > 0)
            {
                pSpace = new T[length];
            }
            printf ("pSpace = %08x\n", pSpace);
        }

        template <typename T>
        int Array<T> :: getLength()
        {
            return iLength;
        }

        template <typename T>
        Array<T> :: Array(const Array& obj)
        {
            iLength = obj.iLength;
            pSpace = new int[iLength];
            printf ("pSpace = %08x\n", pSpace);
            
            for (int i = 0; i < iLength; i++)
            {
                pSpace[i] = obj.pSpace[i];
            }
        }

        template <typename T>
        T& Array<T> :: operator[] (int i)
        {
            return pSpace[i];
        }

        template <typename T>
        Array<T>& Array<T> :: operator= (const Array<T>& obj)
        {
            delete[] pSpace;
            
            iLength = obj.iLength;
            pSpace = new int[iLength];

            for (int i = 0; i < iLength; i++)
            {
                pSpace[i] = obj.pSpace[i];
            }
            
            return *this;
        }

        /* 重载比较操作符 */
        template <typename T>
        bool Array<T> :: operator== (const Array<T>& obj)
        {
            bool ret = true;
            
            if (iLength == obj.iLength)
            {
                for (int i = 0; i < iLength; i++)
                {
                    if (pSpace[i] != obj.pSpace[i])
                    {
                        ret = false;
                        break;
                    }
                }
            }
            else
                ret = false;
            
            return ret;
            
        }
        template <typename T>
        bool Array<T> :: operator!= (const Array<T>& obj)
        {
            return !(*this == obj);
        }
        template <typename T>
        Array<T> :: ~Array()
        {
            delete[] pSpace;
        }
        
        #endif /* _ARRAY_DEF_H_ */
        
        /* main.cpp */
        #include <iostream>
        #include "array.hpp"
        /* run this program using the console pauser or add your own getch, system("pause") or input loop */

        using namespace std;
        int main(int argc, char** argv) {

            Array<int> ia(5);
            
            for (int i = 0; i < ia.getLength(); i++)
            {
                ia[i] = i;
            }
            
            for (int i = 0; i < ia.getLength(); i++)
            {
                cout<<ia[i]<<endl;
            }
            
            Array<double> ib(5);
            
            for (int i = 0; i < ia.getLength(); i++)
            {
                ib[i] = i;
            }
            
            for (int i = 0; i < ia.getLength(); i++)
            {
                cout<<ib[i]<<endl;
            }
            
            
            return 0;
        }

    三.类模板的特化

        3.1 类模板可以被特化,即处理一些无法被处理的类型

         
   Source Example 3.1:
                #include <iostream>
                /* run this program using the console pauser or add your own getch, system("pause") or input loop */

                using namespace std;
                template <typename T>
                class Test {
                public:
                    T test(T v)
                    {
                        cout << "T test(T v)"<< endl;
                        cout << "sizeof(T) = "<< sizeof(T)<<endl;
                        
                        return v;
                    }
                };

                int main(int argc, char** argv) {
                    
                    Test<int> t1;
                    
                    cout<<t1.test(4)<<endl;
                    
                    return 0;
                }
                

            输出结果如下:

                T test(T,v)

                sizeof(T) = 4

         但是,我们想要得到结果

                T test(int,4)

                sizeof(int) = 4

                

            这时,就对Int类型进行特化

            
Source Example:
                #include <iostream>
                /* run this program using the console pauser or add your own getch, system("pause") or input loop */

                using namespace std;
                template<typename T>
                class Test {
                public:
                    T test(T v)
                    {
                        cout << "T test(T v)"<< endl;
                        cout << "sizeof(T) = "<< sizeof(T)<<endl;
                        
                        return v;
                    }
                };


                template<>
                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> t2;
                    Mytest t1;
                    
                    cout<<t1.test(4)<<endl;
                    
                    return 0;
                }
            

    四.特化类模板的意义

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

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

           
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章