C++ Primer(第五版)|练习题答案与解析(第十六章:模板与泛型编程)

C++ Primer(第五版)|练习题答案与解析(第十六章:模板与泛型编程)

本博客主要记录C++ Primer(第五版)中的练习题答案与解析。
参考:C++Primer
C++ Primer

练习题16.1

给出实例化定义

P579,当调用一个函数模板时,编译器会使用实参的类型来确定绑定到模版参数T上的类型。之后编译器利用推断出的模版参数来实例化一个特定版本的函数,这个过程被称之为实例化。

练习题16.2

给出实例化定义

#include <iostream>
using std::cout;
using std::endl;
#include <vector>
using std::vector;
template<typename T>
int compare(const T& lhs, const T& rhs)
{
   
    if(lhs < rhs) return -1;
    if(rhs < lhs) return  1;
    return 0;
}

int main()
{
   
    // 测试 compare 函数
    cout << compare(1, 0) << endl;
    vector<int> vec1{
    1, 2, 3 }, vec2{
    4, 5, 6 };
    cout << compare(vec1, vec2) << endl;

    return 0;
}

测试:

1
-1

练习题16.3

对两个Sales_data对象调用你的compare函数,观察编译器在实例化过程中如何处理错误。
在这里插入图片描述

练习题16.4

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

#include <iostream>
#include <vector>
#include <list>
#include <string>

namespace ch16
{
   
	template<typename Iterator, typename Value>
	auto find(Iterator first, Iterator last, Value const& value)
	{
   
		for (; first != last && *first != value; ++first);
		return first;
	}
}
int main()
{
   
	std::vector<int> v = {
    1, 2, 3, 4, 5, 6, 7, 8, 9 };
	auto is_in_vector = v.cend() != ch16::find(v.cbegin(), v.cend(), 5);
	std::cout << (is_in_vector ? "找到\n" : "未找到\n");
	std::list<std::string> l = {
    "aa", "bb", "cc", "dd", "ee", "ff", "gg" };
	auto is_in_list = l.cend() != ch16::find(l.cbegin(), l.cend(), "zz");
	std::cout << (is_in_list ? "找到\n" : "未找到\n");
	return 0;
}

测试:

找到
未找到

练习题16.5

为6.2.4节中的print函数编写模板版本,它接受一个数组的引用,能处理任意大小、元素类型的数组。

#include <iostream>
#include <string>
template<typename Arr>
void print(Arr const& arr)
{
   
	for (auto const& elem : arr)
		std::cout << elem << " ";
	std::cout << std::endl;
}
int main()
{
   
	std::string s[] = {
    "test", "train", "CNN" };
	char c[] = {
    'a', 'b', 'c', 'd' };
	int  i[] = {
    1, 20, 5 };
	print(i);
	print(c);
	print(s);
	return 0;
}

测试:

1 20 5
a b c d
test train CNN

练习题16.6

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

  • std::begin是一个模板函数,它引用一个数组。它将这个引用作为迭代器返回,迭代器指向这个数组中的第一个元素。
  • std::end是一个模板函数,它获取一个数组的引用并捕获大小。它返回这个引用和指向最后一个元素的迭代器的大小。
#include <iostream>
#include <vector>
#include <list>
#include <string>
// 和std::begin相同
template<typename T, unsigned size>
T* begin_def(T(&arr)[size])
{
   
	return arr;
}
// the same as std::end
template<typename T, unsigned size>
T* end_def(T(&arr)[size])
//我们通常不使用与标准libary函数相同的函数名
//这不应该是const
{
   
	return arr + size;
}
int main()
{
   
	std::string s[] = {
    "a","b","c","d" };
	std::cout << *begin_def(s) << std::endl;
	std::cout << *(begin_def(s) + 1) << std::endl;
	std::cout << *(end_def(s) - 1) << std::endl;
	return 0;
}

测试:

a
b
d

练习题16.7

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

#include <iostream>
#include <vector>
#include <list>
#include <string>
template<typename T, unsigned size>
constexpr unsigned getSize(const T(&)[size])
{
   
	return size;
}
int main()
{
   
	std::string s[] = {
    "test" };
	std::cout << getSize(s) << std::endl;
	char c[] = "t";
	std::cout << getSize(c) << std::endl;
	// 输出为2,因为“\0”被添加到数组的末尾
	return 0;
}

测试:

1
2

练习题16.8

在97页关键概念中,注意到,C++程序员喜欢用!=而不喜欢<,解释原因。

原因是更多的类定义了“!=”而不是<。这样做可以减少与模板类一起使用的类的需求数量。

练习题16.9

什么是函数模板?什么是类模板?

P583

  • 函数模板是一个公式,可以从中生成该函数的特定类型版本。
  • 类模板是生成类的蓝图。类模板与函数模板的区别在于,编译器无法推断类模板的参数类型。相反,要使用类模板,必须在模板名称后面的尖括号内提供附加信息(3.3,第97页)。

练习题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;
    //模板不是类型,必须提供类型
};

练习题16.12

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

Blob.h

#include <memory>
#include <vector>
template<typename T> class Blob
{
   
public:
    typedef T value_type;
    typedef typename std::vector<T>::size_type size_type;
    // constructors
    Blob();
    Blob(std::initializer_list<T> il);
    // number of elements in the Blob
    size_type size() const {
    return data->size(); }
    bool      empty() const{
    return data->empty(); }
    void push_back(const T& t) {
    data->push_back(t); }
    void push_back(T&& t)      {
    data->push_back(std::move(t)); }
    void pop_back();
    // element access
    T& back();
    T& operator[](size_type i);
    const T& back() const;
    const T& operator [](size_type i) const;
private:
    std::shared_ptr<std::vector<T>> data;
    // throw msg if data[i] isn't valid
    void check(size_type i, const std::string &msg) const;
};
// constructors
template<typename T>
Blob<T>::Blob() : data(std::make_shared<std::vector<T>>())
{
    }
template<typename T>
Blob<T>::Blob(std::initializer_list<T> il):
    data(std::make_shared<std::vector<T>>(il)){
    }
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);
}
template<typename T>
T& Blob<T>::back()
{
   
    check(0, "back on empty Blob");
    return data->back();
}
template<typename T>
const T& Blob<T>::back() const
{
   
    check(0, "back on empty Blob");
    return data->back();
}
template<typename T>
T& Blob<T>::operator [](size_type i)
{
   
    // if i is too big, check function will throw, preventing access to a nonexistent element
    check(i, "subscript out of range");
    return (*data)[i];
}
template<typename T>
const T& Blob<T>::operator [](size_type i) const
{
   
    // if i is too big, check function will throw, preventing access to a nonexistent element
    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();
}

blobptr.h

#include "Blob.h"
#include <memory>
#include <vector>
template <typename> class BlobPtr;
template <typename T>
bool operator ==(const BlobPtr<T>& lhs, const BlobPtr<T>& rhs);
template <typename T>
bool operator < (const BlobPtr<T>& lhs, const BlobPtr<T>& rhs);
template<typename T> class BlobPtr
{
   
    friend bool operator ==<T>
    (const BlobPtr<T>& lhs, const BlobPtr<T>& rhs);
    friend bool operator < <T>
    (const BlobPtr<T>& lhs, const BlobPtr<T>& rhs);
public:
    BlobPtr() : curr(0) {
    }
    BlobPtr(Blob<T>& a, std::size_t sz = 0) :
        wptr(a.data), curr(sz) {
    }
    T& operator*() const
    {
   
        auto p = check(curr, "dereference past end");
        return (*p)[curr];
    }
    // prefix
    BlobPtr& operator++();
    BlobPtr& operator--();
    // postfix
    BlobPtr operator ++(int);
    BlobPtr operator --(int);
private:
    // returns  a shared_ptr to the vector if the check succeeds
    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;
};
// prefix ++
template<typename T>
BlobPtr<T>& BlobPtr<T>::operator ++()
{
   
    // if curr already points past the end of the container, can't increment it
    check(curr, "increment past end of StrBlob");
    ++curr;
    return *this;
}
// prefix --
template<typename T>
BlobPtr<T>& BlobPtr<T>::operator --()
{
   
    -- curr;
    check(curr, "decrement past begin of BlobPtr");
    return *this;
}
// postfix ++
template<typename T>
BlobPtr<T> BlobPtr<T>::operator ++(int)
{
   
    BlobPtr ret = *this;
    ++*this;
    return ret;
}
// postfix --
template<typename T>
BlobPtr<T> BlobPtr<T>::operator --(int)
{
   
    BlobPtr ret = *this;
    --*this;
 
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值