函数模板提供了一种机制通过它我们可以保留函数定义和函数调用的语义在一个程序位置上封装了一段代码确保在函数调用之前实参只被计算一次.
函数模板提供一个种用来自动生成各种类型函数实例的算法程序员对于函数接口参数和返回类型中的全部或者部分类型进行参数化(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");
程序崩溃