1、函数模板:所谓函数模板,就是建立一个通用函数,其函数类型和形参类型不具体指定,用一个虚拟的类型来代表,凡是函数体相同的函数都可以用这个模板来代替。
函数模板的出现将算法与数据类型相分离,专注于算法的设计。
1)函数模板的定义是通过关键字 template 来实现的。如:
template <typename T> // 注意末尾没有分号
void test(T a){}
2)调用方式:
test<float>(a, b); // 显示类型调用
test(a, b); // 隐式类型推导
3)函数模板的重载:
普通函数支持的隐式转换模板的数据类型并不支持,必须指明类型;
当普通函数和函数模板都可以被调用时,C++编译器会优先选择普通函数;但此时若模板更优时,还是会选模板的(比如普通函数需要隐式转换时)。
先要强制调用模板的话,可以指定类型(当然,空类型就够了);
4)生成规则:在函数模板声明时,会编译模板本身;而在调用时,会根据传入类型来生成具体函数;
2、类模板:类模板的出现主要是为了实现所需数据的类型参数化。类模板的定义方式和用法都类似函数模板。但在类模板被用来定义对象时,必须指明类型。
Test<int> a;
Test<int> b(3);
1)当模板类对象作函数参数传递时,可以有以下2种方式:
1、指明类型 2、所用函数也写成函数模板
2)类模板的派生类:
1、派生具体类,需要写清楚基类的类型:
class B : public A<int>
{
public:
B(int a, int b) : A<int>(a)
{
this->b = b;
}
protected:
int b;
};
2、派生模板类
template <typename T>
class C : public A<T>
{
public:
C(T c, T a) : A<T>(a)
{
this->c = c;
}
protected:
T c;
};
3)类的实现在类的内部
模板类的友元函数要在类的内部实现(但它还是友元,不能去掉friend);当然,不用再定义为模板函数了。比如这样一个 << 操作符的重载函数:
template <typename T>
class Complex
{
friend ostream &operator<< (ostream &out, Complex<T> &obj)
{
out << obj.a << " + " << obj.b << "i";
return out;
}
private:
T a;
T b;
};
4)类的实现在类的外部
当类的实现是在类的外部时,所有函数都要定义成模板函数。在函数实现时还要加上域解析符,如:
template <typename T>
Test<T>::Test (){}
当友元函数想要在外部实现时,过程十分复杂:
1、进行模板类声明
2、声明友元函数(模板类)
3、在类的内部进行友元函数的声明(友元,非模板),此时需要在函数名后面加上参数类型 <T>,如
friend Complex<T> mySub <T>(Complex<T> &c1, Complex<T> &c2);
4、在类的外部实现(在友元函数内部定义对象时要指定<T>,返回值也需要加上)
5)模板类的声明和实现在不同的文件中时:
在main 函数中需要包含模板类的实现文件;通常这种需要被包含的实现文件,后缀一般要写成.hpp
6)模板类中的static:模板类中的静态成员变量也需要在类的外部初始化
T A<T>::a = 0;
类模板生成的每一个模板类都有自己的静态变量,不同的模板类的静态变量是不共享的;
7)数组的类模板
当自定义类型存放在数组中时,自定义类中需要加上无参构造;
由于数组打印是通过重载左移操作符来实现的,所以需要在自定义类中也要实现左移操作符的重载;
数组内部所做的是值拷贝,默认的赋值方式是浅拷贝,一旦涉及指针的操作就会导致问题,所以需要做赋值操作符= 的运算符重载
另外,在做delete操作时,无参构造中指针需要指向NULL;
3、一定要注意 delete 数组时,要加上[ ] ,真的是很难找啊!!!!!
附上一个数组类模板的头文件:
#ifndef __MYARRAY_H__
#define __MYARRAY_H__
#include <iostream>
using namespace std;
template <typename T>
class MyArray
{
friend ostream& operator<< <T>(ostream & out,MyArray<T> &obj);
public:
MyArray(int len);
MyArray(const MyArray &obj);
~MyArray();
int Getlen();
T & operator[](int index);
MyArray& operator= (const MyArray &obj);
private:
int len;
T *m_p;
};
template <typename T>
MyArray<T>::MyArray(int len)
{
this->len = len;
m_p = new T[len];
//m_p = NULL;
}
template <typename T>
MyArray<T>::MyArray(const MyArray &obj)
{
//len = obj.Getlen(); // Getlen 存在this指针转换
len = obj.len;
m_p = new T[len];
for (int i = 0; i < len; i++)
{
m_p[i] = obj.m_p[i];
}
}
template <typename T>
MyArray<T>::~MyArray()
{
if (m_p != NULL)
{
delete [] m_p; // !!!!!!!!!!!!!!!!!
m_p = NULL;
}
len = 0;
}
template <typename T>
int MyArray<T>::Getlen()
{
return len;
}
template <typename T>
T & MyArray<T>::operator[](int index)
{
return m_p[index];
}
template <typename T>
MyArray<T>& MyArray<T>::operator=(const MyArray &obj)
{
// 1
if (this == &obj)
{
return *this;
}
// 2
if (m_p != NULL)
{
delete [] m_p;
}
// 3
m_p = new T[obj.len];
// 4
len = obj.len;
for (int i = 0; i < len; i++)
{
m_p[i] = obj.m_p[i];
}
return *this;
}
template <typename T>
ostream& operator<<(ostream & out,MyArray<T> &obj)
{
for (int i = 0; i < obj.len; i++)
{
//out << obj.m_p[i];
out << obj.m_p[i] << " ";
}
out << endl;
return out ;
}
#endif //__MYARRAY_H__