C++ 关于泛型模板的相关问题
泛型编程最早提出的目的是为了减轻代码量,避免无意义的重载而提出的;
在检查的时候并不针对于泛型进行检查,只有在编译的时候才会针对于template的类型来进行实例化;
泛型函数模板:
补充:关于内联函数的问题
内联函数类似于c中的宏展开,本质上就是为了减少调用栈的开销而提出的,采用inline方式来构造;
泛型函数也可以使用内联函数,对于最简单的例子如下:
#include<stdio.h>
#include<iostream>
#include<vector>
#include<string>
using namespace std;
template <typename T> inline T cmp(const T& t1, const T& t2) {
if (t1 > t2)
return t1;
else
return t2;
}
int main() {
int a = 1, b = 0;
string s1 = "123", s2 = "234";
cout << cmp(a, b) << endl;
cout << cmp(s1, s2) << endl;
system("pause");
}
泛型类模板:
#include<stdio.h> #include<iostream> #include<vector> #include<string> using namespace std; template <typename Type> class Queue { public: Queue(); type& front(); private: //...private person }; int main() { system("pause"); }
如上所示,和泛型函数模板类似,就是在类的定义前加了模板定义;
模板类型形参:
模板类型形参主要指模板尖括号中的声明元素;
主要注意两点:
1.对于模板声明,typename和class都可以,但是前者只适用于C++标准,后者通用;
2.对于size_type这种容器的成员,其实属于类,对于模板中使用size_type作为类型,需要使用typename来指定,该成员是类中的一个类型,而非一个类数据;
如下所示:
template <class Parm, class U> Parm fcn(Parm* array, U value) {
typename Parm::size_type* p;
Parm::v = value;
}
3.关于非模板类型形参的问题:
对于template尖括号中的内容,不一定非要用模板来进行确定,也可以直接采用非模板参数来进行定义,如下所示:
#include<stdio.h>
#include<iostream>
#include<vector>
#include<string>
using namespace std;
template<typename T, int N> void output(T(&parm)[N]) {
cout << N << endl;
}
int main() {
int x[42];
double y[34];
output(x);
output(y);
system("pause");
}
个人认为该行为最主要的就是隐式的传递了参数;
例如上述的数组大小,避免了函数形参中需要显式的传递数组大小,直接通过模板定义中进行传递;
但是针对于后面的论述,模板参数定义中,参数定义越少越好;
指定模板形参:
其中最主要的特点之一就是可以指定特定的返回值,但是尤其要注意的是模板形参列表得顺序。
一般而言,模板形参的第一个列表可以作为指定的返回值来使用:
#include<stdio.h> #include<iostream> #include<vector> #include<string> using namespace std; template <typename T, typename U, typename V> T output(U a, V b) { cout << a << endl << b << endl; return a + b; } int main() { cout << output<int>(1.5, 2.6) << endl; system("pause"); }
比较重要的还是注意返回值的指定类型。一般来说默认尖括号的第一个位置是返回值;
类模板成员——通过Queue类得构造来理解类泛型:
在这样给小章节,书中主要举例了Queue类的构造,通过例子来说明类模板的一些问题,比如友元、模板实参、成员模板以及static成员。
首先对于Queue队列中元素的定义:
template <typename Type>class QueueItem {
QueueItem(const Type& t) :item(t), next(0) {};
Type item;
QueueItem* next;
};
直接使用模板在QueueItem中进行定义,对于QueueItem中的类型,可以在实例化Queue中进行保存。本次的例子采用链表进行保存;
#include<iostream>
#include<string>
#include<stdio.h>
using namespace std;
template <typename Type>class QueueItem {
public:
QueueItem(const Type& t) :item(t), next(0) {};
Type item;
QueueItem* next;
};
template <typename Type> class MQueue {
public:
MQueue() :head(0), tail(0) {}
MQueue(const MQueue& Q) :head(0), tail(0) {
copy_elems(Q);
}
MQueue& operator=(const MQueue&);
~MQueue() {
destory();
}
Type& front() {
return head->item;
}
const Type& front() const {
return head->item;
}
void push(const Type&);
void pop();
bool empty() const {
return head == 0;
}
private:
QueueItem<Type>* head;
QueueItem<Type>* tail;
void destory();
void copy_elems(const MQueue&);
};
//类函数类外定义形式采用:
//template <typename type>ret-type class_name<Type>::memeber_name
template <typename type> void MQueue<type>::destory() {
while (!empty())
pop();
}
template <typename type> void MQueue<type>::pop() {
QueueItem<type>* p = head;
head = head->next;
delete p;
}
template <typename type> void MQueue<type>::push(const type& val) {
QueueItem<type>* pt = new QueueItem<type>(val);
if (empty()) {
head = tail = pt;
}
else {
tail->next = pt;
tail = pt;
}
}
template <typename type> void MQueue<type>::copy_elems(const MQueue& orig) {
for (QueueItem<type>* pt = orig.head; pt; pt = pt->next)
push(pt->item);
}
值得注意
1.private中的QueueItem指针head和tail,直接通过实例化Queue的模板参数就可以给定了QueueItem的类型;
2.在当前类模板的生效范围内,如果想采用其他模板类,必须要对其指定类型;
对于友元问题:
模板的友元问题也可以很好的在这里得到体现。
对于非模板类,如果声明友元函数,可以i很好的构建一个的或者多个实体的有缘关系。
对于模板而言,又有不同的几种情况:
1.授予对友元素说有实例的访问权;
2.只授予模板实例化的特定实例的访问权;
针对于所有实例都有访问权:
template <typename Type> class Bar {
template <typename T> friend class Fool;
};
该实例其实说明了对于所有的Fool模板实例化的类函数,都可以访问Bar模板所有实例的成员函数和成员;
这里再声明一下友元类的一些细节。
如果A中声明了友元类B,则B的成员函数可以视为A的成员函数,等价于可以直接访问A中的所有私有成员。
同样的,如果是针对于友元函数,而非友元类:
template <typename Type> class Bar {
//template <typename T> friend class Fool;
template <typename T> friend void temp1_fcn1(const T&);
};
针对于特定实例的友元关系:
一般有两种情况:
1.同类型模板友元:
template <typename Type> class Bar {
friend class Fool<Type>;
friend void temp1_fcn1<Type>(Type const&);
};
可以清楚的看到,Fool的实例化类型和Bar类似,所以说明只有同类型的Fool才可以访问Bar的私有成员;
2.指定类型友元:
template <typename Type> class Bar {
friend class Fool<char*>;
friend void temp1_fcn1<char*>(char* const&);
};
可以显而易见,虽然Fool依然是模板,但是制定了类型,Fool<char*>,友元函数同理;
所以可以理解为,只有char*类型的模板实例化可以访问Bar的私有成员;
因此,针对于前面的MQueue中,可以不再将QueueItem变为共用成员,直接采用友元声明:
template <typename Type> class MQueue;
template <typename Type>class QueueItem {
public:
QueueItem(const Type& t) :item(t), next(0) {}
friend class MQueue<Type>;
private:
Type item;
QueueItem* next;
};
但是要注意声明问题,即在声明友元类的时候,进行初始化;
关于成员模板的问题:
一般来说,不一定是类或者函数整体声明为模板,也可以在类内声明模板函数。
例如典型的assign成员函数;
template <typename It> MQueue(It beg, It end) : head(0), tail(0) {
copy_elems(beg, end);
}
template <typename Iter> void assign(Iter, Iter);
template <typename Iter> void copy_elems(Iter, Iter);
//对于该情况,比较典型的需求是将不同类型的元素相互转换,例如vector和Queue相互转换。
所以对于两个不同的模板可以模板类内嵌套的方式进行解决;
对于详细的类外定义,注意,需要补全模板成员函数的声明:
template <typename type> template<typename Iter> void MQueue<type>::assign(Iter beg, Iter end) {
destory();
copy_elems(beg, end);
}
template <typename type> template<typename Iter> void MQueue<type>::copy_elems(Iter beg, Iter end) {
while (beg != end) {
push(*beg);
++beg;
}
}去
q

浙公网安备 33010602011771号