C++Primer第五版 第十六章习题答案(11~20)
来源:互联网 发布:什么是数据流程分析 编辑:程序博客网 时间:2024/06/08 05:42
类模版也是相当的重要,好好理解吧
11:知识点1:类模版的定义:与函数模版不同的是,编译器不会为类模版推断模版参数类型,所以我们在使用类模版时,需要显式地指出元素的类型,在其定义中,模版参数可以当作类型使用,用来表示类保存的元素的类型:比如说类中保存了一个T类型的vector变量
知识点2:我们可以将类中元素访问操作的返回类型定义为T&,也就是模版参数的引用,在被实例化之后,T会被替换为特定的模版实参类型
知识点3:一个类模版的每个实例都会形成一个独立的类,与其他实例化的类之间并没有特殊的访问权限
知识点4:实例化时,编译器会重写类模版,将模版参数替换为给定的模版实参
知识点5:在一个类模版中使用另一种模版,通常不会将一个实际的类型(如:int)当作其模版实参,而将模版自己的参数当作被使用模版的实参,比如在一个模版类中使用的vector和shared_ptr都是类模版,我们在使用时,会将T作为模版实参传递给他们
知识点6:无论何时使用模版都必须提供模版实参
知识点7:我们可以在类模版的内部或者外部对类模版的成员函数进行定义,定义在类模版内的成员函数被隐式的声明为inline函数
知识点8:由于类模版的每个实例都有自己版本的成员函数,因此类模版的成员函数具有和模版相同的模版参数,因此,在定义类模版之外的成员函数必须以关键词template开始,后接类模版实参列表(在其返回类型之后还需要加类名和<>参数列表)
#ifndef CLASS_TYPENAME_H#define CLASS_TYPENAME_Htemplate <typename T> class Blob{public:Blob();void check(size_t,const string&) const;private:shared_ptr<vector<T>> data;};template <typename T> Blob<T>::Blob():data(make_shared(vector<T>)){}//类外定义构造函数template <typename T> void Blob<T>::check(size_t i,const string& msg)//类外定义成员函数{if (i > data->size()){throw out_of_range(msg);}}#endif CLASS_TYPENAME_H
知识点9:类模版的构造函数,无需返回类型,其他和普通成员函数一样
知识点10:类模版的成员函数实例化:只有在程序使用它时才会被实例化,即使其类模版已经被实例化
知识点11:在类模版自己的作用域中(即类内),我们可以直接使用模版名而不提供实参(不需要<>这个东西了)而在类外则需要指定模版参数(返回值是模版的类型)
知识点12:当一个类模版包含一个非模版友元,则友元被授权可以访问所有的模版实例,如果友元自身是模版,类可以授权给所有友元模版实例,也可以只授予给定实例。如想要所有实例称为友元,友元声明中必须使用与类模版不同的模版参数
知识点13:C++11新标准:可以将模版参数类型声明为友元,比如int将称为Blob<int>的友元
知识点14:C++11新标准:我们可以定义一个typedef来引用实例化的类,还可以使用using来声明类型别名
typedef Blob<string> Blob;//之后使用Blob就是string类型的了
template <typename T> using twin = Blob<T>;//twin代指Blob
知识点15:类模版的static成员:每一个类模版的实例都有自己的static成员实例,但对于给定的类型,该static成员实例共享
知识点16:类模版的static成员有且仅有一个定义
答案:由知识点11可知,在List类内,使用模版名不需要再加参数列表,而ListItem使用时必须加上<>模版参数列表
12:见课本
13:一对一关系,否则不同类型的实例会错误相等
14:我擦勒,我都忘了Screen类是在哪一章的了,找了好久在7.3节 P243
Screen.h
#include <string> #include <iostream> struct xyc { int x=0; int y=0; char c=' '; }; template<unsigned N,unsigned M> class Screen { unsigned cursor = 0; unsigned width = N, height = M;//C++11新特性,可以类内初始化 std::string contents; public: Screen() = default; Screen(char c) :contents(N*M, c) {} char get()const { return contents[cursor]; } Screen set(xyc c) { cursor = --c.x*width + --c.y; contents[cursor] = c.c; return *this; } template<unsigned NN,unsigned MM> friend std::ostream &operator<<(std::ostream &os, const Screen<NN, MM> &s);//输出运算符 template<unsigned NN, unsigned MM> friend std::istream &operator>>(std::istream &is, Screen<NN, MM> &s);//输入运算符}; template<unsigned NN, unsigned MM> std::ostream &operator<<(std::ostream &os, const Screen<NN, MM> &s) { for (unsigned i = 0; i < s.width; ++i) { for (unsigned j = 0; j < s.height; ++j) putchar(s.contents[i*s.width + j]); putchar('\n'); } return os; } template<unsigned NN, unsigned MM> std::istream &operator>>(std::istream &is, Screen<NN, MM> &s) { xyc c; is >> c; s.set(c); return is; }
main.cpp
#include "Screen.h" std::istream &operator>>(std::istream &is, xyc &c) { is >> c.x; is >> c.y; is >> c.c; return is; } int main() { using namespace std; Screen<6, 6> scr('-'); cout << scr; cin >> scr; cout << scr; system("pause"); return 0; }
16:借鉴的网上的代码,这个类忘了太久,不想回头再看了
#include <iostream> #include <string> #include <memory> //allocator #include <utility> //move #include <initializer_list> #include <algorithm> //for_each template<class T> class StrVec { std::allocator<T> alloc;//为所有StrVec对象分配内存用 void chk_n_alloc() //如果剩余空间为0就分配新空间 { if (size() == capacity()) reallocate(); } std::pair<T *, T *> alloc_n_copy(const T *b, const T *e);//创建一个内容为b到e之间的元素的对象,并返回这个对象的一对头尾指针 void free();//释放所有alloc分配的所有内存 void reallocate();//移动当前对象的元素到2倍对象大小的新对象里 T *elements; T *first_free; T *cap; public: StrVec() :elements(nullptr), first_free(nullptr), cap(nullptr) {} StrVec(std::initializer_list<T> il); StrVec(const StrVec &s); StrVec(StrVec &&s); StrVec &operator=(StrVec &&s); StrVec &operator=(const StrVec &s); bool operator==(const StrVec &s)//14.16 { if (size() != s.size()) return false; auto it = elements, its = s.elements; while (it != first_free) { if (*it++ != *its++) return false; } return true; } bool operator!=(const StrVec &s)//14.16 { return !(*this == s); } bool operator<(const StrVec &s)//14.18 { if (size()>s.size()) return false; else if (size() < s.size) return true; for (auto it = elements, its = s.elements; it != first_free; ++it, ++its) { if (*it == *its) continue; else if (*it > *its) return false; else return true; } return false; } bool operator>(const StrVec &s)//14.18 { return !(*this < s) && *this != s; } bool operator<=(const StrVec &s)//14.18 { return !(*this > s); } bool operator>=(const StrVec &s)//14.18 { return !(*this < s); } StrVec &operator=(std::initializer_list<T> il) { auto nobj = alloc_n_copy(il.begin(), il.end()); free(); elements = nobj.first; first_free = cap = nobj.second; return *this; } T &operator[](std::size_t n) { return elements[n]; } const T &operator[](std::size_t n)const { return elements[n]; } ~StrVec(); void push_back(const T &s);//把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; } template<class TT> friend std::ostream &operator<<(std::ostream &os, const StrVec<TT> &s); }; template<class T> void StrVec<T>::push_back(const T &s) { chk_n_alloc();//确保空间剩余 alloc.construct(first_free++, s);//在尾后构建一个s(s的拷贝构造函数构造),并把尾后指针first_free指向下一个 } template<class T> std::pair<T *, T *> StrVec<T>::alloc_n_copy(const T *b, const T *e) { auto data = alloc.allocate(e - b);//分配并返回n个T对象的地址 T * return{ data, std::uninitialized_copy(b, e, data) };//uninit_copy返回尾后指针T * //把l~r之间的元素拷贝到data开始的地址,并返回data尾后,然后使用data(begin)和返回值(end)构建一个pair<T *,T *> } template<class T> void StrVec<T>::free() { if (elements)//如果不为空 { for (auto p = first_free; p != elements;) alloc.destroy(--p);//从最后一个元素开始向前摧毁,调用p的析构函数 //for_each(elements, first_free, [this](T *s){alloc.destroy(s); });//13.43 alloc.deallocate(elements, cap - first_free);//释放elements开始的n的T对象的内存 } } template<class T> StrVec<T>::StrVec(std::initializer_list<T> il) { auto newdata = alloc_n_copy(il.begin(), il.end()); elements = newdata.first; first_free = cap = newdata.second; } template<class T> StrVec<T>::StrVec(const StrVec &s) { auto newdata = alloc_n_copy(s.begin(), s.end());//创建一个s的副本 值 elements = newdata.first;//把头指向新创建的副本的头 first_free = cap = newdata.second;//把尾后和内存尾指向副本的尾(以后调用会调用chk_n_alloc,不用担心剩余空间大小) } template<class T> StrVec<T>::StrVec(StrVec &&s) :elements(s.elements), first_free(s.first_free), cap(s.cap) { s.elements = s.first_free = s.cap = nullptr; } template<class T> StrVec<T> &StrVec<T>::operator=(StrVec &&s) { if (this == &s) return *this; free(); elements = s.elements; first_free = s.first_free; cap = s.cap; s.elements = s.first_free = s.cap = nullptr; return *this; } template<class T> StrVec<T>::~StrVec() { free();//清理当前对象alloc分配的内存 } template<class T> StrVec<T> &StrVec<T>::operator=(const StrVec &s) { if (this == &s) return *this; auto data = alloc_n_copy(s.elements, s.first_free); free(); elements = data.first; first_free = cap = data.second; return *this; } template<class T> void StrVec<T>::reallocate() { auto newcapacity = size() ? 2 * size() : 1; //当前空间的两倍大小 auto newdata = alloc.allocate(newcapacity); //分配并返回newcapacity个T对象大小的空间 auto dest = newdata; auto elem = elements;//指向当前对象的头 for (size_t i = 0; i != size(); ++i) { alloc.construct(dest++, std::move(*elem++));//move会让elem指向的T对象放弃自己的内存管理权并返回,然后construct使用T的移动构造函数构建dest指向的地址 } //接受dest会指向newdata的尾后 free(); //移动完后释放当前对象指向的内存 elements = newdata; //指向新头 first_free = dest; //指向新尾后 cap = elements + newcapacity; //指向内存尾 } template<class TT> std::ostream &operator<<(std::ostream &os, const StrVec<TT> &s) { for (auto x : s) std::cout << x; return os; }
17:知识点1:T没有任何的实际意义,只是类型的一个替代
知识点2:模版参数的作用于范围在其声明之后,模版的定义或声明结束之前,且其会隐藏外层作用域中相同的名字(在外层使用了typedef T之后,在类中T的含义还是模版参数的含义),且模版参数名不可才参数列表中重复
知识点3:声明与定义中的模版参数的名字可以不相同
知识点4:我们只能使用关键字typename关键字来表明一个名字表示的是类型
知识点5:C++11新标准允许默认模版实参(可以为函数提供,早起只能为类模版提供默认模版实参)
知识点6:在我们为类模版的所有参数都提供了默认参数后,加一对空尖括号即可
见知识点4,且typename还可以绑定内置类型,而class只能绑定类类型
18:
a、错误,不能连续声明模板参数 改为:template<typename T,typename U,typename V> void f1(T,U,V);
b、错误,模板参数名不能作为变量名 改为:template<typename T> T f2(T &);
c、错误,内联声明位置错误 改为:template<typename T> inline T foo(T,unsigned int *);
d、错误,缺少返回类型 改为:template<typename> T f4(T,T);
e、错误,模板参数将覆盖外层的Ctype 改为:typedef char C;或者是template<typename C> C f5(C a);
19:
Have.h
#ifndef HAVE_H#define HAVE_Htemplate <typename T> void Have(T &t){for (size_t i = 0; i < t.size(); ++i){cout<<t[i]<<endl;}}#endif HAVE_H
main.cpp
#include <iostream>#include <vector>#include <list>#include <string>#include "Have.h"using namespace std;int main(int argc,char** argv){vector<int> vec1;vec1.push_back(2);vec1.push_back(3);Have(vec1);system("pause");return 0;}
20:
Have.h
#ifndef HAVE_H#define HAVE_Htemplate <typename T> void Have(T &t){T::iterator it1 = t.begin();for (it1; it1 != t.end(); ++it1){cout<<*it1<<endl;}}#endif HAVE_H
- C++Primer第五版 第十六章习题答案(11~20)
- C++Primer第五版 第十六章习题答案(1~10)
- C++Primer第五版 第十六章习题答案(21~30)
- C++Primer第五版 第十六章习题答案(31~40)
- C++Primer第五版 第十六章习题答案(41~50)
- C++Primer第五版 第十六章习题答案(51~60)
- C++Primer第五版 第十六章习题答案(61~67)
- C++Primer第五版 第五章习题答案(11~20)
- C++Primer(第五版) 习题答案
- C++Primer第五版 第二章习题答案(11~20)
- C++Primer第五版 第三章习题答案(11~20)
- C++Primer第五版 第四章习题答案(11~20)
- C++Primer第五版 第六章习题答案(11~20)
- C++Primer第五版 第七章习题答案(11~20)
- C++Primer第五版 第九章习题答案(11~20)
- C++Primer第五版 第十章习题答案(11~20)
- C++Primer第五版 第十一章习题答案(11~20)
- C++Primer第五版 第十二章习题答案(11~20)
- TensorFlow基础知识点(二)交互式使用/Interactive Usage
- angularjs默认选中--包括省市联动的默认选中
- Linux系统管理
- CentOS 7 安装 MySQL
- 大自然给每一个人都写过一封情书
- C++Primer第五版 第十六章习题答案(11~20)
- SpringBoot入门:Hello World(一)
- 如何在自己的App中调用Android系统自带的安装/卸载程序
- 学习记录
- 由森马SRM系统中学到的easyui各部件使用方法
- this指针
- 在OpenCV中显示汉字(支持灰度和彩色图片)
- 雪花,是冬天凝固的泪
- linux中bg、fg、ctrl+z、ctrl+d和ctrl+c的区别