Day07 C++模板

来源:互联网 发布:lol 省一 知乎 编辑:程序博客网 时间:2024/06/06 00:13

   1.函数模板的概念

 c++提供了函数模板(function template.)所谓函数模板,实际上是建立一个通用函数,其函数类型和形参类型不具体制定,用一个虚拟的类型来代表。这个通用函数就成为函数模板。

c++提供两种模板机制:函数模板和类模板
类属 - 类型参数化,又称参数模板
总结:
模板把函数或类要处理的数据类型参数化,表现为参数的多态性,成为类属。

模板用于表达逻辑结构相同,但具体数据元素类型不同的数据对象的通用行为。

//class 和 typename都是一样的,用哪个都可以template<class T>void MySwap(T& a,T& b){T temp = a;a = b;b = temp;}void test01(){int a = 10;int b = 20;cout << "a:" << a << " b:" << b << endl;//1. 这里有个需要注意点,函数模板可以自动推导参数的类型MySwap(a,b);cout << "a:" << a << " b:" << b << endl;char c1 = 'a';char c2 = 'b';cout << "c1:" << c1 << " c2:" << c2 << endl;//2. 函数模板可以自动类型推导,那么也可以显式指定类型MySwap<char>(c1, c2);cout << "c1:" << c1 << " c2:" << c2 << endl;}


用模板是为了实现泛型,可以减轻编程的工作量,增强函数的重用性。

2.函数模板实现排序
使用函数模板实现对char和int类型数组进行排序?

//模板打印函数template<class T>void PrintArray(T arr[],int len){for (int i = 0; i < len;i++){cout << arr[i] << " ";}cout << endl;}//模板排序函数template<class T>void MySort(T arr[],int len){for (int i = 0; i < len;i++){for (int j = len - 1; j > i;j--){if (arr[j] > arr[j - 1]){T temp = arr[j - 1];arr[j - 1] = arr[j];arr[j] = temp;}}}}void test(){//char数组char tempChar[] = "aojtifysn";int charLen = strlen(tempChar);//int数组int tempInt[] = {7,4,2,9,8,1};int intLen = sizeof(tempInt) / sizeof(int);//排序前 打印函数PrintArray(tempChar, charLen);PrintArray(tempInt, intLen);//排序MySort(tempChar, charLen);MySort(tempInt, intLen);//排序后打印PrintArray(tempChar, charLen);PrintArray(tempInt, intLen);}


3.函数模板和普通函数的区别、调用规则
#define _CRT_SECURE_NO_WARNINGS#include<iostream>using namespace std;template<class T>T myFunc(T a,T b){return a + b;}int myFunc2(int a,char b){return a + b;}//模板不能直接使用//myFunc<int>(); //具体函数//1. 函数模板和普通函数区别void test01(){int a = 10;int b = 20;char c = 'a';//myFunc2(a, b);//myFunc(a, c);  //先要根据参数进行类型推导,推导出来类型之后,生成函数myFunc<int>(a, c);}//2. 普通函数和函数模板调用规则template<typename T>void func(T a,T b){}template<typename T>void func(T a, T b,T c){}//函数重载void func(int a, int b){}//void func(int a, int b); //告诉编译器func是有的,只不过可能不在当前文件内定义void test02(){int a = 10;int b = 20;//1. 如果函数模板和普通函数都能匹配的话,优先调用普通函数func(a,b);//2. 可以通过空模板参数列表来强制调用函数模板func<>(a, b);//3. 函数模板可以像普通函数那样被重载char c = 'a';char d = 'b';func(c,d);//模板匹配更好//4. 如果函数模板能够产生更好的匹配,那么就调用函数模板}int main(){test02();system("pause");return EXIT_SUCCESS;}
4.函数模板的灵活应用
#define _CRT_SECURE_NO_WARNINGS#include<iostream>#include<string>using namespace std;template<class NameType,class AgeType>class Person{public:Person(NameType name, AgeType age){this->mName = name;this->mAge = age;}NameType getName(){return mName;}AgeType getAge(){return mAge;}void showPerson(){cout << "Name:" << this->mName << " Age:" << this->mAge << endl;}private:NameType mName;AgeType mAge;};void test01(){//类模板不能进行类型自动推导Person<string,int> p("name", 10);cout << p.getName() << endl;cout << p.getAge() << endl;p.showPerson();//Person<string, int, 10> Person<string, int, 11> 两个不同的类型}//当实例化一个类模板的时候,成员函数是没有创建的,当你调用的时候才会创建。struct Person1{void showPerson1(){cout << "Person1::showPerson1" << endl;}};struct Person2{void showPerson2(){cout << "Person1::showPerson1" << endl;}};template<class T>struct myClass{void fun1(){a.showPerson1();}void fun2(){a.showPerson2();}T a;};void test02(){myClass<Person1> m;m.fun1();m.fun2();}int main(){//test01();test02();system("pause");return EXIT_SUCCESS;}

5.类模板做函数做函数参数

#define _CRT_SECURE_NO_WARNINGS#include<iostream>#include<string>using namespace std;template<class T1,class T2>class Person{public:Person(T1 name, T2 age){this->mName = name;this->mAge = age;}void showPerson(){cout << "Name:" << this->mName << " Age:" << this->mAge << endl;}public:T1 mName;T2 mAge;};//类模板做函数参数void doBussiness01(Person<string,int>& person){person.showPerson();}void test01(){Person<string, int>  p("Obama",20);//Person<int, string> p2(10, "AAA"); 和上面不是同一个类型doBussiness01(p);}template<class T1,class T2>void doBussiness02(Person<T1,T2>& person){person.showPerson();cout << typeid(T1).name() << endl;cout << typeid(T2).name() << endl;}void test02(){Person<string, int>  p("Obama", 20);Person<int, string> p2(10, "AAA"); //和上面不是同一个类型//doBussiness02(p);doBussiness02(p2);}template<class T>void doBussiness03(T& person){cout << typeid(T).name() << endl;person.showPerson();}void test03(){Person<string, int>  p("Obama", 20);doBussiness03(p);}int main(){//test01();//test02();test03();system("pause");return EXIT_SUCCESS;}

6.继承类模板
#define _CRT_SECURE_NO_WARNINGS#include<iostream>using namespace std;template<class T>class Base{public:T mA;};//普通类继承类模板//一个普通类为什么不能直接继承一个类模板//c++编译需要给子类分配内存,必须知道基类的大小class Derived : Base<double>{};//类模板继承类模板  继承类模板的时候,必须要确定基类的大小。template<class T,class T2>class Derived2 : public Base<T2>{public:T mParam;};int main(){Derived d;Derived2<int,double> d2;system("pause");return EXIT_SUCCESS;}

7.类模板类外实现

#define _CRT_SECURE_NO_WARNINGS#include<iostream>#include<string>using namespace std;template<class T1, class T2>class Person{public:Person(T1 name, T2 age);void showPerson();public:T1 mName;T2 mAge;};//类外实现template<class T1,class T2>Person<T1,T2>::Person(T1 name, T2 age){this->mName = name;this->mAge = age;}template<class T1,class T2>void Person<T1,T2>::showPerson(){cout << "Name:" << this->mName << " Age:" << this->mAge << endl;}int main(){Person<string, int> p("Obama", 20);p.showPerson();system("pause");return EXIT_SUCCESS;}


8.类模板头文件和源文件分离问题

Person.h

#ifndef PERSON_H#define PERSON_Htemplate<class T>class Person{public:#if 1//重载左移操作符template<class T1> friend ostream& operator<<(ostream& out, Person<T1>& p);//重载普通友元template<class T2> friend void FriendPerson(Person<T2>& p);#endif//构造函数Person(T property);//成员函数void PrintPerson();private:T mProperty;};#endif

Person.cpp

#include "Person.h"template<class T>Person<T>::Person(T property){this->mProperty = property;}template<class T>void Person<T>::PrintPerson(){cout << this->mProperty << endl;}#if 1//重载左移操作符template<class T>ostream& operator<<(ostream& out, Person<T>& p){out << p.mProperty << endl;return out;}//重载普通友元template<class T>void FriendPerson(Person<T>& p){cout << p.mProperty << endl;}#endif

结论: 案例代码在qt编译器顺利通过编译并执行,但是在Linux和vs编辑器下如果只包含头文件,那么会报错链接错误,需要包含cpp文件,但是如果类模板中有友元类,那么编译失败!

解决方案: 

类模板的声明和实现放到一个文件中,我们把这个文件命名为.hpp(这个是个约定的规则,并不是标准,必须这么写).

原因:

类模板需要二次编译,在出现模板的地方编译一次,在调用模板的地方再次编译。

C++编译规则为独立编译。

9.类模板碰到友元函数

#define _CRT_SECURE_NO_WARNINGS#include<iostream>#include<string>using namespace std;template<class T1, class T2> class Person;//告诉编译器这个函数模板是存在template<class T1, class T2> void PrintPerson(Person<T1, T2>& p);template<class T1, class T2> ostream& operator<<(ostream& out, Person<T1, T2>& p);template<class T1, class T2>class Person{//1. 友元函数在类内实现#if 0friend void PrintPerson(Person<T1,T2>& p){cout << "Name:" << p.mName << " Age:" << p.mAge << endl;}friend ostream& operator<<(ostream& out, Person<T1, T2>& p){out << "Name:" << p.mName << " Age:" << p.mAge;return out;}#endif//2. 类模板碰到友元函数//友元函数类外实现  加上<>空参数列表,告诉编译去匹配函数模板//friend void PrintPerson<>(Person<T1, T2>& p);//friend ostream& operator<<<>(ostream& out, Person<T1, T2>& p);//3. 类模板碰到友元函数模板template<class U1,class U2>friend void PrintPerson(Person<U1, U2>& p);template<class U1, class U2>friend ostream& operator<<(ostream& out, Person<U1, U2>& p);public:Person(T1 name, T2 age){this->mName = name;this->mAge = age;}void showPerson(){cout << "Name:" << this->mName << " Age:" << this->mAge << endl;}private:T1 mName;T2 mAge;};void test01(){Person<string, int> p("Obama", 20);//PrintPerson(p);//cout << p << endl;}template<class T1,class T2>void PrintPerson(Person<T1, T2>& p){cout << "Name:" << p.mName << " Age:" << p.mAge << endl;}template<class T1,class T2>ostream& operator<<(ostream& out, Person<T1, T2>& p){out << "Name:" << p.mName << " Age:" << p.mAge;return out;}void test02(){Person<string, int> p("Obama", 20);PrintPerson(p);cout << p << endl;}#if 0template<class T>void myfunc(T a){}void myfunc(int a);void test03(){int a = 10;myfunc(a);}#endifint main(){//test01();test02();//test03();system("pause");return EXIT_SUCCESS;}

10.类模板的应用--数组类模板

MyArray.hpp

#pragma oncetemplate<class T>class MyArray{public:explicit MyArray(int capacity){this->mCapacity = capacity;this->mSize = 0;//如果T为对象类型,对象必须提供默认构造函数this->pAddress = new T[mCapacity];}MyArray(const MyArray& arr){this->mCapacity = arr.mCapacity;this->mSize = arr.mSize;this->pAddress = new T[this->mCapacity];for (int i = 0; i < this->mSize; i ++){//如果T为对象类型,对象包含指针,对象必须提供operator=this->pAddress[i] = arr.pAddress[i];}}//重载[]操作符T& operator[](int index){return this->pAddress[index];}//尾插法void PushBack(T& val){if (this->mSize == this->mCapacity){return;}this->pAddress[this->mSize] = val;this->mSize++;}void PushBack(T&& val){if (this->mSize == this->mCapacity){return;}this->pAddress[this->mSize] = val;this->mSize++;}//提供打印函数void PrintArray(void(*MyPrint)(T&)){for (int i = 0; i < this->mSize;i ++){MyPrint(this->pAddress[i]);}}//尾删法void PopBack(){if (this->mSize == 0){return;}this->mSize--;}//重载赋值操作符MyArray& operator=(const MyArray& arr){if (this->pAddress != NULL){delete[] this->pAddress;this->pAddress = NULL;this->mCapacity = 0;this->mSize = 0;}this->mCapacity = arr.mCapacity;this->mSize = arr.mSize;this->pAddress = new T[this->mCapacity];for (int i = 0; i < this->mSize; i++){//如果T为对象类型,对象包含指针,对象必须提供operator=this->pAddress[i] = arr.pAddress[i];}return *this;}//析构函数~MyArray(){if (this->pAddress != NULL){delete[] this->pAddress;this->pAddress = NULL;this->mCapacity = 0;this->mSize = 0;}}private:T* pAddress; //指向一个堆空间,这个空间存储真正的数据int mCapacity;int mSize;};<strong></strong>

TestArray.cpp

#define _CRT_SECURE_NO_WARNINGS#include<iostream>#include<string>using namespace std;#include"MyArray.hpp"void MyPrint(int& val){cout << val << " ";}//测试存储基础数据类型void test01(){MyArray<int> arr(20);for (int i = 0; i < 19;i ++){arr.PushBack(i + 1);}arr.PushBack(100);//输出for (int i = 0; i < 20; i ++){cout << arr[i] << " ";}cout << endl;arr.PrintArray(MyPrint);cout << endl;}//测试自定义对象class Person{public:Person(){this->pName = NULL;this->mAge = 0;}Person(char* name,int age){this->pName = new char[strlen(name) + 1];strcpy(this->pName, name);this->mAge = age;}Person& operator=(const Person& p){if (this->pName != NULL){delete[] this->pName;this->pName = NULL;}this->pName = new char[strlen(p.pName) + 1];strcpy(this->pName, p.pName);this->mAge = p.mAge;return *this;}~Person(){if (this->pName != NULL){delete[] this->pName;this->pName = NULL;}}public:char* pName;int mAge;};void MyPrintPerson(Person& p){cout << "Name:" << p.pName << " Age:" << p.mAge << endl;}void test02(){MyArray<Person> arr(10);Person p1("aaa", 10);Person p2("bbb", 20);Person p3("ccc", 30);Person p4("ddd", 40);Person p5("eee", 50);arr.PushBack(p1);arr.PushBack(p2);arr.PushBack(p3);arr.PushBack(p4);arr.PushBack(p5);arr.PrintArray(MyPrintPerson);}int main(){//test01();test02();system("pause");return EXIT_SUCCESS;}


0 0
原创粉丝点击