前言 本文主要作為本人學習C\C++歷程的的一種記錄,以期望通過這種方式加深對知識點的記憶,查漏補缺。如有寫得不對的地方,歡迎大家批評改正。 模板概論 模板是泛型編程的基礎,是創建泛型類或函數的藍圖或公式。 C++提供了兩種模板機制:函數模板和類模板。函數模板,實際上是建立了一個通用函數,其函數類型 ...
前言
本文主要作為本人學習C\C++歷程的的一種記錄,以期望通過這種方式加深對知識點的記憶,查漏補缺。如有寫得不對的地方,歡迎大家批評改正。
模板概論
模板是泛型編程的基礎,是創建泛型類或函數的藍圖或公式。
C++提供了兩種模板機制:函數模板和類模板。函數模板,實際上是建立了一個通用函數,其函數類型和形參類型不具體制定,用一個虛擬的類型來代表。類模板和函數模板的定義和使用類似。
下麵開始逐一介紹C++模板的應用。
函數模板
定義函數模板
點擊查看代碼
template<class T> //註意:T代表泛型的數據類型,不是只能寫T,
void mySwap(T& a, T& b)
{
T temp = a;
a = b;
b = temp;
}
也可以這樣定義函數模板
點擊查看代碼
template<typename T>
void mySwap(T& a,T& b)
{
}
怎麼使用函數模板?
點擊查看代碼
template<class T> //註意:T代表泛型的數據類型,不是只能寫T,
void mySwap(T& a, T& b)
{
T temp = a;
a = b;
b = temp;
}
void test01()
{
int a = 10;
int b = 20;
mySwap(a, b);
cout << "a = " << a << endl;
cout << "b = " << b << endl;
}
//可以這樣定義函數模板
//template<typename T>
//void func(T a, T b)
//{
//
//}
template<class T>
T func1(T a, T b)
{
return a + b;
}
void test02()
{
int a = 10;
double b = 20.3;
//如果使用參數列表指定數據類型,那麼實參中可以隱式轉換,
//如果轉換成功,就調用,轉換不成功就報錯
cout << func1<int>(a, b) << endl;
}
普通函數和函數模板的區別
點擊查看代碼
//1.普通函數可以進行隱式轉換,函數模版不能直接的進行隱性轉換
//普通函數
int myPlus(int a, int b)
{
cout << "普通函數" << endl;
return a + b;
}
//函數模板
template<class T>
int myPlus(T a, T b)
{
cout << "函數模板" << endl;
return a + b;
}
void test01()
{
int a = 10;
double b = 20;
myPlus(a, b); //普通函數可以進行隱性轉換
//myPlus2(a, b); //沒有與參數列表匹配的 函數模板 "myPlus2" 實例
myPlus<int>(a, b); //如果要進行隱性轉換,必須加上參數列表
}
普通函數和函數模板的調用規則
點擊查看代碼
//普通函數
int myPlus(int a, int b)
{
cout << "普通函數" << endl;
return a + b;
}
//函數模板
template<class T>
int myPlus(T a, T b)
{
cout << "函數模板" << endl;
return a + b;
}
//函數模板重載
template<class T>
void myPlus(T a, T b, T c)
{
cout << "函數模板myPlus(T a, T b, T c)" << endl;
}
//1、函數模板和普通函數都可以重載
void test02()
{
int a = 10;
int b = 20;
//2、如果普通函數和函數模板都可以實現的功能,普通函數優先調用
myPlus(a, b);
//3、可以使用<>空參數列表強制調用函數模板
myPlus<>(a, b);
//4、函數模板之間也可以進行重載
//5、如果函數模板可以產生更好的匹配,那麼優先使用函數模板
char c1 = 'a';
char c2 = 'b';
myPlus(c1, c2);
}
模板機制剖析——c++編譯器是如何實現函數模板機制的?
- 編譯器並不是把函數模板處理成能夠處理任何類型的函數
- 函數模板通過具體類型產生不同的函數
- 編譯器會對函數模板進行兩次編譯,在聲明的地方對模板代碼本身進行編譯,在調用的地方對參數替換後的代碼進行編譯。
模板也具有局限性
點擊查看代碼
template<class T>
void mycompare(T a, T b)
{
if (a > b)
{
cout << "a>b" << endl;
}
else
{
cout << "a<=b" << endl;
}
}
void test01()
{
//如果傳入的是數組名,那麼函數模板中比較函數名的大小就沒有意義
int arr[20];
int arr2[10];
mycompare(arr, arr2);
}
為解決上述問題,提出了兩種方法
第一種:使用函數模板具體化
點擊查看代碼
class Maker
{
public:
Maker(string name, int age)
{
this->name = name;
this->age = age;
}
public:
string name;
int age;
};
template<class T>
void myfunc(T& a, T& b)
{
if (a > b)
{
cout << "a>b" << endl;
}
else
{
cout << "a<=b" << endl;
}
}
//具體化函數模板,註意上面的函數模板要有,才能具體化
template<>void myfunc<Maker>(Maker& a, Maker& b)
{
cout << "函數模板的具體化" << endl;
if (a.age > b.age)
{
cout << "a>b" << endl;
}
else
{
cout << "a<=b" << endl;
}
}
第二種:重載操作符
點擊查看代碼
class Maker1
{
public:
Maker1(string name, int age)
{
this->name = name;
this->age = age;
}
public:
string name;
int age;
};
bool operator>(Maker1& m1, Maker1& m2)
{
if (m1.name > m2.name && m1.age > m2.age)
{
return true;
}
else
{
return false;
}
}
void test()
{
Maker1 a("aaa", 18);
Maker1 b("bbb", 20);
if (a > b)
{
cout << "a>b" << endl;
}
else
{
cout << "a<=b" << endl;
}
}
定義類模板
點擊查看代碼
//類模板是把類中的數據類型參數化
template<class NameType,class AgeType>
class Maker
{
public:
Maker(NameType name, AgeType age)
{
this->name = name;
this->age = age;
}
void printMaker()
{
cout << "Name:" << this->name << "Age:" << this->age << endl;
}
public:
NameType name;
AgeType age;
};
類模板的使用
點擊查看代碼
template<class NameType,class AgeType>
class Maker
{
public:
Maker(NameType name, AgeType age)
{
this->name = name;
this->age = age;
}
void printMaker()
{
cout << "Name:" << this->name << "Age:" << this->age << endl;
}
public:
NameType name;
AgeType age;
};
//類模板的使用
void test()
{
//1.類模版不會自動推導數據類型,要顯示的告訴編譯器是什麼類型
Maker<string, int> m("高啟強", 35);
m.printMaker();
//2.註意傳入的參數,傳入參數類型要程式員自己把握
Maker<int, int>m2(18, 20);
m2.printMaker();
//Maker<>m3("aaa", 18); //err:類模板“Maker"的參數太少,必須通過參數列表告訴編譯器是什麼類型
}
類模板和函數模板的區別
- 類模板不會自動推導數據類型,要顯示的告訴編譯器是什麼類型
- 函數模板可以根據實參來推導數據類型
類模板的預設類型及其使用
點擊查看代碼
template<class NameType, class AgeType=int>
class Maker2
{
public:
Maker2(NameType name, AgeType age)
{
this->name = name;
this->age = age;
}
void printMaker()
{
cout << "Name:" << this->name << "Age:" << this->age << endl;
}
public:
NameType name;
AgeType age;
};
void test02()
{
//如果有預設類型,那麼<>里可以少寫類型
Maker2<string> m("高啟強", 35);
//Maker2<string, int> m("高啟蘭", 20);
m.printMaker();
//以傳入的類型為準
Maker2<string, double> m2("高啟盛", 24.22);
m2.printMaker();
}
//5、類模板的預設參數註意
//預設類型後面的泛型類型都必須有預設類型
template<class NameType, class AgeType = int, class T = int>
class Maker3
{
public:
Maker3(NameType name, AgeType age)
{
this->name = name;
this->age = age;
}
void printMaker()
{
cout << "Name:" << this->name << " Age:" << this->age << endl;
}
public:
NameType name;
AgeType age;
};
void test03()
{
Maker3<string> m("高啟強", 35);
m.printMaker();
}
類模板做函數參數
點擊查看代碼
template<class NameType, class AgeType>
class Maker
{
public:
Maker(NameType name, AgeType age)
{
this->name = name;
this->age = age;
}
void printMaker()
{
cout << "name = " << this->name << " " << " age = " << this->age << endl;
}
public:
NameType name;
AgeType age;
};
//類模板做函數參數
//1.指定傳入的數據類型
void func(Maker<string, int>& m)
{
m.printMaker();
}
//2.參數模版化(常用)
template<class T1, class T2>
void func2(Maker<T1, T2>& m)
{
m.printMaker();
}
//3.整個類 模版化
template<class T>
void func3(T& m)
{
m.printMaker();
}
類模板的繼承
點擊查看代碼
//1、普通類繼承類模板
template<class T>
class Father
{
public:
Father()
{
m = 20;
}
public:
T m;
};
//普通類 繼承 類模板
class Son : public Father<int> //2、要告訴編譯器父類的泛型數據類型具體是什麼類型
{
public:
};
//2、類模板 繼承 類模板
//類模板 繼承 類模板
template<class T1,class T2>
class Son2 :public Father<T2> //要告訴編譯器父類的泛型數據類型具體是什麼類型
{
};
類模板的成員函數的類內實現
點擊查看代碼
template<class NameType,class AgeType>
class Maker
{
public:
Maker(NameType name, AgeType age)
{
this->name = name;
this->age = age;
}
void printMaker()
{
cout << "Name:" << this->name << " " << "Age:" << this->age << endl;
}
public:
NameType name;
AgeType age;
};
類模板的成員函數類外實現
點擊查看代碼
template<class NameType,class AgeType>
class Maker
{
public:
Maker(NameType name, AgeType age);
void printMaker();
public:
NameType name;
AgeType age;
};
//類模板的成員函數類外實現
//成員函數必須寫成函數模板,並且寫上參數列表
template<class NameType,class AgeType>
Maker<NameType,AgeType>::Maker(NameType name, AgeType age)
{
cout << "構造函數" << endl;
this->name = name;
this->age = age;
}
template<class NameType,class AgeType>
void Maker<NameType, AgeType>::printMaker()
{
cout << "Name:" << this->name << " " << "Age:" << this->age << endl;
}
類模板的友元實現
類內實現
點擊查看代碼
template<class NameType,class AgeType>
class Maker
{
friend void printMaker(Maker<NameType, AgeType>& p)
{
cout << "類內實現" <<" "<< p.name << " " << p.age << endl;
}
public:
Maker(NameType name, AgeType age)
{
this->name = name;
this->age = age;
}
private:
NameType name;
AgeType age;
};
類外實現
點擊查看代碼
//1、聲明模板
template<class NameType,class AgeType>
class Maker2;
//2、聲明函數模板
//告訴編譯器下麵有printMaker2的實現
template<class NameType,class AgeType>
void printMaker2(Maker2<NameType, AgeType>& p);
template<class NameType, class AgeType>
class Maker2
{
//3、在函數名和()之間加上<>
friend void printMaker2<>(Maker2<NameType, AgeType>& p);
//編譯器不知道printMaker2下麵有沒有實現,需要知道函數的結構
public:
Maker2(NameType name, AgeType age)
{
this->name = name;
this->age = age;
}
private:
NameType name;
AgeType age;
};
//友元在類外實現要寫成函數模板
template<class NameType,class AgeType>
void printMaker2(Maker2<NameType, AgeType>& p)
{
cout << "類外實現的友元函數" << " "<<p.name << " " << p.age << endl;
}
類模板實現數組
MyArray.hpp
點擊查看代碼
#pragma once
template<class T>
class MyArray
{
public:
MyArray(int capacity)
{
this->mCapacity = capacity;
this->mSize = 0;
//T如果是Maker,這裡要調用什麼構造函數,要調用無參構造
p = new T[this->mCapacity];
}
//拷貝構造
MyArray(const MyArray &arr)
{
this->mCapacity = arr.mCapacity;
this->mSize = arr.mSize;
p = new T[arr.mCapacity];
for (int i = 0; i < this->mSize; i++)
{
p[i] = arr.p[i];
}
}
//賦值函數
MyArray& operator=(const MyArray& arr)
{
if (this->p != NULL)
{
delete[] this->p;
this->p = NULL;
}
p = new T[arr.mCapacity];
this->mSize = arr.mSize;
this->mCapacity = arr.mCapacity;
for (int i = 0; i < this->mSize; i++)
{
p[i] = arr.p[i];
}
return *this;
}
//重載[]
T& operator[](int index)
{
return this->p[index];
}
//尾插
void Push_Back(const T& val)
{
if (this->mSize == this->mCapacity)
{
return;
}
this->p[this->mSize] = val;
this->mSize++;
}
//尾刪
void Pop_Back()
{
if (this->mSize == 0)
{
return;
}
this->mSize--;
}
//析構
~MyArray()
{
if (this->p != NULL)
{
delete[] p;
p = NULL;
}
cout << "析構函數" << endl;
}
int getSize()
{
return this->mSize;
}
private:
T* p;
int mCapacity;
int mSize;
};
點擊查看代碼
#define _CRT_SECURE_NO_WARNINGS
#include<string>
#include<iostream>
using namespace std;
#include"MyArray.hpp"
class Maker
{
public:
Maker()
{
cout << "無參構造" << endl;
}
Maker(string name,int age)
{
this->name = name;
this->age = age;
}
public:
string name;
int age;
};
void printMaker(MyArray<Maker>& arr)
{
for (int i = 0; i < arr.getSize(); i++)
{
cout << "姓名:" << arr[i].name << " 年齡:" << arr[i].age << endl;
}
}
void test()
{
//Maker類型
MyArray<Maker>myarr(4);
Maker m1("小明", 18);
Maker m2("小強", 19);
Maker m3("小棟", 20);
Maker m4("小興", 21);
myarr.Push_Back(m1);
myarr.Push_Back(m2);
myarr.Push_Back(m3);
myarr.Push_Back(m4);
printMaker(myarr);
//int類型
MyArray<int>myint(10);
for (int i = 0; i < 10; i++)
{
myint.Push_Back(i + 1);
}
for (int i = 0; i < 10; i++)
{
cout << myint[i] << " " << endl;
}
}
int main()
{
test();
system("pause");
return EXIT_SUCCESS;
}
總結
模板是一個比較重要的概念,是創建泛型類或函數的藍圖或公式。庫容器,比如迭代器和演算法,都是泛型編程的例子,它們都使用了模板的概念。
-學習C++任重而道遠,本人愚鈍,唯有勤加練習方能查漏補缺。