函數模板提供了一種機制通過它我們可以保留函數定義和函數調用的語義在一個程序位置上封裝了一段代碼確保在函數調用之前實參只被計算一次.
函數模板提供一個種用來自動生成各種類型函數實例的算法程序員對於函數接口參數和返回類型中的全部或者部分類型進行參數化(parameterize)而函數體保持不變.
函數模板使用注意事項:
1、每個函數模板前邊都需要添加模板聲明例如:template<typename T>
2、在模板類的使用時,注意其類型改變,例如:
template<typename T>
class SeqList
{};
該模板類的類型爲SeqList<T> ,SeqList僅爲一個類名
3、函數模板不能在.h文件和.cpp文件中分開編譯
在實現順序表的時候我們需要其可以存儲任何類型的數據。但是對於string類型對象要特別考慮。下面我們來分析一下常用的拷貝的方法。
1)逐個對象的複製
優點:對於數據的類型無需特別考慮
缺點:拷貝的效率比較低
2)memcpy()複製
優點:拷貝的效率比較高
缺點:在涉及指針的複製時,可能出現多個指針指向同一份空間,導致內存釋放多次引起程序崩潰。
string類深度剖析
在不同的編譯器中string類的空間大小不同,但是可以確定的是,string類存在一個16個字節大小的buffer空間用於存放長度較短的字符來提高效率,還有一個4字節的——ptr用於指向當字符長度大於15時的字符串空間,還有兩個4字節的_size和_capacity用來表示大小和容量。總共28個字節。
這裏就需要考慮在擴容和拷貝構造時的複製了,如果字符串的長度大於15個,則使用memcpy()僅僅是數值的拷貝,並不會爲_ptr申請空間會導致多個指針同時指向同一份空間,使得該空間在釋放時釋放多次導致程序崩潰。
解決方法一:
統統採用單個賦值的,但是效率比較低。
解決方法二(最優解):
類型萃取,將內置類型和非內置類型分開copy的效率比較高。
void Copy(T* dest, const T* src, size_t size)
{
if (TypeTraits<T>::IsPoodType().Get())//類型萃取,內置類型
{
memcpy(dest, src, sizeof(T)*size);
}
else//非內置類型
{
for (size_t i = 0; i < size; i++)
{
dest[i] = src[i];
}
}
}
struct TrueType
{
bool Get()
{
return true;
}
};
struct FalseType
{
bool Get()
{
return false;
}
};
template<typename T>
struct TypeTraits
{
typedef FalseType IsPoodType;//內嵌型別
};
//特化
template<>
struct TypeTraits<int>
{
typedef TrueType IsPoodType;
};
template<>
struct TypeTraits<unsigned int>
{
typedef TrueType IsPoodType;
};
template<>
struct TypeTraits<char>
{
typedef TrueType IsPoodType;
};
template<>
struct TypeTraits<unsigned char>
{
typedef TrueType IsPoodType;
};
template<>
struct TypeTraits<double>
{
typedef TrueType IsPoodType;
};
template<>
struct TypeTraits<bool>
{
typedef TrueType IsPoodType;
};
template<>
struct TypeTraits<float>
{
typedef TrueType IsPoodType;
};
template<>
struct TypeTraits<short>
{
typedef TrueType IsPoodType;
};
template<>
struct TypeTraits<unsigned short>
{
typedef TrueType IsPoodType;
};
template<>
struct TypeTraits<long long>
{
typedef TrueType IsPoodType;
};
template<>
struct TypeTraits<long double>
{
typedef TrueType IsPoodType;
};
#include <iostream>
#include<string>
using namespace std;
const int SIZE = 3;//構造對象的默認大小
const int DEAFAULT = 3;//每次擴容的大小
template<typename T>
class SeqList
{
public:
//構造函數
SeqList();
//拷貝構造函數
SeqList(const SeqList& l);
//賦值
SeqList<T> operator=(SeqList l);
//析構函數
~SeqList();
public:
void PushBack(const T& d);
void PopBack();
void PushFront(const T& d);
void PopFront();
void SortList();
void Reverse();
T* Find(const T& d);
void Insert(int pos, const T& x);
void Remove(T x);
void RemoveAll(T x);
void CheckCapacity();
void Print();
private:
T* _data;
int _size;
int _capacity;
};
template<typename T>
void SeqList<T>::Print()
{
int i = 0;
for (i = 0; i < _size; i++)
{
cout << _data[i] << " ";
}
cout << endl;
}
template<typename T>
//構造函數
SeqList<T>::SeqList()
:_data(new T[SIZE])
, _size(0)
, _capacity(SIZE)
{}
template<typename T>
//拷貝構造函數
SeqList<T>::SeqList(const SeqList& l)
{
_data = new T[l._size];
//int i = 0;
//for (i = 0; i < l._size; i++)//逐個拷貝
//{
// _data[i] = l._data[i];
//}
Copy(_data, l._data, l._size);
_size = l._size;
_capacity = _size;
}
template<typename T>
//賦值
SeqList<T> SeqList<T>:: operator=(SeqList l)
{
swap(_data, l._data);
_size = l._size;
_capacity = l._capacity;
return *this;
}
template<typename T>
//析構函數
SeqList<T>::~SeqList()
{
if (_data != NULL)
{
delete[] _data;
_data = NULL;
}
}
template<typename T>
void SeqList<T>::CheckCapacity()//檢測容量
{
if (_size == _capacity)//擴容
{
T* tmp = new T[_capacity + DEAFAULT];
//memcpy(tmp, _data, sizeof(T)*_size);淺拷貝,導致程序崩潰
//int i = 0;
//for (i = 0; i < _size; i++)//逐個拷貝
//{
// tmp[i] = _data[i];
//}
Copy(tmp, _data, _size);
delete[] _data;
_data = tmp;
_capacity += DEAFAULT;
}
}
template<typename T>
void SeqList<T>::PushBack(const T& d)
{
CheckCapacity();
_data[_size] = d;
_size++;
}
template<typename T>
void SeqList<T>::PopBack()
{
if (_size == 0)
{
return;
}
_size--;
}
template<typename T>
void SeqList<T>::PushFront(const T& d)
{
CheckCapacity();
int i = 0;
for (i = _size-1; i >= 0; i--)
{
_data[i + 1] = _data[i];
}
_data[0] = d;
_size++;
}
template<typename T>
void SeqList<T>::PopFront()
{
if (_size == 0)
{
return;
}
int i = 0;
for (i = 1; i < _size; i++)
{
_data[i - 1] = _data[i];
}
_size--;
}
template<typename T>
void SeqList<T>::SortList()
{
int i = _size;
bool flag = true;//標誌位,當順序表已經是有序的時候則不需要再排序提高效率
for (i = 1; i < _size && flag; i++)
{
int j = 0;
flag = false;
for (j = 0; j < _size - i; j++)
{
if (_data[j] > _data[j + 1])
{
flag = true;
swap(_data[j], _data[j + 1]);
}
}
}
}
template<typename T>
void SeqList<T>::Reverse()
{
int left = 0;
int right = _size;
while (left < right)
{
swap(_data[left++], _data[right--]);
}
}
template<typename T>
T* SeqList<T>::Find(const T& d)
{
int i = 0;
for (i = 0; i < _size; i++)
{
if (_data[i] == d)
{
return &_data[i];//找到返回地址
}
}
return NULL;//沒有找到
}
template<typename T>
void SeqList<T>::Insert(int pos, const T& x)
{
if (pos > 0 && pos <= _size)//判斷位置的正確性
{
CheckCapacity();
int i = 0;
for (i = _size - 1; i >pos-2; i--)
{
_data[i + 1] = _data[i];
}
_data[pos - 1] = x;
_size++;
}
}
template<typename T>
void SeqList<T>::Remove(T x)
{
int i = 0;
for (i = 0; i < _size; i++)
{
if (x == _data[i])
{
for (int j = i; j < _size-1; j++)
{
_data[j] = _data[j + 1];
}
_size--;
return;
}
}
}
template<typename T>
void SeqList<T>::RemoveAll(T x)
{
int i = 0;
for (i = 0; i < _size; i++)
{
if (x == _data[i])
{
Remove(x);
i--;//保留下標,刪除之後從當前繼續尋找,以免順序表遍歷漏掉。
}
}
}
void test1()
{
SeqList<string> l;
//l.PushBack(1);
//l.PushBack(2);
//l.PushBack(3);
//l.PushBack(4);
//l.Print();
//l.PopBack();
//l.Print();
//l.PopBack();
//l.Print();
//l.PopBack();
//l.Print();
l.PushFront("11111");
l.PushFront("22222");
//l.PushFront("333333333333333333333333333333333333");
l.PushFront("33333");
l.PushFront("44444");
//l.PushFront("11111");
l.Print();
cout << sizeof(string) << endl;
//cout << l.Find("11111") << endl;
////cout << l.Find(0) << endl;
//l.Insert(4, "00000");
//l.Print();
//l.RemoveAll("11111");
//l.Print();
//l.SortList();
////l.PopBack();
//l.Print();
//l.PopBack();
//l.Print();
//l.PopBack();
//l.Print();
}
int main()
{
test1();
getchar();
return 0;
}
當使用memcpy()時
測試代碼
l.PushFront("11111");
l.PushFront("22222");
//l.PushFront("333333333333333333333333333333333333");
l.PushFront("33333");
l.PushFront("44444");
結果可以正常運行
但是在字符串長度比較長時
l.PushFront("11111");
l.PushFront("22222");
l.PushFront("333333333333333333333333333333333333");
//l.PushFront("33333");
l.PushFront("44444");
程序崩潰