1. 程式人生 > 其它 >C++進階-1-模板基礎(函式模板、類模板)

C++進階-1-模板基礎(函式模板、類模板)

C++進階 模板

1.1 函式模板

  1 #include<iostream>
  2 using namespace std;
  3 
  4 // 模板
  5 
  6 // 模板的簡單例項
  7 // 要求:
  8 //        1.利用函式模板封裝一個排序函式,可以對不同資料型別陣列進行排序
  9 //        2.排序規則由大到小,排序演算法為:選擇排序
 10 //        3.分別利用char陣列和int陣列進行測試
 11 
 12 // 交換函式模板
 13 template<class T>
 14 void mySwap(T& a, T& b) {
15 T temp = a; 16 a = b; 17 b = temp; 18 } 19 20 // 排序演算法模板 21 template<class T> // 也可以寫typename 22 void mySort(T arr[], int len) { 23 // 選擇排序 24 for (int i = 0; i < len; i++) { 25 int max = i; // 認定最大值下標 26 for (int j = i + 1; j < len; j++) { 27
// 認定的最大值 比 遍歷出的最大值 要小,說明 j 下標的元素才是真正的最大值 28 if (arr[max] < arr[j]) { 29 max = j; // 更新最大值下標 30 } 31 } 32 if (max != i) { 33 // 交換下標為max和i的元素 34 mySwap(arr[max], arr[i]); 35 } 36 } 37 } 38
39 // 列印數字內容模板 40 template<class T> 41 void printArray(T arr[], int len) { 42 for (int i = 0; i < len; i++) { 43 cout << arr[i] << " "; 44 } 45 cout << endl; 46 } 47 48 49 void test01() { 50 51 // 測試陣列 char型別 52 char charArr[] = "badcfe"; 53 int num_charArray = sizeof(charArr) / sizeof(char); 54 mySort(charArr, num_charArray); 55 printArray(charArr, num_charArray); 56 57 // 測試陣列 int型別 58 int intArray[] = { 1, 3, 2, 6, 4, 5, 7 }; 59 int num_int = sizeof(intArray) / sizeof(int); 60 mySort(intArray, num_int); 61 printArray(intArray, num_int); 62 } 63 64 int main() { 65 66 test01(); 67 68 system("pause"); 69 70 return 0; 71 } 72 73 // 總結 74 // C++除了面向物件程式設計思想外,還有 泛型程式設計 以及 STL 技術 75 // 76 // 模板:建立通用的模具,大大提高程式碼的複用性 77 // 模板不可以直接使用,它只是一種框架 78 // 模板的通用並不是萬能的 79 // 80 // 模板分為:函式模板與類模板 81 // 82 // 83 // 函式模板: 84 // 85 // 模板的作用:建立一個通用函式,其函式返回值型別和形參型別都可以不具體制定,用一個虛擬的型別來表示 86 // 語法: 87 // template<typename T> // typename也可以寫成 class 88 // 函式生命或定義 89 // 解釋:T 通用的資料型別,名稱可以替換,通常為大寫字母 90 // 91 // 函式模板的使用方式有兩種:自動型別推導、顯示指定型別 92 // 93 // 模板注意事項: 94 // 1.自動型別推導方式,必須推匯出一致的資料型別T,才可以使用 95 // 2.模板必須要確定出T的資料型別,才可以使用 96 // 97 // 普通函式與函式模板的區別 98 // 1.普通函式呼叫可以發生自動型別轉換(隱式轉換); 99 // 2.函式模板呼叫時,如果利用自動型別推導,不會發生隱式型別轉換 100 // 3.如果利用顯示指定型別的方式,可以發生隱式轉換; 101 // 102 // 所以,建議使用顯示指定型別的方式,呼叫函式模板,因為可以自己確定通用型別 103 // 104 // 普通函式與函式模板的呼叫規則 105 // 1.如果函式模板和普通函式都有可以實現,編譯器會優先呼叫普通函式 106 // 2.可以通過空模板引數列表來強制呼叫函式模板(加<>) 107 // 3.函式模板也可以發生過載 108 // 4.如果利用函式模板可以產生更好的匹配,優先呼叫函式模板 109 // 110 // 所以,既然提供了函式模板,就最好不要再提供普通函數了,否則出現二義性 111 // 112 // 模板的侷限性 113 // 1.模板的通用型不是萬能的 114 // 利用具體化的模板,可以解決自定義型別的通用化 115 // 學習模板不是為了寫模板,而是在STL能夠運用系統提供的模板 116 // 117

 

1.2 類模板

#include<iostream>
#include<string>
using namespace std;

// 類 模板


// 示例1
//template<class Nametype, class Agetype>

// 類模板可以有預設引數
template<class Nametype, class Agetype = int>
class Person1 {
public:

    Person1(Nametype name, Agetype age) {
        this->m_Name = name;
        this->m_Age = age;
    }

    void showPerson() {
        cout << "姓名:" << this->m_Name << " 年齡:" << this->m_Age << endl;
    }

    //string m_Name;
    //int m_Age;

    Nametype m_Name;
    Agetype m_Age;

};

void test01() {

    //Person p1("Tom", 19);  // 報錯,無法自動推導型別
    //Person1<string, int> p1("Tom", 19);
    Person1<string> p1("Tom", 19);

    p1.showPerson();

}

// 示例2
// 類模板物件做函式引數,三種傳入方式

template<class T1, class T2>
class Person2
{
public:
    Person2(T1 name, T2 age) {
        this->m_Name = name;
        this->m_Age = age;
    };

    void showPerson() {
        cout << "姓名:" << this->m_Name << " 年齡:" << this->m_Age << endl;
    }

    T1 m_Name;
    T2 m_Age;

};

// 1.指定傳入型別
void printPerson1(Person2<string, int>& p) {
    p.showPerson();
}

// 2.引數模板化
template<class T1, class T2>
void printPerson2(Person2<T1, T2>& p) {
    p.showPerson();
    // 看一下編譯器推匯出的型別
    cout << "T1的型別為:" << typeid(T1).name() << endl;
    cout << "T2的型別為:" << typeid(T2).name() << endl;
}

// 3.整個類模板化
template<class T>
void printPerson3(T& p) {
    p.showPerson();
    cout << "T的型別為:" << typeid(T).name() << endl;
}

void test02() {
    Person2<string, int>p("小明", 20);
    
    // 1.指定傳入型別(最常用)
    printPerson1(p);

    // 2.引數模板化
    printPerson2(p);

    // 3.整個類模板化
    printPerson3(p);
}


// 示例3 類模板與繼承
template<class T>
class Base {
    T m;
};

// class Son:pubic Base  // 錯誤,C++編譯需要給子類分配記憶體,必須知道父類中T的型別才可以向下繼承
class Son1 :public Base<int> {  // 必須指定一個型別

};

// 如果想靈活制定出父類中T的型別,子類也需要變為模板
template<class T1, class T2>
class Son2 :public Base<T2> {
public:
    Son2() {
        cout << "T1的型別為:" << typeid(T1).name() << endl;
        cout << "T2的型別為:" << typeid(T2).name() << endl;
    }
    T1 obj;
};

void test03() {

    Son1 s1;

    Son2<int, char> s2;
}

// 示例4 類模板成員函式類外實現
template<class T1, class T2>
class Person4
{
public:
    Person4(T1 name, T2 age);  // 類內宣告
    //{
    //    this->m_Name = name;
    //    this->m_Age = age;
    //};

    void showPerson();  // 類內宣告
    //{
    //    cout << "姓名:" << this->m_Name << " 年齡:" << this->m_Age << endl;
    //}

    T1 m_Name;
    T2 m_Age;

};

// 類外實現
template<class T1, class T2>
Person4<T1, T2>::Person4(T1 name, T2 age) {
    this->m_Name = name;
    this->m_Age = age;
}

template<class T1, class T2>
void Person4<T1, T2>::showPerson() {
    cout << "姓名:" << this->m_Name << " 年齡:" << this->m_Age << endl;
}


void test04() {
    Person4<string, int> p4("張三", 100);
    p4.showPerson();
}


int main() {

    // 類模板初識
    //test01();

    // 類模板物件做函式引數
    //test02();

    // 類模板與繼承
    //test03();

    // 類模板成員函式類外實現
    test04();

    system("pause");

    return 0;
}

// 總結
// 
// 模板分為:函式模板與類模板
// 
// 
// 類模板:
// 
// 類模板作用:建立一個通用類,類中的成員 資料型別可以不具體制定,用一個虛擬型別來代表
// 
// 語法:
//        template<typename T>  // typename可以寫成 class,與typename沒啥具體區別
////    
// 類模板與函式模板區別:
//    1.類模板沒有自動型別推導的使用方式,所以必須指定
//    2.類模板在模板引數列表中可以有預設引數
// 
// 類模板中成員函式的建立時機
//    1.普通類中的成員函式一開始就可以建立
//    2.類模板中的成員函式在呼叫時才開始建立
// 
// 類模板物件做函式引數
//    類模板例項化出物件,向引數傳參
//    三種傳參方式:
//        1.制定傳入型別    直接顯示物件的資料型別(最常用)
//        2.引數模板化    將物件中的引數變為模板進行傳遞
//        3.整個類模板化    將這個物件型別 模板化進行傳遞
// 
// 類模板與繼承
//    1.當子類繼承的父類是一個類模板時,子類在宣告的時候,要指定出父類T的型別
//    2.如果不指定,編譯器無法給子類分配記憶體
//    3.如果想靈活制定出父類中T的型別,子類也需要變為模板
// 
// 類模板的成員函式可以類外實現(類內宣告,類外實現)
//    需要新增:1.作用域;2.引數模板/引數模板列表
// 
// 類模板份檔案編寫
//    問題:類模板中成員函式建立時機是在 呼叫階段 ,導致分檔案編寫時連結不到
// 
//    解決:
//        方式1:直接包含.cpp原始檔
//        方式2:將宣告和實現寫到同一個檔案中,並更改字尾名為.hpp hpp是約定的名稱,並不強制
// 
// 類模板與友元
//    全域性函式類內實現:直接在類內宣告友元即可(建議使用類內實現)
//    全域性函式類外實現:需要提前讓直譯器知道全域性函式的存在
//        1.加一個空模板的引數列表<>
//        2.把程式碼剪下到程式碼檔案的最上方,讓編譯器先看到
//        3.加入類的宣告,提前讓編譯器看到模板類
//