模板
模板分为模板函数和模板类。
模板是为了实现泛型编程,所谓泛型编程,就是指编写与类型无关的代码
模板函数
比如以下的场景:
我们想在同一份代码里实现交换整型,浮点型与字符型。
void Swap(int& x, int& y)
{
int tmp = x;
x = y;
y = tmp;
}
void Swap(double& x, double& y)
{
double tmp = x;
x = y;
y = tmp;
}
void Swap(char& x, char& y)
{
char tmp = x;
x = y;
y = tmp;
}
此时,就意味着我得写三份代码,如何我要交换更多类型的数据呢?
所以,模板的好处就在此。
实现一个交换的模板函数:
template <class T>
void Swap(T& x,T& y)
{
T tmp = x;
x = y;
y = tmp;
}
但是,在实现了模板之后,编译器是如何识别的?实际上,模板函数并没有什么实际的意义,而是由编译器根据模板函数推演出函数的实例化,编译器才能够去调用该函数。
也就说是,我将参数传递给函数,编译器会根据该类型去推演处一份代码,然后去调用该函数。
再去汇编中看看:
显然这是两个不同的地址,就说明这确实是两个不同的函数,那么也就是说编译器去调用该代码时,推演出了两份实例化的代码。
实现vector
template <class T>
class Vector
{
protected:
void Expand(size_t newcapacity)
{
if (newcapacity > Capacity())
{
T* tmp = new T[newcapacity];
size_t size = Size();
if (_first)
{
memcpy(tmp, _first, sizeof(T)*Size());
/* for (size_t i = 0; i < Size(); i++)
{
tmp[i] = _first[i];
}*/
delete[] _first;
}
_first = tmp;
_finish = _first + size;
_endofstorage = _first + newcapacity;
}
}
public:
//默认成员函数
Vector()
: _first(NULL)
, _finish(NULL)
, _endofstorage(NULL)
{}
//v1=v2;
Vector(const Vector<T>& v)
{
_first = new T[v.size()];
_finish = _first + v.Size();
_endofstorage = _first + v.Size();
memcpy(tmp, _first, sizeof(T)*Size());
/* for (int i = 0; i < v.Size(); i++)
{
_first[i] = v._first[i];
}*/
}
Vector<T>& operator=(const Vector<int>& v)
{
swap(_first, v._first);
swap(_finish, v._finish);
swap(_endofstorage, v._endofstorage);
return *this;
}
~Vector()
{
if (_first)
{
delete[] _first;
}
_first = _finish = _endofstorage;
}
void PushBack(const T x)
{
Insert(Size(), x);
}
void PopBack()
{
Erase(Size());
}
void Insert(size_t pos, const T& x)
{
//assert(pos <= Size());
if (_finish == _endofstorage)
{
size_t newcapacity = Capacity() == 0 ? 3 : Capacity() * 2;
Expand(newcapacity);
}
//搬运
T* end = _finish - 1;
while (end >= _first + pos)
{
*(end + 1) = *end;
end--;
}
_first[pos] = x;
++_finish;
}
void Erase(size_t pos)
{
assert(pos <= Size());
T* start = _first + pos;
while (start < _finish - 1)
{
*start = *(start + 1);
start++;
}
--_finish;
}
size_t Size() const
{
return _finish - _first;
}
size_t Capacity()
{
return _finish - _first;
}
bool Empty()
{
return _finish == _first;
}
T& operator[](size_t pos)
{
assert(pos < Size());
return _first[pos];
}
void Resever(size_t size);
void Resize(size_t size, const T& value = T());
protected:
T* _first;
T* _finish;
T* _endofstorage;
};
实际上这个代码中有一个细节必须注意,在进行拷贝值时,会牵扯到深浅拷贝的问题。
比如上面这段代码,在vs2013下是一定会崩掉的,原因是取决于编译器对string的实现不同。
在调试窗口观察string:
可以看到string里除了ptr还有一个16字节大小的buf,“aaa”就存在于buf中。
当扩容拷贝值时:
如果是上面那份代码的情况:
可以看到,超长的字符串buf已经村放不下了,那么就会在内存中开辟空间来存放,ptr指向这段内存空间,拷贝时,将ptr也拷贝一份,就会出现如下情况:
这样看似好像也没有问题,但是释放空间就会出问题:
实现List
using namespace std;
template <class T>
struct ListNode
{
ListNode<T>* _prev;
ListNode<T>* _next;
T _data;
ListNode(const T& x)
:_data(x)
,_prev(NULL)
,_next(NULL)
{}
};
template <class T>
class List
{
typedef ListNode<T> Node;
public:
List()
{
_head=new Node(T());
_head->_prev=_head;
_head->_next=_head;
}
~List()
{
Node* cur=_head->_next;
while(cur!=_head)
{
_head->_next=cur->_next;
delete cur;
cur=_head->_next;
}
delete _head;
_head=NULL;
}
List(const List<T>& l)
:_head(new Node(T()))
{
Node* cur=l._head->_next;
while(cur!=_head)
{
PushBack(cur->_data);
cur=cur->_next;
}
}
List<T>& operator=(const List<T>& l)
{
swap(_head,l._head);
return *this;
}
void PushBack(const T x)
{
Insert(_head,x);
}
void PopBack()
{
Erase(_head->_prev);
}
void PushFront(const T x)
{
Insert(_head->_next,x);
}
void PopFront()
{
Erase(_head->_next);
}
//prev pos
//prev newnode next
void Insert(Node* pos,T x)
{
assert(pos);
Node* new_node=new Node(x);
Node* prev=pos->_prev;
Node* next=pos;
new_node->_next=next;
next->_prev=new_node;
new_node->_prev=prev;
prev->_next=new_node;
}
//prev pos next
void Erase(Node* pos)
{
assert(pos&&_head->_next!=_head);
Node* next=pos->_next;
Node* prev=pos->_prev;
prev->_next=next;
next->_prev=prev;
delete pos;
}
size_t Size()
{
Node* cur=_head->_next;
size_t size=0;
while(cur!=_head)
{
size++;
cur=cur->_next;
}
}
bool Empty()
{
return _head->_next==_head;
}
void ListPrint()
{
Node* cur=_head->_next;
while(cur!=_head)
{
cout<<cur->_data<<" ";
cur=cur->_next;
}
cout<<endl;
}
private:
Node* _head;
};