第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;
                }
            

    四.特化類模板的意義

        當類模板在處理某種特定類型有缺陷時,可以通過類模板特化來克服處理這種特定類型帶來的不足!

            注意:編譯器優先選擇特化類生成對象

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