练习16.9:
函数模板:一个函数模板就是一个公式,可以用来生成针对特定类型的函数版本
类模板:类似函数模板,通过实例化生成特定的类
练习16.10:
编译器器会重写模板。将模板参数T的每个实例替换为给定的模板实参
练习16.11:
模板不是类类型,只有实例化后才能形成一个类型。代码中所有的ListItem模板都直接使用ListItem作为类型。应该将List模板类中所有的ListItem替换为ListItem<elemType>即可
练习16.12:
#include <iostream>
#include <vector>
#include <list>
using namespace std;
//声明,类模板与函数模板的声明
template<typename>class BlobPtr;
template<typename>class Blob;
template<typename T>
bool operator==(const Blob<T>&, const Blob<T>&);
template<typename T>class Blob {
//每个Blob实例将访问权限授予用相同类型实例化的BlobPtr和相等运算符
friend class BlobPtr<T>;
//template<typename X>friend class BlobPtr<T>;
friend bool operator==<T>(const Blob<T>&, const Blob<T>&);
//template<typename X>friend bool operator==<T>(const Blob<T>&, const Blob<T>&);
public:
typedef T value_type;
typedef typename std::vector<T>::size_type size_type;
Blob();
Blob(std::initializer_list<T>il);
size_type size()const { return data->size(); }
bool empty()const { return data->element_type(); }
void push_back(const T& t) { data->push_back(t); }
//移动版本
void push_back(T&& t) { data->push_back(std::move(t)); }
void pop_back();
//元素访问
T& back()const;
T& operator[](size_type i);
BlobPtr<T> begin() { return BlobPtr<T>(*this); }
BlobPtr<T> end()
{
auto ret = BlobPtr<T>(*this, data->size());
return ret;
}
T& front()const;
private:
std::shared_ptr<std::vector<T>>data;
//data[i]无效,则抛出msg
void check(size_type i, const std::string& msg)const;
};
template<typename T>
T& Blob<T>::front()const
{
check(0, "front on empty Blob");
return data->front();
}
//check
template<typename T>
void Blob<T>::check(size_type i, const std::string& msg)const
{
if (i >= data->size())
{
throw std::out_of_range(msg);
}
}
//back
template<typename T>
T& Blob<T>::back()const
{
check(0, "back on empty Blob");
return data->back();
}
//下标运算符
template<typename T>
T& Blob<T>::operator[](size_type i)
{
check(i, "subscript out of range");
return (*data)[i];
}
//pop函数
template<typename T>
void Blob<T>::pop_back()
{
check(0, "pop_back on empty Blob");
data->pop_back();
}
//构造函数
template<typename T>
Blob<T>::Blob() :data(std::make_shared<std::vector<T>>()) { }
//接受initializer_list的构造函数
template<typename T>
Blob<T>::Blob(std::initializer_list<T>il) : data(std::make_shared<std::vector<T>>(il)) { }
//若试图访问一个不存在的元素,BlobPtr抛出一个异常
template<typename T>class BlobPtr {
public:
BlobPtr() :curr(0) { }
BlobPtr(Blob<T>& a, size_t sz = 0) :wptr(a.data), curr(sz) { }
T& operator*()const
{
auto p = check(curr, "dereference past end");
return (*p)[curr];
}
//递增与递减
BlobPtr& operator++();
BlobPtr& operator--();
BlobPtr& operator++(int);
BlobPtr& operator--(int);
T& deref() const;
BlobPtr& incr();
private:
std::shared_ptr<std::vector<T>>check(std::size_t, const std::string&)const;
std::weak_ptr<std::vector<T>>wptr;
std::size_t curr;
};
template<typename T>
T& BlobPtr<T>:: deref()const
{
auto p = check(curr, "dereference past end");
return (*p)[curr];
}
template<typename T>
BlobPtr<T>& BlobPtr<T>::incr()
{
//递增curr,判断curr位于合理位置,不合理位置不递增
check(curr, "incrasement past end of BlobPtr");
++curr;
return *this;
}
template<typename T>
BlobPtr<T>& BlobPtr<T>::operator++(int)
{
BlobPtr ret = *this;
++* this;
return ret;
}
template<typename T>
BlobPtr<T>& BlobPtr<T>::operator--(int)
{
BlobPtr ret = *this;
--* this;
return ret;
}
template<typename T>
BlobPtr<T>& BlobPtr<T>::operator++()
{
check(curr, "increment past end of BlobPtr");
++curr;
return *this;
}
template<typename T>
BlobPtr<T>& BlobPtr<T>::operator--()
{
check(curr, "decrement past begin of BlobPtr");
++curr;
return *this;
}
int main()
{
system("pause");
return 0;
}
练习16.13:
选择一对一友好关系。BlobPtr实例化的类是Blob实例化的类的指针,所以BlobPtr实例化的类必须与Blob实例化的类具有相同的类型,否则指针类型与数据类型不一致。因此,选用一对一友好关系
练习16.14:
简易的Screen类为:
#include <iostream>
#include <vector>
#include <list>
using namespace std;
//简易的Screen类模板实现,使用非类型参数定义Screen的宽和高
template<unsigned A>class Screen {
public:
Screen() = default;
Screen(A h, A w) :height(h), width(w) { }
private:
A height;
A width;
};
int main()
{
system("pause");
return 0;
}
练习16.15:
代码为:
#include <iostream>
#include <vector>
#include <list>
using namespace std;
//简易的Screen类模板实现,使用非类型参数定义Screen的宽和高
//声明,类模板与函数模板的声明
template <unsigned T, unsigned N> class Screen;
template <unsigned T, unsigned N>
istream& operator>>(istream&, Screen<T, N>&);
template <unsigned T, unsigned N>
ostream& operator<<(ostream&, Screen<T, N>&);
template<unsigned T, unsigned N>class Screen {
//友元
friend istream& operator>><T, N>(istream&, Screen<T, N>&);
friend ostream& operator<<<T, N>(ostream&, Screen<T, N>&);
public:
Screen() = default;
Screen() :area(T* N) { }
private:
unsigned area;
};
template <unsigned T, unsigned N>
istream& operator>>(istream& is, Screen<T, N>& Scr)
{
string curr;
is >> curr;
Scr.area = curr;
return is;
}
template <unsigned T, unsigned N>
ostream& operator<<(ostream& os, Screen<T, N>& Scr)
{
os << Scr.area;
return os;
}
int main()
{
system("pause");
return 0;
}
练习16.16:
代码如下,相当于一个简易版的vector:
#include <iostream>
#include <string>
using namespace std;
#include <algorithm>
#include <set>
template<typename T>class Vec {
public:
//默认初始化
Vec() :elements(nullptr), first_free(nullptr), cap(nullptr) { };
//拷贝构造函数
Vec(const Vec&);
//拷贝赋值运算符
Vec& operator=(const Vec&);
Vec(initializer_list<T> );
//析构函数
~Vec();
//拷贝元素
void push_back(const T&);
//数组大小和容量
size_t size()const;
size_t capacity()const;
//获得数组的首元素和尾后元素
T* begin()const;
T* end()const;
void reserve(size_t n);
void resize(size_t n);
private:
//静态成员,用来分配元素
static std::allocator<T>alloc;
//工具函数,被添加元素的函数所使用
void chk_n_alloc()
{
if (size() == capacity()) reallocate();
}
//工具函数,被拷贝构造函数、赋值运算符和析构函数使用
std::pair<T*, T*>alloc_n_copy(const T*, const T*);
//销毁元素并释放内存
void free();
//获得更多内存并拷贝已有元素
void reallocate();
//指向数组首元素的指针
T* elements;
//指向数组第一个空闲元素的指针
T* first_free;
//指向数组尾后位置的指针
T* cap;
};
template<typename T>
std::allocator<T> Vec<T>::alloc;
//工具函数,被拷贝构造函数、赋值运算符和析构函数使用
template<typename T>
pair<T*, T*> Vec<T>::alloc_n_copy(const T* t1, const T* t2)
{
//拷贝传递参数指定范围的元素
//创建元素的副本
auto data = alloc.allocate(t2 - t1);
//uninitialized_copy(b,e,b2)作用是从迭代器b和e指出的输出范围中拷贝元素到迭代器b2指定的未构造的原始内存中
// 使用uninitialized_copy(b,e,b2)函数对未构造的函数进行拷贝
//返回一个pair,分别指向拷贝后的数组首元素和最后元素后面的元素
return { data,uninitialized_copy(t1,t2,data) };
}
//工具函数free,destory元素,释放StrVec内存
template<typename T>
void Vec<T>::free()
{
//不能传递给deallocate一个空指针,如果elements为空,函数就什么也不做
if (elements)
{
//逆序销毁元素,收回空间
for (T* p = first_free; p != elements;)
{
alloc.destroy(--p);
}
alloc.deallocate(elements, cap - elements);
}
}
//reallocate成员
template<typename T>
void Vec<T>::reallocate()
{
//分配当前大小两倍的内存空间
auto newCapacity = size() ? 2 * size() : 1;
//分配新内存
auto newData = alloc.allocate(newCapacity);
//将数据从旧内存移到新内存
//指向数组下一个空闲位置
auto dest = newData;
//指向旧数组中下一个元素
auto elem = elements;
for (size_t i = 0; i != size(); ++i)
{
alloc.construct(dest++, std::move(*elem++));
}
//一旦我们移动完元素就释放旧内存空间
free();
//更新我们的数据结构,执行新元素
elements = newData;
first_free = dest;
cap = elements + newCapacity;
}
template<typename T>
Vec<T>::Vec(initializer_list<T> ls)
{
auto newdata = alloc_n_copy(ls.begin(), ls.end());
elements = newdata.first;
first_free = cap = newdata.second;
}
//拷贝构造函数
template<typename T>
Vec<T>::Vec(const Vec& sv)
{
//调用alloc_n_copy分配空间以容纳与sv中一样多的元素
auto newData = alloc_n_copy(sv.begin(), sv.end());
elements = newData.first;
first_free = cap = newData.second;
}
//拷贝赋值运算符
template<typename T>
Vec<T>& Vec<T>::operator=(const Vec& sv)
{
//为了正确处理自赋值,在释放已有元素前调用alloc_n_copy
//分配内存,大小和sv中占用的元素一样多
auto data = alloc_n_copy(sv.begin(), sv.end());
free();
elements = data.first;
first_free = cap = data.second;
return *this;
}
template<typename T>
size_t Vec<T>::capacity()const
{
return cap - elements;
}
template<typename T>
size_t Vec<T>::size()const
{
return first_free - elements;
}
template<typename T>
T* Vec<T>::begin()const
{
return elements;
}
template<typename T>
T* Vec<T>::end()const
{
return first_free;
}
//push_back
template<typename T>
void Vec<T>::push_back(const T& t)
{
chk_n_alloc();
//此时有足够的空间,创建元素,拷贝
alloc.construct(first_free++, t);
}
//析构函数
template<typename T>
Vec<T>::~Vec()
{
free();
}
template<typename T>
void Vec<T>::reserve(size_t n)
{
//分配大小为n的内存
if (capacity() < n)
{
auto newCapacity = n;
//创建新内存
auto newData = alloc.allocate(n);
//将数据从旧内存移到新内存
//指向数组下一个空闲位置
auto dest = newData;
//指向旧数组中下一个元素
auto elem = elements;
//将数据移动
for (size_t i = 0; i != size(); ++i)
{
alloc.construct(dest++, std::move(*elem));
}
//一旦我们移动完元素就释放旧内存空间
free();
//更新我们的数据结构,执行新元素
elements = newData;
first_free = dest;
cap = elements + newCapacity;
}
}
template<typename T>
void Vec<T>::resize(size_t n)
{
//n的大小大于size(),以默认值填充
if (size() < n)
{
size_t num = n - size();
for (size_t i = 0; i != num; ++i)
{
alloc.construct(first_free++, std::string());
}
}
//n的大小小于size(),删除
if (size() > n)
{
//从n开始destory内存
for (auto p = elements + n; p != first_free; ++p)
{
alloc.destroy(p);
}
//更新first_free
first_free = elements + n;
}
}
int main()
{
Vec<int>v1 = { 0,1,2,3,4,5,6,7,8,9 };
//打印
v1.push_back(10);
for (auto it = v1.begin(); it != v1.end(); ++it)
{
cout << *it << " ";
}
cout << endl;
Vec<string>v2 = { "hello","world","lucky","man","C++" };
v2.push_back("niubi");
for (auto it = v2.begin(); it != v2.end(); ++it)
{
cout << *it << " ";
}
cout << endl;
system("pause");
return 0;
}
测试结果如下: