扫二维码与项目经理沟通
我们在微信上24小时期待你的声音
解答本文疑问/技术咨询/运营咨询/技术建议/互联网交流
[TOC]
专业从事成都网站设计、做网站、成都外贸网站建设公司,高端网站制作设计,微信平台小程序开发,网站推广的成都做网站的公司。优秀技术团队竭力真诚服务,采用H5技术+CSS3前端渲染技术,响应式网站建设,让网站在手机、平板、PC、微信下都能呈现。建站过程建立专项小组,与您实时在线互动,随时提供解决方案,畅聊想法和感受。当我们实现一个交换函数时,我们可以写成如下。
void Swap(int& x, int& y)
{
int tmp = x;
x = y;
y = tmp;
}
这里只能交换两个整数,当我们一会需要实现两个字符交换时,我们有需要重新写个函数,然而两份代码有很多相同的部分,这样是不是很麻烦。假如我们只需要写一份代码便可以实现不同类型的交换,是不是很棒。是的,这个编译器已经帮我们设计好了,这就是所谓的泛型编程。
模板是泛型编程的基础,所谓泛型编程就是编写与类型无关的逻辑代码,是一种复用的方式。模板分为模板函数和模板类。
template< class 形参名1, class 形参名2, class 形参名n>
返回类型 函数名(参数列表)
{...}模板形参的定义既可以使用class,也可以使用typename,含义是相同的。
刚刚的Swap函数就可以用模板函数搞定了。
template
void Swap(T& x, T& y)
{
T tmp = x;
x = y;
y = tmp;
}
看看是不是可以进行多种类型交换,测试结果:
这就是模板函数的实现,当然我们很好奇为什么一个函数就可以搞定。其实在底层实现了函数重载,我们转到汇编代码便可得知。
int main()
{
00394D30 push ebp
00394D31 mov ebp,esp
00394D33 sub esp,114h
00394D39 push ebx
00394D3A push esi
00394D3B push edi
00394D3C lea edi,[ebp-114h]
00394D42 mov ecx,45h
00394D47 mov eax,0CCCCCCCCh
00394D4C rep stos dword ptr es:[edi]
00394D4E mov eax,dword ptr [__security_cookie (039A000h)]
00394D53 xor eax,ebp
00394D55 mov dword ptr [ebp-4],eax
int a1 = 1, a2 = 2;
00394D58 mov dword ptr [a1],1
00394D5F mov dword ptr [a2],2
Swap(a1, a2);
00394D66 lea eax,[a2]
00394D69 push eax
00394D6A lea ecx,[a1]
00394D6D push ecx
00394D6E call Swap (039137Ah)
00394D73 add esp,8
char c1 = 5, c2 = 6;
00394D76 mov byte ptr [c1],5
00394D7A mov byte ptr [c2],6
Swap(c1, c2);
00394D7E lea eax,[c2]
00394D81 push eax
00394D82 lea ecx,[c1]
00394D85 push ecx
00394D86 call Swap (0391375h)
00394D8B add esp,8
double d1 = 1.222, d2 = 2.011111111111;
00394D8E movsd xmm0,mmword ptr [__real@3ff38d4fdf3b645a (0397BD0h)]
00394D96 movsd mmword ptr [d1],xmm0
00394D9B movsd xmm0,mmword ptr [__real@400016c16c16c072 (0397BD8h)]
00394DA3 movsd mmword ptr [d2],xmm0
Swap(d1, d2);
00394DA8 lea eax,[d2]
00394DAB push eax
00394DAC lea ecx,[d1]
00394DAF push ecx
00394DB0 call Swap (039137Fh)
00394DB5 add esp,8
return 0;
00394DB8 xor eax,eax
}
可以看到在底层,每一次调用Swap函数都会建立一个栈帧,而每次栈帧建立,形参的类型是不同的,建立栈帧也是不同的。当我们使用模板时编译器会进行一个推演的过程,这个过程在编译之前进行。推演时,编译器会根据传递参数的类型实例化(编译器隐式实例化
)
出相应的函数,在进行编译。例如:
但是当我们遇到这样Swap(1,1.2302102);
,此时编译器如何判断到底实例化成那种类型?
其实我们如果把模板声明为这样既可以解决了。模板函数重载
(与上面的函数构成重载)
template //使用class 和 typename一样的效果
void Swap(T1& x,T2& y)
{
T1 tmp = x;
x = y;
y = tmp;
}
有时候我们可能会要到这样的奇葩问题。
template< class T>
const T Add(T& x,T& y)
{
return x+y;
}
当我们这样调用时Add(1,5.222222);
,编译器又该如何实例化呢?
这就涉及必须显示指定实例化类型 模板参数显示实例化
Add<double>
(1.5.2222222); 这样就可以搞定刚刚的问题。
template
class 类名
{ ... };
当我们刚开始用c++写顺序表和链表之前,我们是这样的。
typedef int Datatype;
typedef struct SeqList
{
struct SeqList* _data;
size_t _size;
}SeqList;
typedef struct ListNode
{
struct ListNode* _prev;
struct ListNode* _next;
Datatype _data;
}ListNode;
我们这样定义顺序表和链表的,但是会存在很大一个问题,如下。
当我们在一个程序中要使用两个不同数据类型顺序表和链表,这样是无法完成的,除非我们每种类型定义一个类型
typedef int Datatype; //存int类型
typedef struct SeqList
{
struct SeqList* _data;
size_t _size;
}SeqList;
typedef struct ListNode
{
struct ListNode _prev;
struct ListNode _next;
Datatype _data;
}ListNode;
typedef char Datatype; //存char类型
typedef struct SeqList
{
struct SeqList* _data;
size_t _size;
}SeqList;
typedef struct ListNode
{
struct ListNode* _prev;
struct ListNode* _next;
Datatype _data;
}ListNode;
这样就会很麻烦,在我们学了模板之后,我们可以这样。
模板实现顺序表
template
class Vector
{
public:
Vector():_first(NULL),_finish(NULL),_endofstorge(NULL)//构造函数
{}
~Vector()//析构函数
{
delete[]_first;
_first = _finish = _endofstorge = NULL;
}
Vector(const Vector& v)//拷贝构造
:_first(NULL)
,_finish(NULL)
,_endofstorge(NULL)
{
int len = v._finish - v._first;
_first = _finish = new T[len];
T* start = v._first;
while(start != v._finish)
{
*(_finish) = *start;
++_finish;
++start;
}
_endofstorge = _first+len;
}
Vector& operator=(Vector& v) //赋值运算符重载
{
delete[]_first;
Vector v1(v);
swap(_first ,v1._first);
swap(_finish , v1._finish);
swap(_endofstorge , v1._endofstorge);
return *this;
}
void PushBack(const T& x) //尾插
{
if(_finish == _endofstorge)
Expand(Capacity()*2+1);
_first[Size()] = x;
++_finish;
}
void PopBack()//尾删
{
Erase(Size()-1);
}
void Expand(size_t n) //扩容
{
int size = Size();
if(n>Capacity())
{
T* tmp = new T[n];
for (int i = 0;i
测试代码及结果:
void VectorTest()
{
Vector v;
v.PushBack(1);
v.PushBack(2);
v.PushBack(3);
v.PushBack(4);
v.PushBack(5);
v.PushBack(6);
v.PushBack(7);
PrintVocter(v);
Vector v1;
v1.PushBack("hello");
v1.PushBack("world !");
v1.PushBack("i");
v1.PushBack("love");
v1.PushBack("you");
PrintVocter(v1);
}
模板实现双链表
#ifndef __LIST_H__
#define __LIST_H__
#include
#include
using namespace std;
template
struct ListNode
{
struct ListNode* _prev;
struct ListNode* _next;
T _data;
};
template
class List
{
typedef ListNode Node;
public:
List()//构造函数
{
_head = new Node;
_head->_next = _head->_prev = _head;
}
List(const List& h) //拷贝构造
{
Node* head = h._head;
Node* tmp = head->_next;
_head = new Node;
_head->_next = _head->_prev = _head;
while (tmp != head)
{
PushBack(tmp->_data);
tmp = tmp->_next;
}
}
~List() //析构函数
{
Clear();
delete _head;
_head = NULL;
}
void PushBack(const T& x) //尾插
{
Node *tmp = new Node;
tmp->_data = x;
Node* tail = _head->_prev;
tail->_next = tmp;
tmp->_prev = tail;
_head->_prev = tmp;
tmp->_next = _head;
}
void PushFront(const T& x) //头插
{
Node *tmp = new Node;
tmp->_data = x;
Node* cur = _head->_next;
tmp->_prev = _head;
_head->_next = tmp;
tmp->_next = cur;
cur->_prev = tmp;
}
void PopBack() //尾删
{
Node* cur = _head->_prev;
_head->_prev = cur->_prev;
cur->_prev->_next = _head;
delete[]cur;
cur->_next = cur->_prev = NULL;
}
void PopFront() //头删
{
Node* cur = _head->_next;
_head->_next = cur->_next;
cur->_next->_prev = _head;
}
void Insert(Node* pos,const T& x) //随机插入
{
Node* cur = new Node;
cur->_data = x;
cur->_prev = pos->_prev;
pos->_prev->_next = cur;
pos->_prev = cur;
cur->_next = pos;
}
void Erase(Node* pos) //删除随机位置
{
Node* cur = pos->_next;
pos->_prev->_next = cur->_next;
cur->_next->_prev = pos->_prev;
}
void Clear() //清除链表数据
{
Node* cur = _head->_next;
while(cur != _head)
{
Node* tmp = cur;
cur = cur->_next;
delete tmp;
}
_head->_next = _head;
_head->_prev = _head;
}
Node* Find(const T& x) //查找
{
Node* cur = _head->_next;
while (cur != _head)
{
if(cur->_data==x)
return cur;
cur = cur->_next;
}
return NULL;
}
size_t Size() //求取链表长度
{
size_t count = 0;
Node* cur = _head->_next;
while (cur != _head)
{
count++;
cur = cur->_next;
}
return count;
}
bool Empty()
{
if (_head->_next = _head->_prev)
return 1;
return 0;
}
void PrintList() //打印链表
{
Node* tmp = _head->_next;
while (tmp != _head)
{
std::cout<_data<<" ";
tmp = tmp->_next;
}
std::cout<
这样我们的顺序表和链表就可以实现任意类型的程序了。
另外有需要云服务器可以了解下创新互联scvps.cn,海内外云服务器15元起步,三天无理由+7*72小时售后在线,公司持有idc许可证,提供“云服务器、裸金属服务器、高防服务器、香港服务器、美国服务器、虚拟主机、免备案服务器”等云主机租用服务以及企业上云的综合解决方案,具有“安全稳定、简单易用、服务可用性高、性价比高”等特点与优势,专为企业上云打造定制,能够满足用户丰富、多元化的应用场景需求。
我们在微信上24小时期待你的声音
解答本文疑问/技术咨询/运营咨询/技术建议/互联网交流