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


1 0
原创粉丝点击