类模板的实现一般构造和析构函数后面不加类型参数,但是其他的成员都需要加上类型参数<T>
下面是一个链表的类模板
而且node和Clink都是模板的名称只有node<T>,Clink<T>才是类的名称
类模板是不支持推演的,只能明确指定需要实例化的类型
template<typename T>
class Clink
{
public:
Clink();
~Clink();
void inserthead(T val);
void insertback(T val);
bool empty();
void deletehead();
void show();
private:
class node
{
public:
node(T val = T())
:data(val), next(nullptr) {}
private:
T data;
node* next;
};
node *head;
template<typename F>//模板友元,可以使模板互为友元结构
friend class Clink;
};
template<typename T>
Clink<T>::Clink()
{
head = new node<T>();
}
template<typename T>
Clink<T>::~Clink()
{
node<T> *pcur = head;
node<T> *pnext;
while (pcur != nullptr)
{
pnext = pcur->next;
delete pcur;
pcur = pnext;
}
head = nullptr;
}
template<typename T>
void Clink<T>::inserthead(T val)
{
node<T>* newnode = new node<T>(val);
newnode->next = head->next;
head->next = newnode;
}
template<typename T>
void Clink<T>::insertback(T val)
{
node<T>* newnode = new node<T>(val);
node<T>* tail = head;
while (tail->next != nullptr)
{
tail = tail->next;
}
tail->next = newnode;
}
template<typename T>
bool Clink<T>::empty()
{
return head->next == nullptr;
}
template<typename T>
void Clink<T>::deletehead()
{
if (empty())
{
return;
}
node<T>* del = head->next;
head->next = del->next;
delete del;
}
template<typename T>
void Clink<T>::show()
{
node<T>* p = head->next;
while (p != nullptr)
{
cout << p->data << endl;
p = p->next;
}
}
int main()
{
Clink<int> c1;
Clink<double> c2=c1;//这样的拷贝构造会成功吗,答案是不会,c1的拷贝构造是 Clink<int>(const Clink<int>&)
//而c2的拷贝构造函数是Clink<double>(const Clink<double>&),类型不同,而且系统自带的拷贝
//构造函数会造成浅拷贝的问题,所以我要手写一个拷贝构造函数的模板,并且在Clink模板中设置
//模板的友元
可以在Clink<T>的private中添加:template<typename F>
friend class Clink;
拷贝构造函数模板为:
template<typename E>
Clink(const Clink<E>& rhs)
{
head=new node();
Clink<E>::node* pcur=rhs.phead->next;
node* ptail=head;
while(pcur!=nullptr)
{
node* newnode=new node(pcur->data);
ptail->next=newnode;
ptail=ptail->next;
pcur=pcur->next;
}
}
//但是这时拿一个不相同类型的对象初始化另一个对象所以它并不能称之为拷贝构造函数
//所以拷贝构造函数的模板会退化为构造函数
return 0;
}
typename作用:1.定义模板类型参数
2.声明一个类型
如果要在上面的链表中写一个新的查询函数,由于模板的关系,我们需要得到他的返回值就会出现新的情况
template<typename T>
class Clink
{
public:
Clink();
~Clink();
void inserthead(T val);
void insertback(T val);
bool empty();
void deletehead();
void show();
//查询某个数据所在的节点
class node;//前置声明
node* find(T val);//要在类外实现
private:
class node
{
public:
node(T val = T())
:data(val), next(nullptr) {}
private:
T data;
node* next;
};
node *head;
template<typename F>//模板友元,可以使模板互为友元结构
friend class Clink;
};
template<typename T>
//node* Clink<T>::find(T val)//但是这样的头部是不对的,因为node*是Clink私有的,在全局是看不见的
Clink<T>::node*::Clink<T>::find(T val)//在前面再次加上node*的作用域,但是这样任然无法运行,在编译是还是出错
{ //错误就在函数名这一行
node* pcur=head->next;
while(pcur!=nullptr)
{
if(pcur->data==val)
{
return pcur;
}
pcur=pcur->next;
}
return nullptr;
}
上面程序错误的原因在于:
例如
class Test
{
public:
static int ma;
};
int Test::ma=10;
Test::ma;//这时访问类的静态变量的方式
class Test
{
public:
typdef int ma;
};
Test::ma;//这是使用类中类型的方式
在这种情况下,编译器无法区分这个ma到底是静态变量还是一个类型。
class Test
{
public:
Test(int a):ma(a){}
void show(){ cout<<getma()<<endl;}
int getma(){ return ma;}
private:
int ma;
};
为什么show能在getma之前就能使用getma这个方法呢?
这说明编译器在编译的时候先编译:类名-》类成员名称-》类成员方法的头部(知道每个方法需要什么参数)
-》类成员方法的函数体
所以在show()中调用getma的时候编译器已经知道该类中存在一个叫getma的成员方法。
而模板编译的顺序:1.类模板的头部。2.成员名称。3.类成员方法的头部
而上面那个Clink<T>::node*::Clink<T>::find(T val)函数错误的原因在于编译器在编译find(T val)方法是
还不清楚Clink<T>::node到底是一个静态成员变量还是一个类型,所以会出现错误,而解决方法问题在于需要明确告诉编译器
node*是属于Clink<T>的一个类型,这时可以使用:typename Clink<T>::node*::Clink<T>::find(T val)
类模板特例化:
template<typename T1,typename T2>
class Sum
{
public:
Sum(T1 a, T2 b) :ma(a), mb(b) {}
T1 add()
{
return ma + mb;
}
private:
T1 ma;
T2 mb;
};
int main()
{
Sum<int, int> sum1(10, 20);
sum1.add();
Sum<double, double> sum2(12.1,12.3);
sum2.add();
Sum<int, double> sum3(15, 12.3);//从此为止上面的方式都可以成功
sum3.add();
Sum<char*, char*> sum2("hello", "word");//但是这就会失败,这样的类型,原来的模板无法满足需求
sum3.add(); //所以需要模板的特例化
return 0;
}
类模板的特例化*************************************
template<>//这就属于完全特例化版本
class Sum<char*,char*>
{
public:
Sum(char* a, char* b)
:ma(a), mb(b) {}
char* add()
{
char tmp[100];
memset(tmp, 0, 100);
strcpy(tmp, ma);
strcat(tmp, mb);
return tmp;
}
private:
char* ma;
char* mb;
};
*************************************
类模板的特例化分为:完全特例化 和 部分特例化版本
如果一个模板中有很多的成员方法,那么完全特例化的工作量就十分巨大
如果一个类模板中只有一俩个函数不能满足需求,就可以使用部分特例化针对这几个函数实现函数特例化
特例化的前提是有模板,而类模板中是没有函数模板的,所以我们需要提供模板版本,然后再对模板版本进行函数特例化
template<typename T1,typename T2>
class Sum
{
public:
Sum(T1 a, T2 b) :ma(a), mb(b) {}
T1 add()//普通版本
{
return ma + mb;
}
template<typename E1,typename E2>//函数模板
E1 add()
{
return ma + mb;
}
template<>//函数模板的特例化
char* add<char*,char*>()
{
char tmp[100];
memset(tmp, 0, 100);
strcpy(tmp, ma);
strcat(tmp, mb);
return tmp;
}
private:
T1 ma;
T2 mb;
};
类模板
最新推荐文章于 2023-09-05 11:14:16 发布