目录
1、泛型编程
首先我们可以考虑一个问题:
如何去实现一个所有类型都适用的交换函数呢?
void Swap ( int & left , int & right ){int temp = left ;left = right ;right = temp ;}void Swap ( double & left , double & right ){double temp = left ;left = right ;right = temp ;}void Swap ( char & left , char & right ){char temp = left ;left = right ;right = temp ;}......
2、函数模板
2.1、概念:
函数模板代表了一个函数家族,该函数模板与类型无关,在使用时被参数化,根据实参类型产生函数特定的类型版本。
2.2、格式
template<typename T1,typename T2,....,typename Tn>
返回值类型 函数名(参数列表) {}
template<typename T>
void Swap(T& left, T& right)
{
T temp = left;
left = right;
right = temp;
}
注意:typename是用来定义模板参数关键字,也可以使用class(切记:不能使用struct代替class)
2.3、函数模板的原理
2.4、函数模板实例化
用不同类型的参数使用函数模板时,称为函数模板的实例化。
模板参数实例化分为:隐式实例化和显式实例化
1、隐式实例化
template<class T>
T Add(const T& left,const T& right)
{
return left +right;
}
int main()
{
int a1 =10, a2 = 20;
double d1 = 10.0,d2 = 20.0;
Add(a1,a2);
Add(d1,d2);
Add(a1,d1); //这样的写法可以吗???
//该语句不能通过编译,因为在编译期间,编译器看到该实例化时,需要推演其实际类型。 实参有两个类型,但是模板参数列表只有一个T,因此编译器不能确定是int还是double
return 0;
}
注意:在模板中,编译器一般不会进行类型转化的操作,因为一旦转化出问题,编译器就要背黑锅。
对于上面语句,有两种处理方式:1、用户自己强制转化 2、使用显式实例化
Add(a, (int)d);
2、显式实例化----在函数名之后加<> 中指定模板参数的实际类型
int main(void)
{
int a = 10;
double b = 20.0;
//显式实例化
Add<int>(a,b);
return 0;
}
2.5、模板参数的匹配原则
2、对于非模板函数和同名函数模板,如果其他条件都相同,在调动时会优先调用非模板函数而不会从该模板产生出一个实例。如果模板可以产生一个具有更好匹配的函数, 那么将选择模板。
3、 模板函数不允许自动类型转换,但普通函数可以进行自动类型转换
3、类模板
3.1、类模板的定义格式
template<class T1,class T2,...,class Tn>
class 类模板名
{
//类内成员定义
};
//动态顺序表
//注意:Vector不是具体的类,是编译器根据被实例化的类型生成具体类的模具
template<class T>
class Vector
{
public:
Vector(size_t capacity = 10)
:_pData(new T[capacity])
,_size(0)
,_capacity(capacity)
{}
//使用析构函数演示:在类中申明,在类外定义
~Vector();
void PushBack(const T& data);
void PopBack();
//...
size_t Size(){return _size;}
T& operator[](size_t pos)
{
assert(pos < _size);
return _pData[pos];
}
private:
T* _pData;
size_t _size;
size_t _capacity;
};
//注意:类模板中函数放在类外进行定义时,需要加模板参数列表
template <class T>
Vector<T>::~Vector()
{
if(_pData)
delete[] _pData;
_size = _capacity = 0;
}
3.2、类模板的实例化
//Vector类名,Vector<int>才是类型
Vector<int> s1;
Vector<double> s2;
类模板实例化与函数模板实例化不同, 类模板实例化需要在类模板名字后跟 <> ,然后将实例化的类型放在 <> 中即可,类模板名字不是真正的类,而实例化的结果才是真正的类
补充:
上节内存管理纠错:
1、栈区先定义的变量放到栈底,地址高,后定义的变量放到栈顶,地址低,因此是向下生长的,堆区则相反
2、.频繁的申请空间和释放空间,容易造成内存碎片,甚至内存泄漏,栈区由于是自动管理,不存在此问题
3、栈可以静态分配,堆只能动态分配。但是堆和栈都可以动态分配。栈可以通过函数_alloca进行动态分配,不过注意,所分配空间不能通过free或delete进行释放
4、new会申请空间,同时调用构造函数初始化对象,malloc只做一件事就是申请空间
5、new是操作符,malloc是函数
6、析构函数调用的顺序
C c;
void main()
{
A*pa=new A();
B b;
static D d;
delete pa;
}
是先手动释放a,然后是b,d,c
7、
A*pa=new A[10];
delete []pa;
这段代码,构造和析构分别调用了10次 而不是1次
8、模板运行时不检查数据类型,也不保证类型安全,相当于类型的宏替换
9、类模板是一个类家族,模板类是通过类模板实例化的具体类
C++中类模板的声明格式为template<模板形参表声明><类声明>,并且类模板的成员函数都是模板函数