16.1
根据模板参数的类型实例化出一个该类型的函数
16.2
#include <iostream> #include <string> #include <functional> //less //#include "../../7.21/7.21/标头.h" template<typename T> int compare(const T &a, const T &b) { if (std::less<T>()(a, b))return 1; if (std::less<T>()(b, a))return -1; return 0; } int main() { using namespace std; string a, b; //Sales_data b1("a"), b2("b"); cin >> a >> b; if (compare(a, b) > 0)cout << a << "<" << b << endl; if (compare(a, b) < 0)cout << a << ">" << b << endl; if (!compare(a, b))cout << a << "=" << b << endl; //cout << compare(b1, b2); system("pause"); return 0; }
16.3
错误:Sales_data类型未定义运算符"<"
16.4
#include <iostream> #include <vector> #include <list> #include <string> 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() != ::find(v.cbegin(), v.cend(), 8); 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() != ::find(l.cbegin(), l.cend(), "ab"); std::cout << (is_in_list ? "找到\n" : "没找到\n"); system("pause"); return 0; }
16.5
#include <iostream> //template<typename T> //void prin(const T &a) //{ // auto b = std::begin(a), e = std::end(a); // for (; b != e; ++b) // std::cout << *b << " "; //} //int main() //{ // using namespace std; // int a[] = { 1,2,3,4,5,6,9,8,7 }; // string str = "primer 5"; // prin(a); // prin(str); // system("pause"); // return 0; //} template<class T, unsigned N> void prin(T(&ar)[N]) { for (int i = 0; i < N; ++i) std::cout << ar[i]; } int main() { using namespace std; int ar[9] = { 4,0,8,6,8,6,4,7,7 }; char str[] = "helloworld!"; prin(ar); prin(str); prin("c++ primer"); system("pause"); return 0; }
16.6
#include <iostream> template<typename T> void print(const T &ar) { auto beg = ::begin(ar), end = ::end(ar); while (beg != end) { std::cout << *beg++ << " "; } } template<class T> auto begin(const T &ar) { return ar; } template<class T> auto end(const T &ar) { return ar + (sizeof ar) / sizeof(*ar); //整个数组的大小除以单个元素的大小得到数量,指向数组之后 } int main() { using namespace std; int l[] = { 2,0,1,6,0,1,0,6 }; print("c++ primer"); print(l); system("pause"); return 0; }
16.7
#include <iostream> template<class T> constexpr size_t sizear(const T &ar) { return sizeof ar; } int main() { int ar[] = { 1,2,3,4,5,6,7,8 }; int *p = ar; std::cout << sizear(ar) << std::endl; system("pause"); return 0; }
16.8
因为所有的标准库容器都有提供==与!=的运算符,而不一定有提供<运算符,所以使用!=能在所有的标准库容器上工作
16.9
函数模板就是一个公式,可以用来生成特定类型的函数版本。
16.10
生成一个特定类型的类,该类中特定类型被替换
16.11
//Blob.h #pragma once #include <iostream> #include <string> #include <vector> #include <initializer_list> #include <memory> //shared_ptr template<typename T> class Blob; template<typename T> class BlobPtr; template<typename T> bool operator==(const Blob<T>&, const Blob<T>&); template<typename T> class Blob { public: friend class BlobPtr<T>; friend bool operator==<T>(const Blob &l, const Blob &r); typedef T value_type; typedef typename std::vector<T>::size_type size_type; //把size_type视为类型名,忽略vector里同名的(如果有)静态变量 Blob(); Blob(std::initializer_list<T> il); 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(); //弹出末尾元素 T &back(); //返回末尾元素的引用 T &operator[](size_type i); //返回第i个元素的引用 private: std::shared_ptr<std::vector<T>> data; void check(size_type i, const std::string &msg)const; }; 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> T &Blob<T>::operator[](size_type i) { check(i, "subscript out 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> 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> bool operator==(const Blob<T> &l, const Blob<T> &r) { if (l.data != r.data) return false; return true; }
//BlobPtr.h #pragma once #include "Blob.h" template<typename T> class BlobPtr { public: BlobPtr(); BlobPtr(Blob<T> &a, size_t sz = 0); T &operator*()const; BlobPtr &operator++(); BlobPtr &operator--(); private: std::shared_ptr<std::vector<T>> check(std::size_t i, const std::string &s)const; std::weak_ptr<std::vector<T>> wptr; std::size_t curr; }; template<typename T> BlobPtr<T>::BlobPtr() :curr(0) {} template<typename T> BlobPtr<T>::BlobPtr(Blob<T> &a, size_t sz /* = 0 */) : wptr(a.data), curr(sz) {} 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++() { ++curr; check(curr, "++obj error"); return *this; } template<typename T> BlobPtr<T> &BlobPtr<T>::operator--() { --curr; if (curr < 0) throw std::out_of_range("--obj error"); return *this; } template<typename T> std::shared_ptr<std::vector<T>> BlobPtr<T>::check(std::size_t i, const std::string &s)const { auto ret = wptr.lock(); //确认未被释放内存 if (!ret) throw std::out_of_range("unbound BlobPtr"); if (i >= ret->size()) throw std::out_of_range(s); return ret; }
16.13
1 VS 1
让不同类型的类模板相等无意义
16.14,16.15
#pragma once #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; 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; }
#include "标头.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.16
#pragma once #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; }
#include "标头.h" int main() { using namespace std; StrVec<char> str{ 'c','l','a','n','n','a','d' }; cout << str; system("pause"); return 0; }
16.17
template<class T,typename T>在声明模板时没有区别,但是在制定为类型而不是静态值时就必须用typename: typename T::size_type func(); 这里的size_type是类型而不是值。
16.18
1、错误,不能连续声明模板参数 改为: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);
16.19
#pragma once #include <iostream> template<typename T> void prin(T &s, std::ostream &os) { typename T::size_type n=0,size = s.size(); auto iter = s.begin(); for (; n < size; ++n) { os << *iter++; } }
#include "标头.h" #include <vector> int main() { using namespace std; vector<char> str = { '1','2','3','4','5','6','7','8','9','0' }; prin(str, cout); system("pause"); return 0; }
16.20
#pragma once #include <iostream> template<typename T> void prin(T &s, std::ostream &os) { for (auto beg = s.begin(); beg != s.end(); ++beg) { os << *beg; } }
#include "标头.h" #include <vector> #include <list> int main() { using namespace std; vector<char> str = { '1','2','3','4','5','6','7','8','9','0' }; list<int> lint = { 1,2,3,4,5,6,7,8,9 }; prin(str, cout); prin(lint,cout); system("pause"); return 0; }
16.21
#pragma once #include <iostream> class DebugDelete { std::ostream &os; public: DebugDelete(std::ostream &s = std::cerr) :os(s) {} template<class T> void operator()(T *p) { os << "delete obj!!!" << std::endl; delete p; } };
16.22
#ifndef HEAD_H_ #define HEAD_H_ #include "DebugDelete.h" #include <iostream> #include <fstream> //ifstream #include <string> #include <vector> #include <sstream> //istringstream #include <map> #include <set> #include <memory> //shared_ptr class QueryResult; using line_no = std::vector < std::string > ::size_type; class TextQuery { std::shared_ptr<std::vector<std::string>> file; //保存整个文件内容,按行分 std::map<std::string, std::shared_ptr<std::set<line_no>>> wm; //每个单词对应行号 public: TextQuery(std::ifstream &is); QueryResult query(const std::string &s)const; //返回QR,单词、行号set,还有关联到文件内容 }; class QueryResult { friend std::ostream &print(std::ostream &os, const QueryResult &qr); std::string sought; std::shared_ptr<std::set<line_no>> lines; //记录出现的行号 std::shared_ptr<std::vector<std::string>> file; //关联到文件内容 public: QueryResult(std::string s, std::shared_ptr<std::set<line_no>> p, std::shared_ptr<std::vector<std::string>> f) :sought(s), lines(p), file(f){} }; TextQuery::TextQuery(std::ifstream &is) :file(new std::vector<std::string>(),DebugDelete(std::cout)) //为智能指针file分配空间 16.22 { std::string text; while (getline(is, text)) { file->push_back(text); int n = file->size() - 1; std::istringstream line(text); std::string word; while (line >> word) { auto &lines = wm[word]; //如果word在wm中第一次出现,那么对应的set就未分配内存,所以为空 if (!lines) //如果第一次出现 lines.reset(new std::set<line_no>()); lines->insert(n); } } } QueryResult TextQuery::query(const std::string &s)const { static std::shared_ptr<std::set<line_no>> nodata(new std::set<line_no>()); //当没找到单词时返回 内存常驻 auto loc = wm.find(s); if (loc == wm.end()) return QueryResult(s, nodata, file); else return QueryResult(s, loc->second, file); } std::ostream &print(std::ostream &os, const QueryResult &qr) { os << qr.sought << " occurs " << qr.lines->size() << " " << (qr.lines->size() > 1 ? "times" : "time") << std::endl; for (auto x : *qr.lines) //*(qr.file->begin()+x) os << "\t(line " << x + 1 << ") " << (*qr.file)[x] << std::endl; //file返回一个指向vec的智能指针,解引用后得到vector 然后使用下标运算符 return os; } #endif
16.23
在对象介绍声明周期时掉用
16.24
#pragma once #include <iostream> #include <string> #include <vector> #include <initializer_list> #include <memory> //shared_ptr template<typename T> class Blob; template<typename T> class BlobPtr; template<typename T> bool operator==(const Blob<T>&, const Blob<T>&); template<typename T> class Blob { public: friend class BlobPtr<T>; friend bool operator==<T>(const Blob &l, const Blob &r); typedef T value_type; typedef typename std::vector<T>::size_type size_type; //把size_type视为类型名,忽略vector里同名的(如果有)静态变量 Blob(); Blob(std::initializer_list<T> il); template<class U> Blob(U b, U e); 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(); //弹出末尾元素 T &back(); //返回末尾元素的引用 T &operator[](size_type i); //返回第i个元素的引用 private: std::shared_ptr<std::vector<T>> data; void check(size_type i, const std::string &msg)const; }; 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> T &Blob<T>::operator[](size_type i) { check(i, "subscript out 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> 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> bool operator==(const Blob<T> &l, const Blob<T> &r) { if (l.data != r.data) return false; return true; } template<class T> template<class U> Blob<T>::Blob(U b, U e) :data(std::make_shared < std::vector<T>>(b, e)) {}
#include "Blob.h" int main() { using namespace std; int vi[] = { 1,2,3,4,5,6 }; Blob<int> b(begin(vi), end(vi)); for (typename Blob<int>::size_type i = 0; i != b.size(); ++i) { cout << b[i]; } system("pause"); return 0; }
16.25
extern template class vector<string>;//声明一个实例化,但是该实例化的定义不一定在本模块,提示编译器可以在其他模块查找
template clss vector<Sales_data>;//定义一个实例化,让编译器生成一个实例
16.26
不可以,因为下面的就会报错:
vector<NoDefault> vN(10);//error,没有NoDefault类没有默认构造函数
16.27
a:没有实例化,只有在有数据时才会实例化
b:没有实例化,引用并不会实例化,因为没有数值存在
c:实例化出一个Stack<int>的实例
d:没有实例化,指针不会实例化,他包含的是地址
e:实例化出一个Stack<char>的实例,因为函数接收到数据,而且是按值传递
f:实例化出一个Stack<string>的实例,
16.28
//DebugDelete.h #pragma once #include <iostream> class DebugDelete { public: DebugDelete(std::ostream& s = std::cerr) : os(s) { } template<typename T> void operator() (T* p) const { os << "deleting ptr" << std::endl; delete p; } private: std::ostream& os; };
#pragma once //shared_p.h #include <functional> #include "DebugDelete.h" #include <memory> template<typename T>class shared_p; class SharedPtrControlBlock //保存引用数所使用 { template<typename T>friend class shared_p; int _count; SharedPtrControlBlock() :_count(1){} }; template<typename T> class shared_p { T *_ptr; SharedPtrControlBlock *control_block; std::function<void(T*)> deleter{ DebugDelete() }; template<typename U> void Initialize(const shared_p<U> &r) { if (r.control_block != nullptr) { AtomicIncrement(&r.control_block->_count); _ptr = r._ptr; control_block = r.control_block; deleter = r.deleter; } } public: explicit shared_p(T *ptr = nullptr, std::function<void(T*)> f = DebugDelete()) :_ptr(ptr), control_block(_ptr != nullptr ? new SharedPtrControlBlock : nullptr), deleter(f) {} shared_p(const shared_p &p) { Initialize(p); } shared_p &operator=(shared_p &p) { if (_ptr != p._ptr) { shared_p temp(p); swap(temp); } return *this; } shared_p(std::shared_ptr<T> &&sp) { if (sp.unique()) { /*shared_p<T> temp =shared_p(new T(*sp)); _ptr = temp._ptr; control_block = temp.control_block;*/ *this = shared_p(new T(*sp)); } else throw std::runtime_error("unique error!!"); } template<typename U> friend std::ostream &operator<<(std::ostream &os, const shared_p<U> &s); ~shared_p() { if (_ptr != nullptr) { if (AtomicDecrement(&control_block->_count) == 0) { deleter(_ptr); delete control_block; } } } int AtomicDecrement(int *count) { return --*count; } int AtomicIncrement(int *count) { return ++*count; } void reset(T *p = nullptr,std::function<void(T*)> del=DebugDelete()) { if (p != _ptr) { shared_p temp(p); temp->swap(*this); deleter = del; } } void swap(shared_p &r) { std::swap(_ptr, r._ptr); std::swap(control_block, r.control_block); std::swap(deleter, r.deleter); } T *get()const { return _ptr; } T &operator*()const { return *_ptr; } auto &operator[](int n) { return (*_ptr)[n]; } T *operator->()const { return _ptr; } int use_count()const //返回引用数 { return control_block ? control_block->_count : 1; } bool unique()const //是否单一占用 引用==1 { return use_count() == 1; } }; template<class T> std::ostream &operator<<(std::ostream &os, const shared_p<T> &s) { os << *s._ptr; return os; }
#pragma once //unique_p.h #include <functional> #include "DebugDelete.h" template<typename T> class unique_p { T *_ptr; std::function<void(T*)> deleter{ DebugDelete() }; public: explicit unique_p(T *ptr = nullptr, std::function<void(T*)> del = DebugDelete()) :_ptr(ptr), deleter(del) {} unique_p(unique_p &u) :_ptr(u._ptr), deleter(u.deleter) { u._ptr = nullptr; u.deleter = nullptr; } unique_p(unique_p &&u) :_ptr(u._ptr), deleter(u.deleter) { u._ptr = nullptr; u.deleter = nullptr; } unique_p &operator=(unique_p &u) { _ptr = (u._ptr); deleter = u.deleter; u.deleter = nullptr; u._ptr = nullptr; } unique_p &operator=(unique_p &&u) { _ptr = (u._ptr); deleter = u.deleter; u.deleter = nullptr; u._ptr = nullptr; } template<typename U> friend std::ostream &operator<<(std::ostream &os, const unique_p &u); ~unique_p() { delete _ptr; } void swap(unique_p &u) { std::swap(_ptr, u._ptr); std::swap(deleter, u.deleter); } void reset(T *p = nullptr, std::function<void(T*)> del = DebugDelete()) { if (p != _ptr) { unique_p<T> temp(p); temp.swap(*this); } } T *get()const { return _ptr; } T &operator*()const { return *_ptr; } auto &operator[](int n) { return (*_ptr)[n]; } T *operator->()const { return _ptr; } }; template<typename T> std::ostream &operator<<(std::ostream &os, const unique_p<T> &u) { os << *_ptr; return os; }
#include <iostream> #include "shared_p.h" #include "unique_p.h" #include <vector> #include <string> #include <memory> int main() { using namespace std; shared_p<vector<char>> sv(new vector<char>); char a; while ((a = getchar()) != 'q') sv->push_back(a); for (auto &x : *sv) cout << x; unique_p<int> ui(new int(4096)); cout << *ui; unique_p<int> uii(ui); cout << *uii; system("pause"); cout << *ui; //出错,空指针 system("pause"); return 0; }
16.29、16.30
#pragma once //shared_p.h #include "shared_p.h" #include <functional> #include <memory> #include "DebugDelete.h" template<typename T> class shared_p { T *_ptr=nullptr; std::size_t *_count=nullptr; std::function<void(T*)> deleter{ DebugDelete() }; void free() { if (_ptr) { if (--*_count == 0) { delete _count; deleter(_ptr); } _ptr = nullptr; _count = nullptr; } } public: shared_p() = default; shared_p(T *ptr, std::function<void(T*)> del = DebugDelete()) : _ptr(ptr), deleter(del),_count(new std::size_t(1)) {} shared_p(std::shared_ptr<T> &&sp, std::function<void(T*)> del = DebugDelete()) //16.29 { if (sp.unique()) { *this = shared_p(new T(*sp), del); } else throw std::runtime_error("unique error!!"); } shared_p(shared_p &&s)noexcept:_ptr(std::move(s._ptr)),_count(std::move(s._count)),deleter(std::move(s.deleter)) { s._ptr = nullptr; s._count = nullptr; s.deleter = nullptr; } shared_p &operator=(shared_p &s) { if(_ptr!=nullptr) free(); _ptr = s._ptr; _count = s._count; deleter = s.deleter; ++*_count; return *this; } shared_p &operator=(shared_p &&s) { if (_ptr != nullptr) free(); _ptr = s._ptr; _count = s._count; deleter = s.deleter; s._count = nullptr; s._ptr = nullptr; s.deleter = nullptr; return *this; } operator bool()const { return _ptr ? true : false; } T &operator*()const { return *_ptr; } T *operator->()const { return _ptr; } std::size_t use_count()const { return *_count; } T *get()const noexcept { return _ptr; } bool unique()const noexcept { return *_count == 1; } void swap(shared_p &s) { ::__ExceptionPtrSwap(*this, s); } void reset()noexcept { free(); } void reset(T *s, std::function<void(T*)> &del=DebugDelete()) { if (_ptr != p) { free(); _ptr = s; _count = new std::size_t(1); deleter = del; } } void reset(std::function<void(T*)> &del = DebugDelete()) { deleter = del; } ~shared_p() { free(); } };
#pragma once //Bolb.h #include <iostream> #include <string> #include <vector> #include <initializer_list> #include <memory> //make_shared #include "shared_p.h" template<typename T> class Blob; template<typename T> bool operator==(const Blob<T>&, const Blob<T>&); template<typename T> class Blob { public: friend bool operator==<T>(const Blob &l, const Blob &r); typedef T value_type; typedef typename std::vector<T>::size_type size_type; //把size_type视为类型名,忽略vector里同名的(如果有)静态变量 Blob(); Blob(std::initializer_list<T> il); template<class U> Blob(U b, U e); 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(); //弹出末尾元素 T &back(); //返回末尾元素的引用 T &operator[](size_type i); //返回第i个元素的引用 private: shared_p<std::vector<T>> data; void check(size_type i, const std::string &msg)const; }; 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> T &Blob<T>::operator[](size_type i) { check(i, "subscript out 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> 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> bool operator==(const Blob<T> &l, const Blob<T> &r) { if (l.data != r.data) return false; return true; } template<class T> template<class U> Blob<T>::Blob(U b, U e) :data(std::make_shared<std::vector<T>>(b, e)) { }
#include "Blob.h" int main() { using namespace std; int vi[] = { 1,2,3,4,5,6 }; vector<int> vint(begin(vi), end(vi)); Blob<int> b(vint.begin(),vint.end()); for (typename Blob<int>::size_type i = 0; i != b.size(); ++i) { cout << b[i]; } system("pause"); return 0; }
16.31
在类内部定义的方法默认内联,unique_ptr内保存删除器的是一歌函数指针,所以并不会内联而是跳转
16.32
编译器通过实参的类型类推断模板的参数
16.33
一种是非const到const的转换
另外一种是数组或函数到指针的转换
16.34
a、非法,实参是两个不同的类型,前者为 char [3],后者为 char [6]
b、合法,T为const char [4]
16.35
a、合法,T为char,const char被转换为int
b、合法,T为double,float被转换为int
c、合法,T为char
d、非法,实参类型不统一,前者是double,后者是float
16.36
a、T为int*
b、T1和T2都是int*
c、T为const int*
d、都是const int*
e、非法,实参类型不相同,int*与const int*
f、T1为int*,T2为const int*
16.37
不能,该函数只能接受一个模板参数类型
16.38
该函数接收的实参是迭代器或指针,如果不给出实参类型,那么就不知道分配内存的大小
make_shared根据指定的实参类型分配合适的空间,并发返回一个指向这块空间的shared_ptr
16.39
compare<string>
16.40
合法、因为decltype会推导表达式的内容
所以需要改类型支持该表达式的操作:decltype(*beg+0);需要类型支持operator+操作
返回类型取决于该操作符operator的返回值
16.41
template<typename T>
auto sum(T &a,T &b)->decltype(a+b) //制定返回值的类型
{
return a+b;
}
16.42
a、i为左值,T为int&,val:int& &&折叠为int&
b、ci为左值,T为const int&,val:const int & &&折叠为const int&
c、i*ci为右值,T为int&&,val:T&& &&折叠为int&&
16.43
表达式i=ci最终留下的是左值,所以T为T&,val为int&
16.44
1、template<typename T> void g(T);
a、T为int,val为int
b、T为int,val为int,const被忽略,因为是按值传递
c、T为int,val为int,实参是右值,但是按值传递给形参
2、template<typename T> void g(const T&);
a、T为int,val为const int&
b、T为int,val为const int&
c、T为int&&,val为const int& &&,折叠为const int&
16.45
当实参为42时,编译器解析T为一个右值:int&&,所以val为:int&& &&,折叠后为int&&
当实参为int类型变量时,编译器解析T为一个左值:int&,所以val为:int& &&,折叠后为int&
在第一次传递时,T为int&&,这对于vector没有错误。
但是第二次传递时,T为int&,这样会出现一个错误:未初始化的左值
16.46
auto newcapacity = size() ? 2 * size() : 1; //当前空间的两倍大小 auto newdata = alloc.allocate(newcapacity); //分配并返回newcapacity个string对象大小的空间 auto dest = newdata; auto elem = elements;//指向当前对象的头 for (size_t i = 0; i != size(); ++i) { alloc.construct(dest++, std::move(*elem++));//move会让elem指向的string对象放弃自己的内存管理权并返回,然后construct使用string的移动构造函数构建dest指向的地址 }
作用:把elem的所有元素移动到dest的空间里
16.47
#include <vector> #include <iostream> template <typename F,typename T1,typename T2> void flip(F f, T1 &&t1, T2 &&t2) { f(std::forward<T2>(t2), std::forward<T1>(t1)); } void pp(int a, int &b) { std::cout << a << " " << ++b << std::endl; } void gv(int &a, int &b) { a += b; b = a - b; a = a - b; } int main() { using namespace std; int a = 40, b = 86; flip(pp,a, b); flip(gv, a, b); cout << a << " " << b << endl; system("pause"); return 0; }
16.49、16.50
#include <iostream> template<typename T> void f(T t) { std::cout << "f(T):" << t << std::endl; } template<typename T> void f(const T *t) { std::cout << "f(const T *):" << t << " " << *t << std::endl; } template<typename T> void g(T t) { std::cout << "g(T):" << t << std::endl; } template<typename T> void g(T *t) { std::cout << "f(T *):" << t << " " << *t << std::endl; } int main() { using namespace std; int i = 42, *p = &i; const int ci = 0, *p2 = &ci; g(42); //g(T)<span style="white-space:pre"> </span>const int更匹配T g(p); //g(T*) <span style="white-space:pre"> </span>p是指针更匹配T* g(ci); //g(T) g(p2); //g(T*) f(42); //f(T) f(p); //f(T) f(ci); //f(T) f(p2); //f(const T*)<span style="white-space:pre"> </span>更加特例化 system("pause"); return 0; }
16.51、16.52
#include <iostream> #include <string> template<typename T,typename... Args> void foo(const T &t, Args... args) { std::cout << "Args:" << sizeof...(Args) << "\targs:" << sizeof...(args) << std::endl; } int main() { using namespace std; int i = 0; double d = 3.14; string s = "c++ primer"; foo(i, s, 42, d); //Args:3 args:3 foo(i, 42, "hello"); //Args:2 args:2 foo(d, s); //Args:1 args:1 foo("hi"); //Args:0 args:0 foo("c++", '5', 2016, 4.2, i, s); //Agrs:5 args:5 system("pause"); return 0; }
16.53
#include <iostream> #include <string> template<typename T> std::ostream &print(std::ostream &os, const T &t) { return os << t; } template<typename T,typename... Args> std::ostream &print(std::ostream &os,const T &t,const Args&... args) { os << t << ", "; return print(os, args...); } int main() { using namespace std; int a = 1; double d = 2.2; string s = "c++ primer"; print(cout, 42) << endl; print(cout, 42, d) << endl; print(cout, 42, d, s, "hello", 'w') << endl; system("pause"); return 0; }
16.54
缺少“<<”操作符的定义:
class Test {}; ... Test t; print(cout,t);//二进制“<<”: 没有找到接受“const aaa”类型的右操作数的运算符(或没有可接受的转换)
16.55
题目无法理解,会的同学请@我
16.56
#include <iostream> #include <string> #include <sstream> template <typename T> std::string debug_rep(const T& t); template <typename T> std::string debug_rep(T* p); std::string debug_rep(const std::string &s); std::string debug_rep(char* p); std::string debug_rep(const char *p); template<typename T> std::string debug_rep(const T &s) { std::ostringstream ret; ret << s; return ret.str(); } template<typename T> std::string debug_rep(T *p) { std::ostringstream ret; std::cout << "point:" << s; if (p) ret << " " << debug_rep(*p); else ret << "point is NULL!"; return ret.str(); } template<typename T> std::ostream &print(std::ostream &os, const T &t) { return os << t; } template<typename T,typename... Args> std::ostream &print(std::ostream &os, const T &t, const Args&... args) { os << t << ", "; return print(os, args...); } std::string debug_rep(const std::string &s) { return '"' + s + '"'; } std::string debug_rep(char *p) { return debug_rep(std::string(p)); } std::string debug_rep(const char *p) { return debug_rep(std::string(p)); } template<typename... Args> std::ostream &errorMsg(std::ostream &os, const Args... args) { return print(os, debug_rep(args)...); } int main() { using namespace std; string str = "c++"; errorMsg(cout, str, "primer", 4, 8.6, '5'); system("pause"); return 0; }
16.57
前者使用的是initialize_list,只能传递相同类型的实参,相比之下可变参数更灵活
16.58
#pragma once #include <iostream> #include <string> #include <memory> //allocator #include <utility> //move #include <initializer_list> #include <algorithm> //for_each class StrVec { std::allocator<std::string> alloc;//为所有StrVec对象分配内存用 void chk_n_alloc() //如果剩余空间为0就分配新空间 { if (size() == capacity()) reallocate(); } std::pair<std::string *, std::string *> alloc_n_copy(const std::string *b, const std::string *e);//创建一个内容为b到e之间的元素的对象,并返回这个对象的一对头尾指针 void free();//释放所有alloc分配的所有内存 void reallocate();//移动当前对象的元素到2倍对象大小的新对象里 std::string *elements; std::string *first_free; std::string *cap; public: StrVec() :elements(nullptr), first_free(nullptr), cap(nullptr){} StrVec(std::initializer_list<std::string> il); StrVec(const StrVec &s); StrVec(StrVec &&s); StrVec &operator=(StrVec &&s); StrVec &operator=(const StrVec &s); template<typename... Args> //16.58 void emplace_back(Args&&... args) { chk_n_alloc(); alloc.construct(first_free++, std::forward<Args>(args)...); } 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<std::string> il) { auto nobj = alloc_n_copy(il.begin(), il.end()); free(); elements = nobj.first; first_free = cap = nobj.second; return *this; } std::string &operator[](std::size_t n) { return elements[n]; } const std::string &operator[](std::size_t n)const { return elements[n]; } ~StrVec(); void push_back(const std::string &s);//把string添加到尾后指针 size_t size()const { return first_free - elements; } size_t capacity()const { return cap - elements; } std::string *begin()const { return elements; } std::string *end()const { return first_free; } }; void StrVec::push_back(const std::string &s) { chk_n_alloc();//确保空间剩余 alloc.construct(first_free++, s);//在尾后构建一个s(s的拷贝构造函数构造),并把尾后指针first_free指向下一个 } std::pair<std::string *, std::string *> StrVec::alloc_n_copy(const std::string *b, const std::string *e) { auto data = alloc.allocate(e-b);//分配并返回n个string对象的地址 string * return{ data, std::uninitialized_copy(b, e, data) };//uninit_copy返回尾后指针string * //把l~r之间的元素拷贝到data开始的地址,并返回data尾后,然后使用data(begin)和返回值(end)构建一个pair<string *,string *> } void StrVec::free() { if (elements)//如果不为空 { for (auto p = first_free; p != elements;) alloc.destroy(--p);//从最后一个元素开始向前摧毁,调用p的析构函数 //for_each(elements, first_free, [this](std::string *s){alloc.destroy(s); });//13.43 alloc.deallocate(elements, cap - first_free);//释放elements开始的n的string对象的内存 } } StrVec::StrVec(std::initializer_list<std::string> il) { auto newdata = alloc_n_copy(il.begin(), il.end()); elements = newdata.first; first_free = cap = newdata.second; } StrVec::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,不用担心剩余空间大小) } StrVec::StrVec(StrVec &&s) :elements(s.elements), first_free(s.first_free), cap(s.cap) { s.elements = s.first_free = s.cap = nullptr; } StrVec &StrVec::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; } StrVec::~StrVec() { free();//清理当前对象alloc分配的内存 } StrVec &StrVec::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; } void StrVec::reallocate() { auto newcapacity = size() ? 2 * size() : 1; //当前空间的两倍大小 auto newdata = alloc.allocate(newcapacity); //分配并返回newcapacity个string对象大小的空间 auto dest = newdata; auto elem = elements;//指向当前对象的头 for (size_t i = 0; i != size(); ++i) { alloc.construct(dest++, std::move(*elem++));//move会让elem指向的string对象放弃自己的内存管理权并返回,然后construct使用string的移动构造函数构建dest指向的地址 } //接受dest会指向newdata的尾后 free(); //移动完后释放当前对象指向的内存 elements = newdata; //指向新头 first_free = dest; //指向新尾后 cap = elements + newcapacity; //指向内存尾 }
16.59
16.60
可接受可变参数模板,转发其参数把初始化一个内存与没陪空间,返回一个shared_ptr
16.61
#include <memory> #include <iostream> #include <string> template<typename T, typename... Args> std::shared_ptr<T> make_shared(Args&&... args) { return std::shared_ptr<T>(new T(std::forward<Args>(args)...)); } int main() { auto p = make_shared<int>(40); std::cout << *p << std::endl; auto cp = make_shared<std::string>(10, 'a'); std::cout << *cp << std::endl; system("pause"); return 0; }
16.62
#pragma once #include <iostream> #include <string> class Sales_data { std::string bookNo; unsigned units_sold; double revenue; double avg_price()const { return units_sold ? revenue / units_sold : 0; } public: Sales_data(const std::string &s=std::string(), unsigned n = 0, double p = 0) :bookNo(s), units_sold(n), revenue(p) {} Sales_data(std::istream &is); std::string isbn()const { return bookNo; } Sales_data &operator+=(const Sales_data &s); friend std::hash<Sales_data>; friend std::ostream &operator<<(std::ostream &os, const Sales_data &s); friend std::istream &operator>>(std::istream &is, Sales_data &s); friend bool operator==(const Sales_data &ls, const Sales_data &rs); friend Sales_data operator+(const Sales_data &ls, const Sales_data &rs); friend std::ostream &print(std::ostream &os, const Sales_data &s); friend std::istream &read(std::istream &is, Sales_data &s); }; bool operator!=(const Sales_data &ls, const Sales_data &rs); Sales_data add(const Sales_data &ls, const Sales_data &rs); namespace std { template<> struct hash<Sales_data> { typedef size_t result_type; typedef Sales_data argument_type; size_t operator()(const Sales_data &s)const { return hash<string>()(s.bookNo) ^ hash<unsigned>()(s.units_sold) ^ hash<double>()(s.revenue); } }; }
#include "Sales_data.h" Sales_data::Sales_data(std::istream &is) { is >> *this; } Sales_data &Sales_data::operator+=(const Sales_data &s) { units_sold += s.units_sold; revenue += s.revenue; return *this; } std::ostream &operator<<(std::ostream &os, const Sales_data &s) { os << s.isbn() << " " << s.units_sold << " " << s.revenue << " " << s.avg_price(); return os; } std::istream &operator>>(std::istream &is, Sales_data &s) { double price; is >> s.bookNo >> s.units_sold >> price; if (is) s.revenue = s.units_sold*price; else s = Sales_data(); return is; } bool operator==(const Sales_data &ls, const Sales_data &rs) { return ls.bookNo == rs.bookNo&&ls.units_sold == rs.units_sold&&ls.revenue == rs.revenue; } bool operator!=(const Sales_data &ls, const Sales_data &rs) { return !(ls == rs); } Sales_data operator+(const Sales_data &ls, const Sales_data &rs) { Sales_data temp = ls; temp += rs; return temp; } Sales_data add(const Sales_data &ls, const Sales_data &rs) { Sales_data temp = ls; temp += rs; return temp; } std::ostream &print(std::ostream &os, const Sales_data &s) { os << s.isbn() << " " << s.units_sold << " " << s.revenue << " " << s.avg_price(); return os; } std::istream &read(std::istream &is, Sales_data &s) { double price; is >> s.bookNo >> s.units_sold >> price; s.revenue = s.units_sold*price; return is; }
#include <memory> #include <unordered_set> #include "Sales_data.h" int main() { //! test for ex16.62 std::unordered_multiset<Sales_data> mset; Sales_data sd("Bible", 10, 0.98); mset.emplace(sd); mset.emplace("C++ Primer", 5, 9.99); for (const auto &item : mset) std::cout << "the hash code of " << item.isbn() << ":\n0x" << std::hex << std::hash<Sales_data>()(item) << "\n"; system("pause"); return 0; }
16.63、16.64
#pragma once #include <vector> #include <string> #include <cstring> template<typename T> std::size_t count(const std::vector<T> &vec, T value) { std::size_t count = 0; for (auto &x : vec) count += (x == value) ? 1 : 0; return count; } template<> std::size_t count(const std::vector<char *> &vec, char *value) { std::size_t count = 0; for (auto &x : vec) count += (strcmp(x, value) == 0) ? 1 : 0; return count; }
#include <iostream> #include "标头.h" int main() { using namespace std; //16.63 vector<double> dv = { 5,6,8,3,5,5,6,3,4 }; vector<int> iv = { 2,3,5,6,3,3,4,8,3,5,1 }; vector<string> sv = { "q","w","q","q","a","d","f","q" }; cout << count(dv, 5.0) << " " << count(iv, 3) << " " << count<string>(sv, "q"); //16.64 vector<char *> cpv = { "c++","primer","4th","hello","world","c++" }; cout << count<char *>(cpv, "c++"); system("pause"); return 0; }
16.65
#pragma once #include <sstream> #include <string> template<typename T> std::string debug_rep(T *t) { std::ostringstream ret; ret << t; return ret.str(); } template<> std::string debug_rep(char *t) { return std::string(t); } template<> std::string debug_rep(const char *t) { return std::string(t); }
#include "标头.h" #include <iostream> int main() { using namespace std; char c[] = "c++ primer 5"; const char *p = c; cout << debug_rep(c) << endl; cout << debug_rep(p) << endl; system("pause"); return 0; }
16.66
重载会改变匹配优先度
16.67
不会影响,
特化的模板匹配优先度和模板级别一致,还是非模板优先度最高
2016年4月9日23:34:23
过了那么久...连我自己都怀疑自己的耐心了