16.1节练习

练习16.1 给出实例化的定义。

编译器用推断出的模版参数来为我们实例化一个特定版本的函数。


练习16.2 编写并测试你自己版本的compare函数。

#include <iostream>
using namespace std;
template <typename T>
int compare(const T &v1, const T &v2)
{
	if (v1 < v2) { return -1; }
	if (v2 < v1) { return 1; }
}
int main()
{
	int x = 2, y = 1;
	cout << compare(x, y) << endl;
}

练习16.3 对两个Sales_data对象调用你的compare函数,观察编译器在实例化过程中如何处理错误。

编译器找不到<运算符。


练习16.4 编写行为类似标准库find算法的模板。函数需要两个模板类型参数,一个表示函数的迭代器参数,另一个表示值得类型。使用你的函数在一个vector<int>和一个list<string>中查找给定值。

#include <iostream>
#include <iterator>
#include <string>
#include <vector>
#include <list>
using namespace std;
template <typename iterator,typename T>
iterator findd(const iterator &begin, const iterator &end, const T &val)
{
	for (iterator iter = begin; iter != end; ++iter) {
		if (*iter == val) {
			return iter;
		}
	}
	return end;
}
int main()
{
	
	vector<int> ivec{ 1,3,15,24 };
	string s1("rose");
	string s2("paul");
	list<string> slist{s1,s2 };
	
	auto iter = findd(ivec.begin(), ivec.end(), 3);
	auto ster = findd(slist.begin(), slist.end(), "paul");
	cout << *iter << endl;
	cout << *ster << endl;
}

练习16.5 为6.2.4节(第195页)中的print函数编写模板类型,它接受一个数组的引用,能处理任意大小、任意元素类型的数组。

#include <iostream>
using namespace std;
template <typename T,std::size_t num>
void print(const T (&arr)[num])
{
	for (auto &i : arr) {
		cout << i << " ";
	}
	cout << endl;
}
int main()
{
	int val[] = { 1,2,3,4,5,6,7,9,7,8 };
	print(val);
	
	char* s[] = { "ss","dd" };
	print(s);
}


练习16.6 你认为接受一个数组实参的标准库函数begin和end是如何工作的,定义你自己版本的begin和end。

#include <iostream>
#include <iterator>
using namespace std;
template <typename T, std::size_t num>
T* beginn( T(&arr)[num]) 
{
	cout << "useing begin" << endl;
	return arr; 
}
template <typename T, std::size_t num>
T* endd( T(&arr)[num])
{
	cout << "using end" << endl;
	return arr + num;
}
int main()
{
	int val[] = { 1,2,3,4,5,6,7,8,9 };
	cout << *beginn(val) << endl;
	cout << *(endd(val)-1) << endl;
}

练习16.7 编写一个constexpr模板,返回给定数组的大小。

#include <iostream>
using namespace std;
template <typename T, std::size_t num>
constexpr std::size_t coutt(T(&arr)[num])
{
	return num;
}
int main()
{
	int val[] = { 1,2,4,3,4,6 };
	cout << coutt(val) << endl;
}

练习16.8 在第97页的“关键概念”中,我们注意到,C++程序员习喜欢使用!=而不喜欢<。解释这个习惯的原因。

在面向对象编程的时候,类不一定定义了<运算符,如Sales_Data类,而使用!=运算符更兼容,更易理解。


练习16.9 什么是函数模板,什么是类模板?

我们可以定义一个通用的函数模板,而不是为每个类型都定义一个新函数,一个函数模板就是一个公式,可以用来生成针对特定类型的函数版本。

类模板是用来生成类的蓝图。编译器不能为类模板推断模板参数类型,为了使用类模板,我们必须在模板名后的尖括号中提供额外信息——用来代替模板参数的模板实参列表。


练习16.10 当一个类模板被实例化时,会发生什么?

一个类模板的每个实例都能形成一个独立的类。其它的不同的实例化对其没有特殊的访问权限。


练习16.11 改正下面LIst的定义。

给出修改后的版本:

template <typename elemType> class ListItem;  
template <typename elemType> class List{  
public:  
  List<elemType>();  
  List<elemType>(const List<elemType> &);  
  List<elemType>& operator=(const List<elemType> &);  
  ~List();  
  void insert(ListItem<elemType> *ptr, elemType value);  
private:  
  ListItem<elemType> *front, *end;  
}; 
List<elemType>在类作用域中可以省略为List。


练习16.12 编写你自己版本的Blob和BlobPtr模板,包含书中未定义的多个const成员。

#include <iostream>
#include <string>
#include <vector>
#include <memory>
#include <initializer_list>
using namespace std;
template <typename T> class Blob {
public:
	typedef T value_type;
	typedef typename vector<T>::size_type size_type;
	Blob() :data(make_shared<vector<T>>()) {}
	Blob(initializer_list<T> il) :data(make_shared<vector<T>>(il)) {}
	size_type size() const { return data->size(); }
	bool empty() const { return data->empty(); }
	void push_back(T &&t) { data->push_back(std::move(t)); }
	void pop_back();
	T& back();
	T& operator[](size_type i);
private:
	shared_ptr<vector<T>> data;
	void check(size_type i, const string &msg) const;
};
template <typename T>
void Blob<T>::check(size_type i, const string &msg) const
{
	if (i >= data->size()) {
		throw out_of_range(msg);
	}
}
template <typename T>
T& Blob<T>::back()
{
	check(0, "back on emppty Blob");
	return data->back();
}
template <typename T>
T& Blob<T>::operator[](size_type i)
{
	check(i, "subscript out of range");
	return (*data)[i];
}
template <typename T>
void Blob<T>::pop_back()
{
	check(0, "pop_back on empty Blob");
	data->pop_back();
}

template <typename T> class BlobPtr {
	friend bool operator==(const BlobPtr<T>&, const BlobPtr<T>&);
public:
	BlobPtr() :curr(0) {}
	BlobPtr(Blob<T> &a, size_t sz = 0) :wptr(a.data), curr(sz) {}
	T& operator*()const;
	BlobPtr& operator++();
	BlobPtr& operator--();
private:
	shared_ptr<vector<T>> check(size_t, const string&)const;
	weak_ptr<vector<T>> wptr;
	size_t curr;
};
template<typename T>
shared_ptr<vector<T>> BlobPtr<T>::check(size_t i, const string &msg) const
{
	auto ret = wptr.lock();
	if (!ret) {
		throw runtime_error("unbound BlobPtr");
	}
	if (i >= wptr->size()) {
		throw out_of_range(msg);
	}
	return ret;

}
template<typename T>
T& BlobPtr<T>::operator* ()const
{
	auto p = check(curr, "dereference past end");
	return (*p)[curr];
}
template <typename T>
BlobPtr<T>& BlobPtr<T>::operator++()
{
	check(curr, "incremnet past end of BlobPtr");
	++curr;
	return *this;
}
template <typename T>
BlobPtr<T>& BlobPtr<T>::operator--()
{
	--curr;
	check(curr, "decrement past begin of BlobPtr");
	return *this;
}
template<typename T>
operator ==(const BlobPtr<T> &lhs, const BlobPtr<T> &rhs)
{
	return lhs.wptr == rhs.wptr&&lhs.curr == rhs.curr;
}


练习16.13 解释你为BlobPtr的相等和关系运算符选择哪种类型的友好关系?

与实例化一致的类型。


练习16.14 编写Screen类模板,用非类型参数定义Screen的高和宽。

#include <iostream>
#include <vector>
#include <string>
using namespace std;
template <typename T> class Screen {
	friend istream& operator >> (istream&, Screen<T>&);
	friend ostream& operator << (ostream&, const Screen<T>&);
public:
	Screen() = default;
	Screen(T h, T w) :height(h), width(w) {}
	Screen(T h, T w, char c) :height(h), width(w), contents(w*h, c) {}

	char get() const { return contents[currsor]; }
	char get(T h, T w)const { T row = h*w; return contents[row + w]; }
	Screen& move(T, T);
	Screen& set(char);
	Screen& set(T, T, char);
	Screen& display(ostream &os) { do_display(os); return *this; }
	const Screen& display(ostream &os)const { do_display(os); return *this; }
private:
	T hegiht;
	T width;
	T currsor;
	std::string contents;
	void do_display(ostream &os)const { os << contents; }
};
template <typename T>
Screen<T>& Screen<T>::move(T r, T c)
{
	T row = r*c;
	currsor = row + c;
	return *this;
}
template <typename T>
Screen<T>& Screen<T>::set(char c)
{
	contents[currsor] = c;
	return *this;
}
template <typename T>
Screen<T>& Screen<T>::set(T r, T c, char ch)
{
	contents[r*c + c] = ch;
	return *this;
}
template <typename T>
istream& operator >> (istream &is, Screen<T> &item)
{
	is >> item.hegiht >> item.width;
	if (!is) {
		item = Screen();
	}
	return is;
}
template <typename T>
ostream& operator >> (ostream &os, const Screen<T>& item)
{
	os << item.hegiht << " " << item.width << " " << item.contents;
	return os;
}

练习16.15 如上题。


练习16.16 将StrVec类(参见465页)重写为模板,命名为Vec。

#include <iostream>
#include <string>
#include <algorithm>
#include <initializer_list>
using namespace std;
template<typename T> class Vec {
public:
	Vec() :elements(nullptr), first_free(nullptr), cap(nullptr) {}
	Vec(const Vec&);
	Vec &operator=(const Vec&);
	~Vec();
	void push_back(const T&);
	size_t size() const { return first_free - elements; }
	size_t capacity() const { return cap - elements; }
	T *begin()const { return elements; }
	T *end()const { return first_free; }
private:
	static allocator<T> alloc;
	void chk_n_alloc() { if (size() == capacity()) reallocate(); }
	pair<T*, T*> alloc_n_copy(const T*, const T*);
	void free();
	void reallocate();
	T *elements;
	T *first_free;
	T *cap;
};
template <typename T>
void Vec<T>::free()
{
	if (elements) {
		for (auto p = first_free; p != elements;) {
			alloc.deallocate(elements, cap - elements);
		}
	}
}
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>
pair<T*, T*> Vec<T>::alloc_n_copy(const T *b, const T *e)
{
	auto data = alloc.allocate(e - b);
	return{ data,uninitialized_copy(b,e,data) };
}
template <typename T>
Vec<T>::Vec<T>(const Vec<T> &item)
{
	auto newdata = alloc_n_copy(item.begin(), item.end());
	elements = newdata.first;
	first_free = cap = newdata.second;
}
template <typename T>
Vec<T>& Vec<T>::operator=(const Vec<T> &item)
{
	auto data = alloc_n_copy(item.begin(), item.end());
	free();
	elements = data.first;
	first_free = cap = data.second;
	return *this;
}
template <typename T>
Vec<T>::~Vec()
{
	free();
}
template <typename T>
void Vec<T>::push_back(const T &s)
{
	chk_n_alloc();
	alloc.construct(first_free++, s);
}

练习16.17 声明为typename的类型和声明为class的类型参数有什么不同(如果有的话)?什么时候必须使用typename?

没有什么不同。

在区别对象为类型或者是数据成员时候用typename,C++默认情况假定通过作用域访问符访问的名字不是类型。

当我们希望通知编译器一个名字表示类型时,必须使用关键字typename,而不能使用class。


练习16.18 解释下面每个函数模板声明并指出他们是否合法?更正你发现的所有错误。

(a) template <typename T, U, typename V> vpod f1(T, U, V);	// typename U
(b) template <typename T> T f2(int &T);		//参数列表为(int&) 或者 (T&)
(c) inline template <typename T> T foo(T, unsigned int*);	//inline应该在模版参数列表的尖括号之后
(d) template <typename T> f4(T, T);		//函数缺少返回类型
(e) typedef char Ctype;
	template <typename Ctype> Ctype f5(Ctype a);	//合法

练习16.19 编写函数,接受一个容器的引用,打印容器中的元素。使用容器的size_type和size成员来控制打印元素的循环。

#include <iostream>
#include <vector>
using namespace std;
template <typename T>
void func(const vector<T>& data)
{
	typedef typename vector<T>::size_type size_type;
	size_t size = data.size();
	for (size_t i = 0; i != size; ++i) {
		cout << data[i] << " ";
	}
	cout << endl;
}
int main()
{
	vector<int> ivec = { 0,1,2,3,4,5,6,7,8,9 };
	func(ivec);
}

练习16.20 重写上一题的函数,使用begin个end返回的迭代器来控制循环。

#include <iostream>
#include <vector>
using namespace std;
template <typename T>
void func(const vector<T>& data)
{
	for (auto iter = data.cbegin(); iter != data.cend(); ++iter) {
		cout << *iter << " ";
	}
	cout << endl;
}
int main()
{
	vector<int> ivec = { 0,1,2,3,4,5,6,7,8,9 };
	func(ivec);
}

练习16.21 编写你自己的DebugDelete版本。

#include <iostream>
using namespace std;
class DebugDelete {
public:
	DebugDelete(ostream &s = cerr) :os(s) {}
	template<typename T> void operator()(T* p)const
	{
		os << "deleting unique_ptr" << endl;
		delete p;
	}
private:
	ostream &os;
};

练习16.24 为你的Blob模板添加一个构造函数,它接受两个迭代器。

template<typename It> Blob(It b, It e) :data(make_shared<vector<T>>(b, e)) {}
类内定义。


练习16.25 解释下面这些声明的含义:

extern template class vector<string>;	//实例化声明
template class vector<Sales_data>;		//实例化定义

练习16.26 假设NoDefault是一个没有默认构造函数的类,我们可以显式实例化vector<NoDefault>吗?如果不可以,解释为什么。

不可以。

一个类模板的实例化定义会实例化改模板的所有成员。

在未定义默认构造函数的情况下实例化vector<NoDefault>,会出现尝试引用已经删除的函数这一错误。


练习16.27 对下面每条带标签的语句,解释发什么了什么样的实例化(如果有的话)。如果一个模板被实例化,解释为什么;如果未实例化,解释为什么没有。

#include <iostream>
using namespace std;
template <typename T> class Stack{};
void f1(Stack<char>);					//(a) 实例化模板Stack<char>
class Exercise {	
	Stack<double> &rsd;					//(b) 实例化模板Stack<double>
	Stack<int> si;						//(c) 实例化模板Stack<int>
};
									
int main()
{
	Stack<char> *sc;					//(d) 未被实例化,每个文件中有一个该模板的实例化
	f1(*sc);							//(e) 未被实例化。
	int iobj = sizeof(Stack<string>);	//(f) 实例化模板Stack<string>
}



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值