模板参数的默认值,非类型参数和模板参数
模板参数的默认值
1.类模板的参数可以有默认值,有默认值的类型参数必须靠右
如果实例化不提供参数的类型,就使用默认值
2.右边类型参数的默认值可以是左边的类型参数
类模板的非类型参数
1.类模板可以接收非类型参数
2.非类型参数只能是常量,常量表达式,常属性的变量
3.非类型参数也可以有默认值
4.函数模板也可以接收非类型参数和默认值
以下了解的可以了实际开发可能用不到
模板的模板参数
模板除了可以接收类型参数和非类型参数以外,也可以接收模板作为参数,语法如下:
template <template <typename T> class xxx>
class Crab{
//...可以使用参数类模板,传入的模板保持统一
};
模板成员
模板可以作为类模板或类的成员/友元,STL的实现使用了该语法
1.类模板成员
类模板实例化的类型参数可以来自于外部类模板,也可以直接指定。
2.函数模板成员
函数模板实例化的类型可以来自于外部类模板或者直接指定,也支持类型推断
3.函数模板和类模板作为友元
如果友元使用了所在类模板的类型参数,属于约束性友元模板
否则属于非约束型友元模板
类模板的继承
类继承类模板
语法:
template <typename T>
class BaseA{
public:
//......
private:
T data;
};
//类继承类模板
class Derived:public BaseA<int>{
//......
};
类模板继承类
语法:
class BaseA{
public:
//......
private:
int data;
};
//类模板继承类
template <typename T>
class Derived:public BaseA{
public:
//.....
private:
T data2;
};
类模板继承类模板
语法:
template <typename T>
class BaseA{
public:
//......
private:
T data;
};
//类模板继承类模板
template <typename N>
class Derived:public BaseA<N>{
public:
//.....
private:
N data2;
};
继承中使用模板参数
语法:
template <typename T>
class Derived:public T{
//......
};
模板的递归实例化
所谓的模板的递归实例化就是使用类模板实例化后作为参数继续实例化本模板
MyArray<int> arr;
MyArray<MyArray<int>> arr;//递归实例化
MyArray<MyArray<MyArray<int>>> arr;//递归实例化
模板的划分
在实际的C++开发中,我们会将类的声明(模板的声明)写在头文件,类/类模板中的函数实现写在源文件,由于模板的实例化是在编译时完成的,所以必须把模板的声明和实现写在一个编译单元中,此时需要使用模板的划分来实现。
可以在模板声明的头文件中用以下语法将实现加进来
#include "xxx.cpp"
练习:
将1search.cpp的类模板实现模板的划分。
class A{
public:
void show()
{
cout<<"show A"<<endl;
}
};
int main()
{
A *pa = NULL;
pa->show();
}
注意:可以使用类类型的空指针去调用成员函数,因为成员函数不属于对象,也不存储在对象中,存储在代码段,调用成员函数只需要找到成员函数的地址即可。如果成员函数中访问了成员变量,就会发生段错误。
智能指针
将指针对象模板化实现了类型的通用,构成智能指针。
根据智能指针 拷贝/赋值 处理的不同,可以分为三种实现形式
1.拷贝/赋值时分配新的地址空间,拷贝原指针指向的内容
2.拷贝/赋值时将右边的空间和地址传递给左边,右边会失去原来的地址空间
3.拷贝/赋值时共享地址空间,同时增加一个引用计数(记录使用地址空间的对象个数)
当引用技术为0释放地址空间
C++预定义了智能指针,可以直接使用,智能指针有4种,介绍其中3种,使用它们需要添加头文件memory
//C++98加入 C++11弃用 C++17移除
auto_ptr ------- 使用方法2实现(只能一个对象记录地址空间)
//C++11添加
unique_ptr ------ 方法2实现,禁止传递的语法
shared_ptr ------ 方法3实现,采用引用计数
智能指针对象销毁时会自动释放记录的地址空间,很大程度上避免了内存泄漏的发生。
作业:
将模板成员的代码实现模板的划分
附加:用户输入两个字符串,编写代码求出两个字符串的最长公共子串
kkkhellowebye
ghsasabyehellod
===>hello
/*02-求最长公共子串*/
#include <iostream>
using namespace std;
//暴力求解
string getLCS(const string &str1,const string &str2)
{
//记录最长子串在str1中的起始位置和长度
size_t max_start = 0,max_len = 0;
for(size_t i=0;i<str1.length();i++){
for(size_t j=0;j<str2.length();j++){
//计算当前最长公共子串
size_t k,l;
for(k=i,l=j;k<str1.length()&&l<str2.length();k++,l++){
if(str1.at(k)!=str2.at(l))
break;
}
//记录最长长度的公共子串
if(k-i>max_len){
max_len = k-i;
max_start = i;
}
}
}
return str1.substr(max_start,max_len);
}
//矩阵法求解
string getLCS1(const string &str1,const string &str2)
{
//记录最长子串在str1中的结束位置和长度
int max_end = 0,max_len = 0;
//创建比较矩阵
int **arr = new int *[str1.length()];
for(size_t i=0;i<str1.length();i++){
arr[i] = new int[str2.length()];
for(size_t j=0;j<str2.length();j++){
//比较字符,将比较结果写入矩阵
if(str1.at(i)==str2.at(j)){//赋值 左上角+1
//如果左上角不存在
if(i==0||j==0)
arr[i][j] = 1;
else
arr[i][j] = arr[i-1][j-1]+1;
//记录最长长度和此时str1中最后一个字符的位置
if(arr[i][j]>max_len){
max_len = arr[i][j];
max_end = i;
}
}
else{
arr[i][j] = 0;
}
}
}
//释放空间
for(size_t i=0;i<str1.length();i++){
delete[] arr[i];
}
delete[] arr;
//返回最长公共子串
return str1.substr(max_end-max_len+1,max_len);
}
int main()
{
string str1 = "kkkhelaalowebyebye";
string str2 = "kkkhelaalowebyebye111";
cout<<getLCS(str1,str2)<<endl;
cout<<getLCS1(str1,str2)<<endl;
return 0;
}