hi,欢迎访问本站!
当前位置: 首页学习笔记正文

【C++】函数模板&类模板

用户投稿 学习笔记 37阅读

目录

 

模板的概念:

函数模板:

函数模板定义:

函数模板的注意事项:

1.不能不指定模板的通用数据类型

2.函数模板中的通用数据类型要唯一

3.函数模板的隐式转换问题

4.函数模板和普通函数的调用规则

5.函数模板的局限

类模板:

类模板定义:

类模板的注意事项:

1.类模板不能选择使用让编译器自动推导数据类型的方式

2.类模板的参数列表可以有默认参数......

3.函数模板成员函数的创建时机

4.类模板的对象作为形参

5.类模板的继承问题:

6.函数模板中的成员函数类外实现问题:

7.类模板分文件编写问题:

8.类模板与全局函数做友元:


模板的概念:

C++除了面向对象编程的编程思想,还有一种编程思想就是泛型编程,泛型编程的具体例子就是模板。所谓模板,就是一个通用的类型,我们可以向模板传递不同的数据类型,从而得到不同的具体类型。C++中提供的模板有函数模板和类模板;

函数模板: 函数模板定义:

函数模板的作用就是将函数的数据类型参数化,什么意思,就是我们平常写的函数都是具体有形参类型,返回类型,里面具体数据类型的。类型参数化可以在我们写函数时将这些数据类型变成未知的变量,从而得到一个函数模板,我们可以根据传入不同的数据类型得到不同的结果和实现。

函数模板的语法:

template<typename T>

上面的黑色加粗体template就是语法关键字,表示要声明一个模板;而黑色加粗体typename语法关键字表示后面紧跟一个虚拟数据类型;绿色的粗体就是一个通用类型。所以我们可以写一个模板函数 :

template<typename T>void Swap(T &a, T &b){T tmp;tmp = a;a = b;b = tmp;}

这个函数的两个形参是一个通用数据类型T,我们可以根据传进去的数据不同得到不同的实现。例如我们如果将T定义为char类型,那么这个函数就变成交换两个char类型;如果我们将T定义为int类型,那么这个函数的实现就变成两个int交换。

那么我们怎么利用这个模板得到一个具体的函数呢?下面有两种具体定义一个函数的方法:

1.让编译器自动类型推导:

    Swap(a, b);

上面表示让编译器根据我们传进去的数据自己推导T应该成为什么数据类型。

2.显式指定数据类型:

   Swap<int>(a, b);

 上面表示显式指定数据类型,我们可以在实参列表前加上声明指定T的数据类型。

函数模板的注意事项: 1.不能不指定模板的通用数据类型

就是模板中的通用数据类型T,我们在调用模板时一定要显式说明T的数据类型,即使我们不要用到T,不然编译器会报错。

template<typename T>void Swap(){;}int main(){//Swap();//错误Swap<char>();return 0;} 2.函数模板中的通用数据类型要唯一

即我们不能模板传递不同的数据类型,这样编译器会不知道选择哪一种数据类型。

template<typename T>void func(T& a, T& b){;}int main(){char a = 'a';int b = 20;func(a,b);return 0;} 3.函数模板的隐式转换问题

我们知道,在普通函数中,如果我们传递实参与形参不同,编译器可能会自动帮我们发生隐式的类型转换。而在函数模板中,如果我们使用的不是显式数据指定的方式创建函数模板,那么编译器会不知道T的具体数据类型,所以不会发生隐式的数据转换。而如果我们使用的是显式地函数模板,那么编译器也可能会自动帮我们进行隐式数据转换。

4.函数模板和普通函数的调用规则

规则1:函数模板和普通函数都可以调用,优先调用普通函数;

规则2:我们可以加上空的模板参数列表强制调用函数模板,即函数调用时后面加上尖括号:

func < >(...);

规则3:函数模板可以发生重载,即不同模板函数名可以一样;

规则4:如果有更好的匹配,编译器也会根据更好匹配优先调用函数模板; 

void func(int a){cout << "普通函数" << endl;}template<typename T>void func(T a){cout << "函数模板" << endl;}template<typename T>void func(T a, T b){cout<<"函数模板2" << endl;}int main(){func(100);//调用普通函数,规则1func<>(100);//调用函数模板,规则2func(100, 200);//传进去两个数据,优先匹配调用函数模板2,模板函数发生重载,规则3,4return 0;} 5.函数模板的局限

函数模板也不是万能的,对于一些不可以比较的数据,函数模板也无法进行对比。例如如果传进去两个不同的类,那么函数模板就无法对比了。

当然,为了解决这个问题,我们也可以有两种方法:

方法一:具体实例化一个对比函数,明确告知里面的对比方式和数据类型,它的语法是:

template<> 返回值类型+函数名+(参数列表) 

注意,参数列表里面的参数需要指定确定的数据类型!

class Info{public:Info(int a, char b){this->a = a;this->b = b;}int a;char b;};template<typename T>bool Compare(T a, T b){if (a == b)return true;elsereturn false;}template<> bool Compare(Info a, Info b)//具体化一个函数模板!{if (a.a == b.a && a.b == b.b)return true;elsereturn false;}int main(){Info A(2,'a');Info B(2,'a');if (!Compare(A, B)){cout << "不相等" << endl;}elsecout<<"相等" << endl;return 0;}

方法二:重载“==”运算符:

class Info{public:Info(int a, int b){this->a = a;this->b = b;}bool operator== (Info &A){if (A.a == this->a && A.b == this->b)return true;elsereturn false;}int a;int b;};template<typename T>bool Compare(T a, T b){if (a == b)return true;elsereturn false;}int main(){Info A(2, 1);Info B(2, 1);if (!Compare(A, B))cout << "不相等" << endl;elsecout<<"相等" << endl;return 0;} 类模板: 类模板定义:

类模板与函数模板的作用类似,类模板可以将类中的成员属性的类型设置为通用数据类型。以此可以让类应用更加广泛。

下面是一个简单的类模板的创建:

template<class G,class T>class Info{public:Info(T a, G b){this->a = a;this->b = b;}T a;G b;};int main(){Info<int, int> A(2,3);return 0;} 类模板的注意事项: 1.类模板不能选择使用让编译器自动推导数据类型的方式 2.类模板的参数列表可以有默认参数...... //template<class T,class G=char>//模板的参数列表可以有默认参数,即可以提早定义一个数据类型....template<class T, class G>class Info{public:Info(T a,G b){this->m_a = a;this->m_b = b;}T m_a;G m_b;};int main(){//Info A(2, 5);//不能让编译器自动推导数据类型,只能够显式声明模板的数据类型Info<int, int> A(2,5);return 0;} 3.函数模板成员函数的创建时机

函数模板中的成员函数只会在调用模板时创建,它不同于普通的类,一个普通的类会在创建类时就创建成员函数。所以我们可以利用这个特性,大大扩展模板函数的应用范围。

class C1{public:int mem1=2;};class C2{public:int mem2=1;};template<typename T>class Info{public:T m_class;int func1(){return m_class.mem1;}int func2(){m_class.mem2;}};int main(){Info<C1>A;cout << A.func1() << endl;return 0;} 4.类模板的对象作为形参

我们往往有时候会将用类模板创建出来的类对象传递到某个函数中,但是函数的形参应该写为什么数据类型呢?因为我们的对象是用类模板创建的,所以我们不得不考虑这个问题,为了解决这个问题,C++支持函数形参的数据类型有三种接收的方法:

我们先创建一个类模板并且写一个对象:

template<class T>class Info{public:Info(){a = 20;}void Show(){cout<<this->a << endl;}T a;};int main(){Info<int>A;return 0;}

1.我们可以将形参指定具体数据类型:

void print01(Info<int> A){A.Show();}

2.我们可以将函数形参的模板类里的数据类型也模板化:

template<class T>void print02(Info<T> A){A.Show();}

3.我们可以直接将整个形参的数据类型模板化:

template<class T>void print03(T A){A.Show();} 5.类模板的继承问题:

首先我们创建一个父类模板,要是我们想要再让一个子类去继承这个父类,我们就要注意对于父类模板中的通用数据类型的处理问题:

1.子类继承时直接指明父类模板中通用数据类型的具体类型

template<typename T>class Fath{public:T a;};class Son2 :public Fath<int>{};

2.子类也变成一个类模板

template<typename T>class Fath{public:T a;};template<class T,class G>class Son:public Fath<T>{public:G b;};

!!!查看数据类型的方法:

头文件:<typeinfo>

语法:typeid(数据类型).name();

cout<<"char的数据类型:" << typeid(char).name() << endl;cout <<"int的数据类型:" << typeid(int).name() << endl; 6.函数模板中的成员函数类外实现问题:

我们知道,普通的类成员函数要在类外实现,只需要在类内写个函数声明,并且在类外实现函数时加上作用域即可。而类模板的成员函数类外实现不同,它需要在作用域的 “ :: ”前加上参数列表。

template<class T>class Info{public:void func();//{//cout<<"this is Info" << endl;//}};template<class T>//类外实现void Info<T>::func(){cout << "this is Info" << endl;}int main(){Info<int> A;A.func();return 0;} 7.类模板分文件编写问题:

因为我们都知道,类模板的成员函数在具体实例化一个对象时创建。而如果我们将一个类模板的函数声明放在一个头文件,而将成员函数的类外实现放在另一个源文件中,这样会带来问题。因为此时类模板的成员函数没有创建。

主函数文件:

 类模板头文件:

模板成员函数实现源文件:

 而这样运行会带来问题:

 就是因为类模板里面的成员函数没有创建。而解决这个问题,有两种方法:

方法1:直接将类模板和类模板里成员函数的实现放在同一个头文件中,并且将这个头文件后缀改为为.hpp。这是一个约定俗成的习惯,说明里面是个类模板。

方法二:直接包含类模板成员函数的源文件。因为类模板无法找到创建成员函数,所以直接包含源文件。

8.类模板与全局函数做友元:

全局函数在类模板中做友元,全局函数的实现可以在类模板中或者类模板外部。

1.全局函数实现在类模板中:

template<class T>class Info{public:friend void func(Info A){cout<<A.m_name<<endl;}Info(T a){m_name = a;}private:T m_name;};int main(){Info<string> A("梁");func(A);return 0;}

2.全局函数实现在类模板外:

全局函数做友元类外实现的几点注意事项:

1.类内全局函数声明要在函数名后加“ < > ”表面这个是个函数模板;

2.函数模板实现要在类创造前;

3.因为全局友元函数创建在类之前,而我们传进去了类模板。所以要提早声明类模板的存在;

4.全局函数不是类模板作用于下,不用加作用域;

template<class T>class Info;template<class T>void func(Info<T> A){cout<<A.m_name<<endl;}template<class T>class Info{public:friend void func<>(Info A);Info(T a){m_name = a;}private:T m_name;};int main(){Info<string> A("梁");func(A);return 0;}

标签:
声明:无特别说明,转载请标明本文来源!
发布评论
正文 取消