目錄
一、類模板
1.模板
2.類模板的作用
3.語法
4.聲明
二、類模板和函數(shù)模板的區(qū)別
三、類模板中成員函數(shù)的創(chuàng)建時(shí)機(jī)
四、類模板對象做函數(shù)參數(shù)
五、類模板與繼承
六、類模板成員函數(shù)類外實(shí)現(xiàn)
七、類模板分文件編寫
八、類模板與友元
九、類模板案例
一、類模板
1.模板
模板是C++支持參數(shù)化多態(tài)的工具,使用模板可以使用戶為類或者函數(shù)聲明一種一般模式,使得類中的某些數(shù)據(jù)成員或者成員函數(shù)的參數(shù)、返回值取得任意類型。
模板是一種對類型進(jìn)行參數(shù)化的工具;
通常有兩種形式:函數(shù)模板和類模板;
函數(shù)模板針對僅參數(shù)類型不同的函數(shù);
類模板針對僅數(shù)據(jù)成員和成員函數(shù)類型不同的類。
使用模板的目的就是能夠讓程序員編寫與類型無關(guān)的代碼。比如編寫了一個(gè)交換兩個(gè)整型int 類型的swap函數(shù),這個(gè)函數(shù)就只能實(shí)現(xiàn)int 型,對double,字符這些類型無法實(shí)現(xiàn),要實(shí)現(xiàn)這些類型的交換就要重新編寫另一個(gè)swap函數(shù)。使用模板的目的就是要讓這程序的實(shí)現(xiàn)與類型無關(guān),比如一個(gè)swap模板函數(shù),即可以實(shí)現(xiàn)int 型,又可以實(shí)現(xiàn)double型的交換。模板可以應(yīng)用于函數(shù)和類。
注意:模板的聲明或定義只能在全局,命名空間或類范圍內(nèi)進(jìn)行。即不能在局部范圍,函數(shù)內(nèi)進(jìn)行,比如不能在main函數(shù)中聲明或定義一個(gè)模板。
2.類模板的作用
建立一個(gè)通用類,類中的成員? 數(shù)據(jù)類型可以不具體制定,用一個(gè)虛擬的類型來代表
3.語法
template <typename? T>
類
4.聲明
template? -- 聲明創(chuàng)建模板
typename? --? 表明其后的符號是一種數(shù)據(jù)類型,可以用clss代替
T????? --? 通用的數(shù)據(jù)類型,名稱可以替換,通常為大寫字母
示例
#include<iostream>
using namespace std;
// 類模板
template<class name_type,class age_type>
class person
{
public:
person(name_type name,age_type age)
{
this->name=name;
this->age=age;
}
void show()
{
cout<<"姓名:"<<this->name<<"\t年齡:"<<this->age<<endl;
}
?
name_type name;
age_type age;
};
void test01()
{
// 類型參數(shù)化
person<string,int> p1("Ton",89);
p1.show();
}
int main()
{
test01();
return 0;
}
運(yùn)行結(jié)果:
總結(jié):類模板和函教模板語法相似。在聲明模板template后面加類,此類稱為類模板
二、類模板和函數(shù)模板的區(qū)別
區(qū)別:
- 類模板沒有自動(dòng)類型推導(dǎo)的使用方式
- 類模板在模板參數(shù)列表中可以有默認(rèn)參數(shù)
示例:
#include<iostream>
using namespace std;
// 類模板
template<class name_type,class age_type? = int> // 可以指定某一個(gè)通用類型具體的類型
class person
{
public:
person(name_type name,age_type age)
{
this->name=name;
this->age=age;
}
void show()
{
cout<<"姓名:"<<this->name<<"\t年齡:"<<this->age<<endl;
}
?
name_type name;
age_type age;
};
void test01()
{
// 自動(dòng)類型推導(dǎo)不行
// person p1("TON",23);
// 只能用指定類型
person<string,int> p1("Ton",89);
p1.show();
}
void test02()
{
// 類模板在模板參數(shù)列表中可以有默認(rèn)值
person<string> p2("Jack",90);
p2.show();
}
int main()
{
test01();
test02();
return 0;
}
運(yùn)行結(jié)果:
三、類模板中成員函數(shù)的創(chuàng)建時(shí)機(jī)
類模板中成員函數(shù)和普通類中成員函數(shù)創(chuàng)建時(shí)機(jī)的區(qū)別:
- 普通類中的成員函數(shù)一開始就可以創(chuàng)建
- 類模板中的成員函數(shù)是在調(diào)用的時(shí)候才創(chuàng)建的
示例:
#include<iostream>
using namespace std;
class person1
{
public:
void showperson1()
{
cout<<"person1的調(diào)用"<<endl;
}
};
class person2
{
public:
void showperson2()
{
cout<<"person2的調(diào)用"<<endl;
}
};
template<class T>
class my_class
{
public:
T obj;
?
// 類模板中的成員函數(shù)
// ??在運(yùn)行前都不會創(chuàng)建這兩個(gè)成員函數(shù)
void func1()
{
obj.showperson1();
}
?
void func2()
{
obj.showperson2();
}
};
?
void test01()
{
my_class<person1> m;
m.func1();
//m.func2();? 運(yùn)行出錯(cuò),說明函數(shù)調(diào)用才會去創(chuàng)建成員函數(shù)
my_class<person2> m1;
m1.func2();
}
int main()
{
test01();
return 0;
}
運(yùn)行結(jié)果:
四、類模板對象做函數(shù)參數(shù)
類模板實(shí)例化出的對象,向函數(shù)傳參的方式
三種傳入方式:
- 指定傳入的類型 --- 直接顯示對象的數(shù)據(jù)類型(**常用)**
- 參數(shù)模板化???????? --- 將對象中的參數(shù)變?yōu)槟0暹M(jìn)行傳遞
- 整個(gè)類模板化???? --- 將這個(gè)對象類型?? 模板化進(jìn)行傳遞
示例:
#include<iostream>
#include<typeinfo>
using namespace std;
/*
三種傳入方式:
?
1. 指定傳入的類型 --- 直接顯示對象的數(shù)據(jù)類型
2. 參數(shù)模板化???????? --- 將對象中的參數(shù)變?yōu)槟0暹M(jìn)行傳遞
3. 整個(gè)類模板化???? --- 將這個(gè)對象類型?? 模板化進(jìn)行傳遞
*/
template<class T1,class T2>
class person
{
public:
person(T1 name, T2 age)
{
this->name = name;
this->age = age;
}
void show()
{
cout<<"姓名:"<<this->name<<"\t年齡:"<<this->age<<endl;
}
T1 name;
T2 age;
};
?
// 1.指定傳入類型
void print1(person<string ,int> &p)
{
p.show();
}
void test01()
{
// 1. 指定傳入的類型 --- 直接顯示對象的數(shù)據(jù)類型
person<string ,int> p ("TOM",99);
print1(p);
}
?
// 2.參數(shù)模板化
template<class T1,class T2>
void print2( person<T1,T2> &p1)
{
p1.show();
cout<<"看編譯器推斷的模板是什么類型"<<endl;
cout<<"T1的類型為:"<<typeid(T1).name()<<endl;
cout<<"T2的類型為:"<<typeid(T2).name()<<endl;
}
void test02()
{
// 2. 參數(shù)模板化???????? --- 將對象中的參數(shù)變?yōu)槟0暹M(jìn)行傳遞
person <string ,int> p1 ("JACK",78);
print2(p1);
}
?
// 3.將整個(gè)類模板化
template<class T>
void print3(T &p2)
{
p2.show();
cout<<"T的數(shù)據(jù)類型"<<endl;
cout<<"T的類型為:"<<typeid(T).name()<<endl;
}
void test03()
{
person<string,int> p2("LILY",45);
print3(p2);
}
int main()
{
test01();
test02();
test03();
return 0;
}
運(yùn)行結(jié)果:
總結(jié):
- 通過類模板創(chuàng)建的對象??梢杂腥N方式向函數(shù)中進(jìn)行傳參
- 使用比較廣泛是第一種:指定傳入的類型
五、類模板與繼承
注意:
- 當(dāng)子類繼承父類是一個(gè)類模板時(shí),子類在聲明的時(shí)候,要指定出父類T的類型
- 如果不能確定,編譯器無法給予子類分配內(nèi)存
- 如果想靈活指定父類中T的類型,子類也需要變?yōu)轭惸0?/li>
示例1:
#include<iostream>
#include<typeinfo>
using namespace std;
// 類模板與繼承
template<class T>
class Base
{
T m;
};
// class Son:public Base? // 必須要知道父類中T的類型,才能繼承給子類
class Son:public Base<int>
{
?
};
int main()
{
Son s1;
return 0;
}
生成成功:
示例2:
#include<iostream>
#include<typeinfo>
using namespace std;
template<class T>
class Base
{
T m;
};
// 如果想要靈活指定父類中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 ojb;
};
void test02()
{
??? Son2<int ,char>s2;// char 傳給父類,int 傳給子類
}
int main()
{
test02();
return 0;
}
運(yùn)行結(jié)果:
六、類模板成員函數(shù)類外實(shí)現(xiàn)
示例:
#include<iostream>
using namespace std;
?
// 類模板成員函數(shù)類外實(shí)現(xiàn)
template<class T1,class T2>
class person
{
public:
person(T1 name,T2 age);
/*{
this->name=name;
this->age=age;
}*/
void show();
/*{
cout<<"姓名:"<<this->name<<"\t年齡:"<<this->age<<endl;
}*/
??? T1 name;
??? T2 age;
};
// 構(gòu)造函數(shù)的類外實(shí)現(xiàn)
template<class T1,class T2>
person<T1,T2>::person(T1 name,T2 age)
{
this->name=name;
this->age=age;
}
?
// 成員函數(shù)的類外實(shí)現(xiàn)
template<class T1,class T2>
void person<T1,T2>::show()
{
cout<<"姓名:"<<this->name<<"\t年齡:"<<this->age<<endl;
}
void test01()
{
person<string,int>p("TOM",28);
p.show();
}
int main()
{
test01();
return 0;
}
運(yùn)行結(jié)果:
七、類模板分文件編寫
問題:
類模板中成員函數(shù)創(chuàng)建的時(shí)機(jī)是在調(diào)用階段,導(dǎo)致分文件編寫時(shí)連接不到
解決
- 直接包含 .cpp 源文件
- 將聲明和實(shí)現(xiàn)寫到同一個(gè)文件中,并更改后綴名為 .hpp , hpp 是約定的名稱,并不是強(qiáng)制
示例:
person.hpp文件
#include<iostream>
using namespace std;
// 類模板成員函數(shù)類外實(shí)現(xiàn)
template<class T1, class T2>
class person
{
public:
person(T1 name, T2 age);
T1 name;
T2 age;
};
// 構(gòu)造函數(shù)的類外實(shí)現(xiàn)
template<class T1, class T2>
person<T1, T2>::person(T1 name, T2 age)
{
this->name = name;
this->age = age;
}
// 成員函數(shù)的類外實(shí)現(xiàn)
template<class T1, class T2>
void person<T1, T2>::show()
{
cout << "姓名:" << this->name << "\t年齡:" << this->age << endl;
}
.cpp文件
#include<iostream>
#include"person.hpp"
using namespace std;
void test01()
{
person<string, int>p("TOM", 28);
p.show();
}
int main()
{
test01();
return 0;
}
八、類模板與友元
類模板配合友元函數(shù)的類內(nèi)實(shí)現(xiàn)和類外實(shí)現(xiàn)
- 全局函數(shù)類內(nèi)實(shí)現(xiàn) -- 直接在類內(nèi)聲明友元即可
- 全局函數(shù)類外實(shí)現(xiàn) -- 需要提前讓編譯器知道全局函數(shù)的存在
示例:
#include<iostream>
using namespace std;
?
template<class T1,class T2>
class person;
?
// 類外實(shí)現(xiàn)
template<class T1,class T2>
void print2(person<T1,T2> p)
{
cout<<"類外實(shí)現(xiàn)---姓名:"<<p.name<<"\t年齡:"<<p.age<<endl;
}
?
// 類模板與友元
template<class T1,class T2>
class person
{
?
// 通過全局函數(shù)打印輸出
// 全局函數(shù)類內(nèi)實(shí)現(xiàn)
friend void print1(person<T1,T2> p)
{
cout<<"姓名:"<<p.name<<"\t年齡:"<<p.age<<endl;
}
// 全局函數(shù)類外實(shí)現(xiàn)
// 需要加空模板的參數(shù)列表
// 如果全局函數(shù)是類外實(shí)現(xiàn),需要讓編譯器提前知道這個(gè)函數(shù)的存在
friend void print2<>(person<T1,T2> p);
public:
person(T1 name,T2 age)
{
this->name=name;
this->age=age;
}
?
private:
??? T1 name;
??? T2 age;
};
?
?
void test01()
{
person<string,int>p("TOM",28);
print1(p);
print2(p);
}
int main()
{
test01();
return 0;
}
運(yùn)行結(jié)果:
九、類模板案例
目的:
- 可以對內(nèi)置數(shù)據(jù)類型以及自定義數(shù)據(jù)類型的數(shù)據(jù)進(jìn)行存儲
- 將數(shù)組中的數(shù)據(jù)存儲到堆區(qū)
- 構(gòu)造函數(shù)中可以傳入數(shù)組的容量
- 提供對應(yīng)的拷貝構(gòu)造函數(shù)以及operator=防止淺拷貝問題
- 提供尾插法和尾刪除法對數(shù)組中的數(shù)據(jù)進(jìn)行增加和刪除
- 可以通過下標(biāo)的方式訪問數(shù)組中的元素
- 可以獲取數(shù)組中當(dāng)前元素的個(gè)數(shù)和數(shù)組的容量
my_array.hpp
#pragma once
#include<iostream>
using namespace std;
// 自己的通用的數(shù)組類
?
template<class T>
class my_array
{
public:
// 有參構(gòu)造, 傳入容量
my_array(int copacity)
{
cout << "my_array的有參構(gòu)造" << endl;
this->m_Capacity = copacity;
this->m_Size = 0;
this->p_Address = new T[this->m_Capacity];
}
?
//拷貝構(gòu)造
my_array(const my_array& arr)
{
this->m_Capacity = arr.m_Capacity;
this->m_Size = arr.m_Size;
// 淺拷貝 this->p_Address = arr. p_Address;
// 深拷貝
this->p_Address = new T[arr.m_Capacity];
?
//? 將arr中的數(shù)據(jù)拷貝進(jìn)來
for (int i = 0; i < this->m_Size; i++)
{
this->p_Address[i] = arr.p_Address[i];
}
}
?
// operator = 防止淺拷貝的問題
my_array& operator=(const my_array& arr)
{
// 先判斷原來堆區(qū)是否有數(shù)據(jù),先釋放堆區(qū)數(shù)據(jù)
if (this->p_Address != NULL)
{
delete[]this->p_Address;
this->p_Address = NULL;
this->m_Capacity = 0;
this->m_Size = 0;
}
this->m_Capacity = arr.m_Capacity;
this->m_Size = arr.m_Size;
this->p_Address = new T[arr.m_Capacity];
for (int i = 0; i < this->m_Size; i++)
{
this->p_Address[i] = arr.p_Address[i];
}
return *this;
}
?
// 尾插法
void Push_Back(const T& Val )
{
// 判斷容量是否最大了
if (this->m_Capacity == this->m_Size)
{
return;
}
this->p_Address[this->m_Size] = Val;? // 在數(shù)組的末尾插入數(shù)據(jù)
this->m_Size++;? // 更新數(shù)組的大小
}
?
// 尾刪法
void Pop_Back()
{
// 讓用戶訪問不到最后一個(gè)元素,就是刪除
if (this->m_Size == 0)
{
return;
}
this->m_Size--;
}
?
// 可以通過下標(biāo)的方式訪問數(shù)組
T& operator[](int index)
{
return this->p_Address[index];
}
?
// 返回?cái)?shù)組的容量
int get_Caoacity()
{
return this->m_Capacity;
}
?
// 返回?cái)?shù)組大小
int get_Size()
{
return this->m_Size;
}
?
// 析構(gòu)函數(shù)
~my_array()
{
if (this->p_Address != NULL)
{
delete[] this->p_Address;
this->p_Address = NULL;
}
}
private:
T* p_Address;?? // 指針指向堆區(qū)開辟的真實(shí)數(shù)據(jù)
?
int m_Capacity;?? // 數(shù)組容量
?
int m_Size;? // 數(shù)組大小
};
模板array.cpp
#include"my_array.hpp"
#include<iostream>
using namespace std;
void print_arr(my_array<int>& arr)
{
for (int i = 0; i < arr.get_Size(); i++)
{
cout << arr[i] << endl;
}
}
void test01()
{
my_array<int> arr1(5);
for (int i = 0; i < 5; i++)
{
// 利用尾插法向數(shù)組中插入數(shù)據(jù)
arr1.Push_Back(i);
}
cout << "arr1的打印輸出" << endl;
print_arr(arr1);
cout << "arr1的容量為:" <<arr1.get_Caoacity()<< endl;
cout << "arr1的大小為:" << arr1.get_Size() << endl;
?
cout << "arr2的打印輸出" << endl;
my_array<int> arr2(arr1);
print_arr(arr2);
// 尾刪
arr2.Pop_Back();
cout << "arr2的容量為:" << arr2.get_Caoacity() << endl;
cout << "arr2的大小為:" << arr2.get_Size() << endl;
}
?
// 測試自定義的數(shù)據(jù)類型
class person
{
public:
person() {};
person(string name,int age)
{
this->name = name;
this->age = age;
}
string name;
int age;
};
?
void print_person_arr(my_array<person>& arr)
{
for (int i = 0; i < arr.get_Size(); i++)
{
cout << "姓名:" << arr[i].name << "\t年齡:" << arr[i].age << endl;
}
}
?
void test02()
{
my_array<person> arr(10);
person p1("TON", 78);
person p2("JEEYU", 678);
person p3("hello", 567);
person p4("bgood", 567);
person p5("daj", 78);
?
// 將數(shù)據(jù)插入到數(shù)組中
arr.Push_Back(p1);
arr.Push_Back(p2);
arr.Push_Back(p3);
arr.Push_Back(p4);
arr.Push_Back(p5);
// 打印數(shù)組
print_person_arr(arr);
// 輸出容量
cout << "arr的容量:" << arr.get_Caoacity() << endl;
// 輸出大小
cout << "arr的大小:" << arr.get_Size() << endl;
}
int main()
{
test01();
test02();
return 0;
}
運(yùn)行結(jié)果:文章來源:http://www.zghlxwxcb.cn/news/detail-822365.html
文章來源地址http://www.zghlxwxcb.cn/news/detail-822365.html
到了這里,關(guān)于C++提高編程---模板---類模板的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!