C++Priemr16.1.2节练习

练习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;
}






测试结果如下:

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小白学C++.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值