C++模闆封裝Vector:
注意:
在Vector的拷貝構造,擴容函數中,需要注意string的影響。(深層次的深淺拷貝的問題)
在插入string類的變量時,這裡會發生兩個錯誤,一個是對已經釋放的記憶體進行通路,另一個是記憶體的多次析構。
我們知道在windows下或者是說在VS2013環境下,string類的大小為28,這是因為string類為了高效的存取資料,類中有一個大小為16的空間,當建立的對象大小小于16時将直接存在對象中,是以在發生記憶體拷貝時,memcpy并不會出現問題。析構時,新空間和舊空間各自釋放各自的空間,并不會發生錯誤。
但是當對象的大小超過16時,編譯器就會建立一塊空間存放字元串,并使string中的指針指向這塊空間,但是memcpy拷貝時就隻拷貝指向空間的指針,這是發生的就是淺拷貝,兩個指針指向了同一塊記憶體,在擴容函數中調用delete時已經釋放了這塊記憶體,再對其進行通路時,獲得的就是随機值,并且在最後析構的時候,因為對已經析構了的記憶體進行析構,就會導緻程式崩潰。

為了解決這個問題:我們可以采用for循環對對象進行深拷貝。(本質是調用string類的 operator=),但是我們不能進行類型的判斷而選擇不同的代碼執行方案(可以選擇類型萃取),是以目前選擇深拷貝來進行拷貝。
代碼:
為了防止分離編譯的問題這裡将類的聲明和定義都放在 Vector.h中。
#pragma once
#include <iostream>
#include <assert.h>
#include <string.h>
#include <string>
using namespace std;
template<class T>
class Vector
{
public:
Vector();
Vector(const Vector<T>& v);
//Vector<T>& operator=(const Vector<T>& v);
Vector& operator=(Vector<T> v);
~Vector();
//inline size_t Size()const;
inline size_t Size() const;
inline size_t Capacity() const;
inline bool Empty();
void PushBack(const T& x);
void PopBack();
void Insert(size_t pos, const T& x);
void Erase(size_t pos);
size_t Find(const T& x);
void Print();
const T& Back()const
{
return *(_finish - 1);
}
T& operator[](size_t index)
{
return _start[index];
}
void Expand(size_t n);
void Clear()
{
_finish = _start;
}
protected:
T* _start;
T* _finish;
T* _endofstorage;
};
//類名 : aa vector
//類型名: aa vector<T>
template<class T>
Vector<T>::Vector()
:_start(NULL)
, _finish(NULL)
, _endofstorage(NULL)
{}
//v1(v2);
template<class T>
Vector<T>::Vector(const Vector<T>& v)
{
_start = new T[v.Size()];
// memcpy(_start, v._start, sizeof(T)* v.Size()); //string類型資料的拷貝會存在深淺拷貝的問題
for (int i = 0; i < Size(); ++i)
{
_start[i] = v._start[i];
}
_finish = _start + v.Size();
_endofstorage = _start + v.Capacity();
}
template<class T>
Vector<T>& Vector<T>::operator=(Vector<T> v) //Vector<T>類型 Vector 類名
{
if (_start != v._start)
{
swap(_start, v._start);
swap(_finish, v._finish);
swap(_endofstorage, v._endofstorage);
}
return *this;
}
template<class T>
Vector<T>::~Vector()
{
delete []_start;
_start = _finish = _endofstorage = NULL;
}
template<class T>
inline size_t Vector<T>::Size()const
{
return _finish - _start;
}
template<class T>
inline size_t Vector<T>::Capacity() const
{
return _endofstorage - _start;
}
template<class T>
inline bool Vector<T>::Empty()
{
return _start == _finish;
}
template<class T>
void Vector<T>::Insert(size_t pos, const T& x)
{
assert(pos <= Size());
if (Size() >= Capacity())
{
Expand(Capacity() * 2);
}
T* end = _finish - 1;
while (end >= _start + pos)
{
*(end + 1) = *end;
--end;
}
_start[pos] = x;
++_finish;
}
template<class T>
void Vector<T>::Erase(size_t pos)
{
assert(pos < Size());
T* Cur = _start + pos + 1;
while (Cur != _finish)//<
{
*(Cur - 1) = *Cur;
++Cur;
}
--_finish;
}
template <class T>
void Vector<T>::Expand(size_t n)
{
if (Empty())
{
_start = new T[3];
_finish = _start;
_endofstorage = _start + 3;
}
else if (n > Capacity()) //擴容時進行記憶體拷貝如果是string類型的時候會出現錯誤,因為要進行資料拷貝,
//string有16個字元大小的buf數組空間,小一點的時候是存放在buf中
//大的時候進行拷貝發生的是淺拷貝,開辟空間,指針指向同一個位置釋放空間出錯
{
size_t size = Size();
T* tmp = new T[n];
memcpy(tmp, _start, sizeof(T)* Size());
//for (size_t i = 0; i < Size(); i++)
//{
// tmp[i] = _start[i];
//}
delete []_start; //調析構函數,再調free, 如果是string深層次調用string的析構函數,
//淺拷貝導緻的析構兩次的問題
_start = tmp;
_finish = _start + size;
_endofstorage = _start + n;
}
}
template <class T>
void Vector<T>::PushBack(const T& x)
{
Insert(Size(), x);
}
template <class T>
void Vector<T>::PopBack()
{
Erase(Size() - 1);
}
template <class T>
size_t Vector<T>::Find(const T& x)
{
T* Cur = _start;
while (Cur != _finish)
{
if (*Cur == x)
{
return Cur - _start;
}
}
return -1;
}
template <class T>
void Vector<T>::Print()
{
T* Cur = _start;
while (Cur != _finish)
{
cout << *Cur << " ";
++Cur;
}
cout << endl;
}
void TestVector()
{
//Vector<char> v1;
//v1.PushBack('c');
//v1.PushBack('c');
//v1.PushBack('c');
//v1.PushBack('c');
//v1.PushBack('c');
//v1.PushBack('c');
//v1.PushBack('c');
//v1.Print();
//Vector<char>v2(v1);
//v2.Print();
Vector<string> v3; //string類型發生擴容的時候的空間拷貝比較特殊
v3.PushBack("1"); //char* []
v3.PushBack("2");
//v3.PushBack("111b1111111111111111111111111111111111111111");
v3.PushBack("4");
//v3.PopBack();
v3.Print();
}
SList.h
#pragma once
#include <iostream>
#include <string>
#include <assert.h>
using namespace std;
template<class T>
struct SListNode{
SListNode(const T& x)
: _data(x)
, _prev(NULL)
, _next(NULL)
{}
T _data;
SListNode<T>* _prev;
SListNode<T>* _next;
};
template<class T>
class SList{
typedef SListNode<T> Node;
public:
SList()
{
_head = new Node(T());
_head->_prev = _head;
_head->_next = _head;
}
SList(const SList<T>& l);
//SList<T>& operator=(const SList<T>& l);
SList<T>& operator=(SList<T> l);
~SList();
void PushBack(const T& x);
void PopBack();
void PushFront(const T& x);
void PopFront();
// pre newnd pos
void Insert(Node* pos, const T& x);
// pre pos next
void Erase(Node* pos);
void Clear();
void Print();
void Swap(SList<T>& l);
size_t Size()const;
const T& Back()const;
const T& Front()const;
bool Empty()const;
protected:
Node* _head;
};
template<class T>
SList<T>::SList(const SList<T>& l)
:_head(new Node(T()))
{
_head->_next = _head;
_head->_prev = _head;
Node* pCur = l._head->_next;
while (pCur != l._head)
{
PushBack(pCur->_data);
pCur = pCur->_next;
}
}
template<class T>
SList<T>& SList<T>::operator=(SList<T> l)
{
swap(_head, l._head);
return *this;
}
template<class T>
SList<T>::~SList()
{
//Node* pCur = _head->_next;
//Node* Next = NULL;
//while (pCur != _head)
//{
// Next = pCur->_next;
// delete pCur;
// pCur = Next;
//}
Clear();
delete _head;
_head = NULL;
}
template<class T>
void SList<T>::PushBack(const T& x)
{
Insert(_head, x);
}
template<class T>
void SList<T>::PopBack()
{
Erase(_head->_prev);
}
template<class T>
void SList<T>::PushFront(const T& x)
{
Insert(_head->_next, x);
}
template<class T>
void SList<T>::PopFront()
{
Erase(_head->_next);
}
template<class T>
void SList<T>::Insert(Node* pos, const T& x)
{
assert(NULL != pos);
Node* NewNode = new Node(T(x));
Node* Pre = pos->_prev;
NewNode->_prev = Pre;
NewNode->_next = pos;
Pre->_next = NewNode;
pos->_prev = NewNode;
}
template<class T>
void SList<T>::Erase(Node* pos)
{
assert(_head != pos);
Node* Pre = pos->_prev;
Node* Next = pos->_next;
Pre->_next = Next;
Next->_prev = Pre;
delete pos;
pos = NULL;
}
template<class T>
void SList<T>::Clear()
{
Node* Cur = _head->_next;
Node* Next = NULL;
while (Cur != _head)
{
Next = Cur->_next;
delete Cur;
Cur = Next;
}
}
template<class T>
void SList<T>::Print()
{
Node* pCur = _head->_next;
while (pCur != _head)
{
cout << pCur->_data << " ";
pCur = pCur->_next;
}
cout << endl;
}
template<class T>
void SList<T>::Swap(SList<T>& l)
{
swap(_head, l._head);
}
template<class T>
size_t SList<T>::Size()const
{
size_t size = 0;
Node* Cur = _head->_next;
while (Cur != _head)
{
++size;
Cur = Cur->_next;
}
return size;
}
template<class T>
const T& SList<T>::Back()const
{
return _head->_prev->_data;
}
template<class T>
const T& SList<T>::Front()const
{
return _head->_next->_data;
}
template<class T>
bool SList<T>::Empty() const
{
return _head->_next == _head;
}
void TestSList()
{
SList<string> l1;
l1.PushBack("1111");
l1.PushBack("1112");
l1.PushBack("1113");
l1.PushBack("1114");
l1.PushBack("1115");
l1.Print();
l1.PopBack();
l1.Print();
}