函数模板
模板的意义:对类型也可以进行参数化
#include
using namespace std;
template
bool compare(T a, T b)
{
return a > b;
}
int main()
{
compare(10, 20);
return 0;
}
在函数调用点,编译器用用户指定的类型,从原模版实例化一份函数代码出来。
当用户没有指定类型时,模板会进行实参推演,可以根据用户传入的实参的类型,来推导出模板类型参数的具体类型
函数模板不进行编译,模板函数才是要进行编译的
当对于某些类型来说,依赖编译器默认实例化的模板代码,代码处理逻辑是错误时,就要进行模板特例化(特殊的实例化,不是编译器提供的,而是用户提供的)
下面将针对compare函数模板,提供const char *类型的特例化版本
#include
using namespace std;
template
bool compare(T a, T b)
{
return a > b;
}
template <>
bool compare(const char* a, const char* b)
{
cout << "const cha *" << endl;
return strcmp(a, b);
}
int main()
{
compare(10, 20);
compare("aaa", "bbb");
return 0;
}
编译器会优先把compare处理成函数,没有再去找模板
#include
using namespace std;
template
bool compare(T a, T b)
{
return a > b;
}
template <>
bool compare(const char* a, const char* b)
{
cout << "const cha *" << endl;
return strcmp(a, b);
}
bool compare(const char* a, const char* b)
{
cout << "const cha " << endl;
return strcmp(a, b);
}
int main()
{
compare(10, 20);
compare("aaa", "bbb");
compare("aaa", "bbb");
return 0;
}
模板代码不能在一个文件定义,在另一个文件中进行使用的
模板代码调用之前,一定要看到模板被定义的地方,这样的话,模板才能够进行正常的实例化,产生能够被编译器编译的代码
所以,模板代码都是放在头文件中,然后再源文件当中直接进行#include包含
类模板
模板的非类型参数 必须是整数类型(整数或者地址/引用)都是常量,只能使用不能修改
#include
using namespace std;
template
void sort(T* arr)
{
for (int i = 0; i < SIZE -1; ++i)
{
for (int y = 0; y < SIZE - i - 1; ++y)
{
if (arr[y] > arr[y + 1])
{
int temp = arr[y];
arr[y] = arr[y+1];
arr[y + 1] = temp;
}
}
}
}
int main()
{
int arr[] = { 35,25,48,69,20,10,3 };
int size = 7;
sort(arr);
for (int val : arr)
{
cout << val << " ";
}
cout << endl;
return 0;
}
类模板
模板函数+类型参数列表 = 类名称
构造和析构函数不用加
#include
using namespace std;
template
class SetStack
{
public:
SetStack(int size = 10)
:_stack(new int[10])
, _top(0)
,_size(size)
{}
~SetStack()
{
delete[]_stack;
_stack = nullptr;
}
SetStack(const SetStack& other)
:_top(other._top)
,_size(other._size)
{
_stack = new T[_size];
for (int i = 0; i < _top; ++i)
{
_stack[i] = other._stack[i];
}
}
SetStack& operator=(const SetStack& other)
{
if (this == &other)
{
return *this;
}
delete[]_stack;
_top = other._top;
_size = other._size;
_stack = new T[_size];
for (int i = 0; i < _top; ++i)
{
_stack[i] = other._stack[i];
}
return *this;
}
void push(int val)
{
if (full())
{
expend();
}
_stack[_top++] = val;
}
void pop()
{
if (empty())
{
return;
}
--_top;
}
T top() const
{
if (empty())
{
throw "stack is empty!";
}
return _stack[_top - 1];
}
bool full() const
{
return _top == _size;
}
bool empty() const
{
return _top == 0;
}
private:
T* _stack;
int _top;
int _size;
void expend()
{
T* temp = new T[_size * 2];
for (int i = 0; i < _top; ++i)
{
temp[i] = _stack[i];
}
delete[]_stack;
_stack = temp;
_size *= 2;
}
};
int main()
{
SetStack s1;
s1.push(20);
s1.push(30);
s1.push(80);
s1.push(50);
cout << s1.top() << endl;;
s1.pop();
cout << s1.top() << endl;;
return 0;
}
实现C++STL向量容器vector
以下是没有加空间配置器allocator
#include
using namespace std;
template
class vector
{
public:
vector(int size = 10)
:_first(new T[size])
,_last(_first)
,_end(_first+size)
{
}
~vector()
{
delete[]_first;
_first = _last = _end = nullptr;
}
vector(const vector & other)
{
_first = new T[other._end - other._first];
for (int i = 0; i < (other._last - other._first); i++)
{
_first[i] = other._first[i];
}
_last = _first + other._last - other._first;
_end = _first + other._end - other._first;
}
vector& operator=(const vector& other)
{
if (this == &other)
{
return *this;
}
delete[]_first;
_first = new T[other._end - other._first];
for (int i = 0; i < (other._last - other._first); i++)
{
_first[i] = other._first[i];
}
_last = _first + other._last - other._first;
_end = _first + other._end - other._first;
return *this;
}
void push_back(T val)
{
if (full())
{
expand();
}
*_last++ = val;
}
void pop_back()
{
if (empty())
{
return;
}
--_last;
}
T back() const
{
if (empty())
{
throw "vector is empty";
}
return *(_last - 1);
}
bool full() const
{
return _last == _end;
}
bool empty() const
{
return _first == _last;
}
int size() const
{
return _last - _first;
}
private:
T* _first;
T* _last;
T* _end;
void expand()
{
int size = _end - _first;
T* temp = new T[2 * size];
for (int i = 0; i < size; i++)
{
temp[i] = _first[i];
}
delete[]_first;
_first = temp;
_end = _first + size*2;
_last = _first + size;
}
};
int main()
{
vector vec;
for (int i = 0; i < 20; i++)
{
vec.push_back(rand() % 100);
}
while (!vec.empty())
{
cout << vec.back() << " ";
vec.pop_back();
}
cout << endl;
return 0;
}
理解空间配置器allocator的重要性
class test
{
public:
test()
{
cout << "test()" << endl;
}
~test()
{
cout << "~test()" << endl;
}
};
int main()
{
vector vec;
return 0;
}
当我们用没有自己写的没有空间配置器的vector,按照上述代码,会根据vector构造函数的参数给我们构造十个test对象,这显然是不合理的,并不是我们想要得,我们需要把开辟空间和构造对象分开,析构函数析构有效元素(而不是释放每个元素)再开辟释放内存空间,并且pop_back也需要析构对象而不释放内存,所以要把析构对象和释放内存分开
空间配置器allocator只做四件事 空间开辟/空间释放 对象构造/对象析构
下面是加了空间配置器allocator的自定义vector
#include
using namespace std;
template
struct Allocator
{
T* allocate(size_t size)
{
return (T*)malloc(sizeof(T) * size);
}
void deallocate(void* p)
{
free(p);
}
void construct(T* p, const T& val)
{
new (p) T(val); //定位new
}
void destroy(T* p)
{
p->~T();
}
};
template>
class vector //容器底层的空间开辟,空间释放,对象构造,对象析构都通过allocator空间配置器
{
public:
vector(int size = 10)
//:_first(new T[size])
:_first(_allocator.allocate(size))
,_last(_first)
,_end(_first+size)
{
}
~vector()
{
//delete[]_first;
for (T* p = _first; p != _last; ++p)
{
_allocator.destroy(p); //析构
}
_allocator.deallocate(_first); //释放内存
_first = _last = _end = nullptr;
}
vector(const vector & other)
{
//_first = new T[other._end - other._first];
_first = _allocator.allocate(other._end - other._first);
for (int i = 0; i < (other._last - other._first); i++)
{
//_first[i] = other._first[i];
_allocator.construct(_first+i, other._first[i]);
}
_last = _first + other._last - other._first;
_end = _first + other._end - other._first;
}
vector& operator=(const vector& other)
{
if (this == &other)
{
return *this;
}
//delete[]_first;
for (T* p = _first; p != _last; ++p)
{
_allocator.destroy(p); //析构
}
_allocator.deallocate(_first); //释放内存
//_first = new T[other._end - other._first];
_first = _allocator.allocate(other._end - other._first);
for (int i = 0; i < (other._last - other._first); i++)
{
//_first[i] = other._first[i];
_allocator.construct(_first + i, other._first[i]);
}
_last = _first + other._last - other._first;
_end = _first + other._end - other._first;
return *this;
}
void push_back(T val)
{
if (full())
{
expand();
}
//*_last++ = val;
_allocator.construct(_last, val);
_last++;
}
void pop_back()
{
if (empty())
{
return;
}
//--_last;
--_last;
_allocator.deallocate(_last);
}
T back() const
{
if (empty())
{
throw "vector is empty";
}
return *(_last - 1);
}
bool full() const
{
return _last == _end;
}
bool empty() const
{
return _first == _last;
}
int size() const
{
return _last - _first;
}
private:
T* _first;
T* _last;
T* _end;
Alloc _allocator;
void expand()
{
int size = _end - _first;
//T* temp = new T[2 * size];
T* temp = _allocator.allocate(2*size);
for (int i = 0; i < size; i++)
{
_allocator.construct(temp+i, _first[i]);
//temp[i] = _first[i];
}
//delete[]_first;
for (T* p = _first; p != _last; ++p)
{
_allocator.destroy(p); //析构
}
_allocator.deallocate(_first); //释放内存
_first = temp;
_end = _first + size*2;
_last = _first + size;
}
};
class test
{
public:
test()
{
cout << "test()" << endl;
}
~test()
{
cout << "~test()" << endl;
}
};
int main()
{
vector vec;
test t1;
vec.push_back(t1);
return 0;
}