由上一篇的文章中 ,我们已经知道,模板是一个代码生成器,可以极大效率提高我们的工作效率,实现让编译器为我们产生代码的机制
在C++面向对象中,我们在对类的使用上,已经达到了最少80%了,因此,模板类的使用,将极大的提高程序的运行效率,节省程序的运行时间。
(一)概念区分
类模板:也称类属类或类生产类,允许用户为类定义一种模式,使得类中的某些数据成员,默写成员函数的参数,某些成员参数的返回值,能够取任意类型(包括系统预定义和用户自定义),如果一个类中数据成员的数据类型不能确定,或者是某个成员函数的参数返回值的类型不能确定,就必须将此类声明为模板,他的存在不是代表一个具体的,实际的类,而是代表一类类。
类模板由三种类型的模板参数:类型模板参数,非类型模板参数和模板的模板参数。
模板类:是类模板实例化的的一个产物,可以从类模板派生出新的类,既可以派生类模板,也可以派生非类模板,模板类的重点是类表示由一个模板生成而来的类,模板类与平台无关,具有可移植性,可用于基本数据类型,可用来创建动态增长和减小的数据结构。
模板类的实例化:只要有一种不同的类型,编译器就会实例化出一个对应的类。
(二)有关Vector模板的实现
Vector:是用来连续的存储数据的,类似于顺序表,在其中任意位置的插入或者删除的效率比较低,但是对内存碎片存在的就比较少,在某些情况下,对内存的使用率比较好。
#define _CRT_SECURE_NO_WARNINGS 1
//注:memcpy会出现浅拷贝的问题,因此在拷贝元素时,应该慎用
#define _CRT_SECURE_NO_WARNINGS 1
#include
#include
#include
using namespace std;
template
class Vector
{
public:
Vector()//构造函数,顺序表为空
:_start(0)
, _finish(0)
, _endOfStorage(0)
{}
Vector(const T*arr, size_t size);//声明,顺序表中已经存放元素
Vector(const Vector& t) //拷贝构造函数
{
size_t size = t.Size();
_start = new T[size];
for (size_t i = 0; i < size; ++i)
{
_start[i] = t._start[i];
}
_finish = _start + size;
_endOfStorage = _finish;
}
Vector&operator=(const Vector&v)//赋值运算符的重载
{
if (this != &v)
{
size_t size = v.Size();
_start = new T[size];
for (size_t i = 0; i < size; ++i)
{
_start[i] = v._start[i];
}
}
return*this;
}
void PushBack(const T& data)//尾插,注意在此处,const修饰的是data
{
CheckCapacity();
*_finish++ = data;//改变_finish的位置,从而将数据插入在上面
}
void PopBack()//尾删,直接改变顺序表中_finish的位置
{
--_finish;
}
void Insert(size_t pos, const T& data)//任意位置的插入
{
//检查pos是否合法
assert(pos < Size());//检查当前插入的位置是否有效
CheckCapacity();
CheckCapacity();
size_t i = 0;
//搬移元素
for (i = Size(); i > pos; --i)
{
_start[i] = _start[i - 1];
}
_start[pos] = data;
++_finish;
}
void Erase(size_t pos)//任意位置删除
{
assert(pos < Size());
size_t i = 0;
for (i = pos; i < Size() - 1; ++i)//减1的原因是:防止拿到最大元素处的后一个元素
{
_start[i] = _start[i + 1];
}
--_finish;
}
size_t Size()const//元素的个数
{
return _finish - _start;
}
size_t Capacity()const//容量
{
return _endOfStorage - _start;
}
bool Empty()const//判断顺序表是否为空
{
return (Size() == 0);//_start==_finish;
}
void Resize(size_t newSize, const T& data = T())//将顺序表中的size改变到newSize
{
size_t OldSize = Size();//将顺序表中的size做一个记录,从而方便下面的比较
size_t capacity = Capacity();
if (newSize < OldSize)
{
_finish = _start + newSize;//改变里面存储空间的大小
}
else if ((OldSize < newSize) && (newSize < capacity))
{
for (size_t i = OldSize; i < newSize; ++i)//将需要拷贝的数据拷贝下来
{
_start[i] = data;
}
_finish = _start + newSize;
}
else//此种情况为,新的顺序表的大小,比原来顺序表中的空间都大,因此需要开辟新的空间
{
T*tmp = new T[newSize];
for (size_t i = 0; i < OldSize; ++i)//先拷贝旧空间的元素
{
tmp[i] = _start[i];
}
for (size_t j = OldSize; j < newSize; ++j)//将新的元素填充剩下的这段空间
{
tmp[j] = data;
}
delete[]_start;
_start = tmp;
_finish = _start + newSize;
_endOfStorage = _start + newSize;
}
}
T& operator[](size_t index)//下标运算符重载(成对出现)随机访问
{
return _start[index];
}
const T& operator[](size_t index)const
{
return _start[index];
}
T& Front()//返回最开始的元素
{
return *_start;//_start表示指向首元素的指针,对其解引用表示取出首地址的内容
}
const T& Front()const
{
return *_start;
}
T& Back()
{
return *(_finish - 1);//同Front
}
const T& Back()const
{
return *(_finish - 1);
}
void Clear()//置空顺序表
{
_finish = _start;
}
template//不是类的成员函数
friend ostream&operator<<(ostream&_cout, const Vector&t)//输出运算符重载
{
for (size_t i = 0; i < t.Size(); ++i)
{
_cout << t[i] << " ";
}
//memcpy(_Start, t._start, sizeof(t));
return _cout;
}
void Display()
{
size_t size = Size();
for (size_t i = 0; i < size; ++i)
{
cout << _start[i];
cout << " ";
}
cout << endl;
}
~Vector()//析构函数
{
if (_start != NULL)
{
delete[]_start;
_start = NULL;
_finish = NULL;
_endOfStorage = NULL;
}
}
private:
void CheckCapacity()
{
size_t size = Size();
size_t capacity = Capacity();
if (size >= capacity)
{
size_t newCapacity = capacity * 2 + 3;
//申请新空间
T*_tmp = new T[newCapacity];
//拷贝元素
for (size_t i = 0; i < size; ++i)
{
_tmp[i] = _start[i];
}
//释放旧空间
if (_start)//防止释放的空间为空,从而导致程序崩溃
delete[] _start;
_start = _tmp;
_finish = _start + size;
_endOfStorage = _start + newCapacity;
}
}
private:
T*_start; //表示顺序的开始,空间起始位置
T*_finish; //表示顺序表中存储元素的多少
T*_endOfStorage; //表示顺序表的容量
};
template
Vector::Vector(const T*arr, size_t size)//有元素,1、开辟大小 2、复制数据
:_start(new T[size])
, _finish(_start + size)
, _endOfStorage(_start + size)
{
//memcpy(_start, arr, sizeof(T)*size)//会存在浅拷贝的问题
for (size_t i = 0; i < size; ++i)//拷贝元素
{
_start[i] = arr[i];
}
}
class String
{
public:
String(const char* pStr = "")
{
if (_pStr == NULL)
{
_pStr = new char[1];
(*_pStr) = '\0';
}
else
{
_pStr = new char[strlen(pStr) + 1];
strcpy(_pStr, pStr);
}
}
/*String(const String &s)//拷贝构造函数
:_pStr(NULL)
{
String tmp(s._pStr);
swap(_pStr, tmp);
}*/
String(const String &s)
:_pStr(new char[strlen(s._pStr) + 1])
{
strcpy(_pStr, s._pStr);
}
String& operator=(const String&s)
{
if (this != &s)//检测是不是自己给自己赋值
{
char*tmp = new char[strlen(s._pStr) + 1];
strcpy(tmp, s._pStr);
delete[] _pStr;
_pStr = NULL;
_pStr = tmp;
}
return *this;
}
friend ostream&operator<<(ostream&_cout, const String&t)//输出运算符重载
{
_cout << t._pStr;
return _cout;
}
~String()
{
if (_pStr != NULL)
{
delete[] _pStr;
_pStr = NULL;
}
}
private:
char*_pStr;
};
#if 0
void FunTest1()
{
//Vector s1;
int arr[] = { 1, 2, 3, 4 };
Vector s2(arr, sizeof(arr) / sizeof(arr[0]));
//Vector s3(s2);
//cout << s2 << endl;
//s1 = s2;
//cout << s1 << endl;
cout << "size= " << s2.Size() << endl;
cout << "capacity= " << s2.Capacity() << endl;
cout << s2 << endl;
s2.Display();
s2.PushBack(5);
s2.PushBack(6);
s2.PushBack(7);
cout << "size= " << s2.Size() << endl;
cout << "capacity= " << s2.Capacity() << endl;
cout << s2 << endl;
s2.Display();
s2.PopBack();
s2.PopBack();
cout << "size= " << s2.Size() << endl;
cout << "capacity= " << s2.Capacity() << endl;
cout << s2 << endl;
s2.Display();
s2.Erase(2);
cout << "size= " << s2.Size() << endl;
cout << "capacity= " << s2.Capacity() << endl;
s2.Insert(3,7);
cout << "size= " << s2.Size() << endl;
cout << "capacity= " << s2.Capacity() << endl;
cout << s2 << endl;
s2.Display();
s2.Resize(4);
cout << "size= " << s2.Size() << endl;
cout << "capacity= " << s2.Capacity() << endl;
s2.Display();
s2.Resize(30);
cout << "size= " << s2.Size() << endl;
cout << "capacity= " << s2.Capacity() << endl;
cout << s2 << endl;
s2.Display();
}
#endif
void FunTestString()
{
Vector s;
s.PushBack("1111");
s.PushBack("2222");
s.PushBack("3333");
cout << "size= " << s.Size() << endl;
cout << "capacity= " << s.Capacity() << endl;
cout << s << endl;
s.PushBack("4444");
cout << "size= " << s.Size() << endl;
cout << "capacity= " << s.Capacity() << endl;
cout << s << endl;
s.PopBack();
cout << "size= " << s.Size() << endl;
cout << "capacity= " << s.Capacity() << endl;
cout << s << endl;
s.Insert(2, "5555");
cout << "size= " << s.Size() << endl;
cout << "capacity= " << s.Capacity() << endl;
cout << s << endl;
s.Resize(4);
cout << "size= " << s.Size() << endl;
cout << "capacity= " << s.Capacity() << endl;
cout << s << endl;
s.Resize(20,"0000");
cout << "size= " << s.Size() << endl;
cout << "capacity= " << s.Capacity() << endl;
cout << s << endl;
s.Clear();
cout << "size= " << s.Size() << endl;
cout << "capacity= " << s.Capacity() << endl;
cout << s << endl;
}
int main()
{
FunTestString();
system("pause");
return 0;
}
由上面可以看出,由于在模板中对于输出运算符的重载,存在着一定的问题,因此上面对于String类型与内置类型分别测试。
结果如下:
String类型