第十二章 动态内存
12.1
b1和b2各包含4个元素,且这4个元素是b1和b2共享的。
更多:StrBlob的data成员是一个指向string的vector(即vector<string>)的shared_ptr,因此StrBlob的赋值不会拷贝vector的内容,而是多个StrBlob对象共享同一个vector对象。因此题目中尽管只是对b2进行了增加元素的操作,但结果就是b1和b2均包含4个string。
12.2
1 #ifndef MY_STRBLOB_H 2 #define MY_STRBLOB_H 3 #include <iostream> 4 #include <memory> 5 #include <string> 6 #include <initializer_list> 7 #include <vector> 8 #include <stdexcept> 9 10 //using声明在头文件中的全局作用域中 11 //在相应的实现文件就不要再次声明了 12 using std::shared_ptr; 13 using std::make_shared; 14 using std::vector; 15 using std::string; 16 using std::initializer_list; 17 using std::out_of_range; 18 using std::cin; 19 using std::cout; 20 using std::endl; 21 22 class StrBlob { 23 public: 24 using size_type = vector<string>::size_type; 25 StrBlob(); 26 StrBlob(initializer_list<string> il); 27 size_type size() const { return data->size(); } 28 bool empty() const { return data->empty(); } 29 void push_back(const string &s); 30 void pop_back(); 31 //返回string的引用,是因为调用点会使用该string 32 //如b.front() = "first"; 33 string& front(); 34 string& back(); 35 //只有const StrBlob对象才会调用以下函数 36 const string& front() const; 37 const string& back() const; 38 private: 39 shared_ptr<vector<string>> data; 40 void check(size_type i, const string &msg) const; 41 }; 42 43 StrBlob::StrBlob(): data(make_shared<vector<string>>()) 44 { 45 } 46 47 StrBlob::StrBlob(initializer_list<string> il): data(make_shared<vector<string>>(il)) 48 { 49 } 50 51 void StrBlob::check(size_type i, const string &msg) const 52 { 53 if (i >= data->size()) 54 throw out_of_range(msg); 55 } 56 57 void StrBlob::push_back(const string &s) 58 { 59 data->push_back(s); 60 } 61 62 void StrBlob::pop_back() 63 { 64 check(0, "此StrBlob对象指向一个空vector!\n"); 65 data->pop_back(); 66 } 67 68 string& StrBlob::front() 69 { 70 check(0, "此StrBlob对象指向一个空vector!\n"); 71 return data->front(); 72 } 73 74 string& StrBlob::back() 75 { 76 check(0, "此StrBlob对象指向一个空vector!\n"); 77 return data->back(); 78 } 79 80 const string& StrBlob::front() const 81 { 82 check(0, "此StrBlob对象指向一个空vector!\n"); 83 cout << "调用对象为const StrBlob!\n"; 84 return data->front(); 85 } 86 87 const string& StrBlob::back() const 88 { 89 check(0, "此StrBlob对象指向一个空vector!\n"); 90 cout << "调用对象为const StrBlob!\n"; 91 return data->back(); 92 } 93 #endif
1 #include <iostream> 2 #include "StrBlob.h" 3 4 StrBlob b1; 5 6 void func() 7 { 8 StrBlob b2{"first", "second", "third"}; 9 b1 = b2; //b1和b2指向同一vector<string>对象,该对象引用计数为2 10 cout << b1.size() << endl; 11 b2.front() = "zero"; 12 cout << b1.front() << endl; 13 } 14 15 int main() 16 { 17 func(); 18 //b1指向的对象引用计数递减,现为1 19 cout << b1.size() << endl; 20 return 0; 21 }
12.3
不需要。push_back和pop_back的功能分别是向StrBlob对象共享的vector对象添加和删除元素。因此,我们不应该为其重载const版本,因为const版本只能被const对象调用,而常量StrBlob对象是不应该被允许修改共享vector对象的内容的。
12.4
size_type是无符号类型,即使 i 是负数,也会自动转换为非负数。
12.5
观点一:
explicit的作用就是抑制构造函数的隐式转换
优点:不会自动的进行类型转换,必须清楚的知道类类型
缺点:必须用构造函数显示创建一个对象,不够方便简单
观点二:
使用容易,因为可以自动转换参数类型
调试难,出问题时就悲剧
观点三:
未编写一个初始化列表参数的显式构造函数,意味着可以进行列表向StrBlob的隐式类型转换,在需要StrBlob的地方可以使用列表进行替代。而且还可以进行拷贝形式的初始化,这令程序编写更为简单方便。
但这种隐式转换并不总是好的。例如,列表中可能并非都是合法的值。再如,对于接受StrBlob的函数,传递给它一个列表,会创建一个临时的StrBlob对象,用列表对其初始化,然后将其传递给函数,当函数完成后,此对象将被丢弃,再也无法访问了。
12.6
1 #include <iostream> 2 #include <fstream> 3 #include <sstream> 4 #include <iterator> 5 #include <initializer_list> 6 #include <vector> 7 #include <string> 8 #include <deque> 9 #include <list> 10 #include <forward_list> 11 #include <array> 12 #include <stack> 13 #include <queue> 14 #include <algorithm> 15 #include <functional> 16 #include <map> 17 #include <set> 18 #include <cctype> 19 #include <unordered_map> 20 #include <unordered_set> 21 #include <memory> 22 #include <new> 23 24 using namespace std; 25 using namespace std::placeholders; 26 27 vector<int> *func() 28 { 29 return new vector<int>(); 30 } 31 32 void func2() 33 { 34 int i; 35 vector<int> *pv = func(); 36 while (cin >> i) { 37 (*pv).push_back(i); 38 } 39 for (auto &v : *pv) 40 cout << v << " "; 41 cout << endl; 42 delete pv; 43 pv = nullptr; 44 } 45 46 47 int main() 48 { 49 func2(); 50 return 0; 51 }
12.7
1 #include <iostream> 2 #include <fstream> 3 #include <sstream> 4 #include <iterator> 5 #include <initializer_list> 6 #include <vector> 7 #include <string> 8 #include <deque> 9 #include <list> 10 #include <forward_list> 11 #include <array> 12 #include <stack> 13 #include <queue> 14 #include <algorithm> 15 #include <functional> 16 #include <map> 17 #include <set> 18 #include <cctype> 19 #include <unordered_map> 20 #include <unordered_set> 21 #include <memory> 22 #include <new> 23 24 using namespace std; 25 using namespace std::placeholders; 26 27 shared_ptr<vector<int>> func() //修改处1 28 { 29 return make_shared<vector<int>>(); //修改处2 30 } 31 32 void func2() 33 { 34 int i; 35 auto spv = func(); //即shared_ptr<vector<int>> spv = func(); 36 while (cin >> i) { 37 (*spv).push_back(i); 38 } 39 for (auto &v : *spv) 40 cout << v << " "; 41 cout << endl; 42 } 43 44 45 int main() 46 { 47 func2(); 48 return 0; 49 }
12.8
错误:这段程序的意图是判断分配动态内存是否成功(p的值可以转换为整型值,再转换为bool值),若自由空间被耗尽,则new不能分配所要求的内存空间,它就会抛出一个类型为bad_alloc的异常,而不是返回nullptr,即不会返回false,故程序在自由空间被耗尽时将达不到预期的目的。可将new int改为new (nothrow) int 来令new在分配失败时不抛出异常,而是返回nullptr。但这仍然不是一个好方法,应该通过捕获异常或是判断返回的指针来判断true或false,而不是依赖类型转换。除此,本段程序并没有释放分配的动态内存,会造成内存泄漏。
12.9
r = q:r原先指向的内存没有释放,变为孤儿内存,会造成内存泄漏
r2 = q2:递增q2指向的对象的引用计数,递减r2原来指向的对象的引用计数(r2原来指向的对象已没有引用者,会自动释放)
补充:1.首先是一个直接的内存泄露的问题,r和q一样都指向42的内存地址,而r中原来保存的地址——100的内存再无指针管理,变成“孤儿内存”,从而造成内存泄漏。 2.其次是一个“空悬指针“的问题。由于r和q指向同一个动态对象,如果程序编写不当,很容易产生释放了其中一个指针,而继续使用另一个指针的问题。继续使用的指针指向的是一块已经释放的内存,是一个空悬指针,继续读写它指向的内存可能导致程序崩溃甚至系统崩溃的严重问题。 而shared_ptr则可很好地解决这些问题。首先,分配了两个共享的对象,分别由共享指针p2和g2指向,因而它们的引用计数均为1.接下来,将q2赋予r2,。赋值操作会将q2指向的对象地址赋予r2,并将r2原来指向的对象的引用计数减1,将q2指向的对象的引用计数加1。这样,前者的引用计数变为0,其占用的内存空间会被释放,不会造成内存泄露。而后者的引用计数变为2,也不会因为r2和q2之一的销毁而释放它的内存空间,因此也不会造成空悬指针的问题。
12.10
正确:我们将一个利用p创建的临时shared_ptr传递给process,此时p和临时对象都指向相同的int对象,引用计数被正确地置为2。当这个调用表达式结束后,这个临时对象就被销毁了,引用计数递减,只有p指向该对象。
12.11
知识点:p.get()返回一个内置指针,指向智能指针p管理的对象,但是使用该内置指针的代码不能delete此指针。
该调用语句用get返回的指针创建了一个临时对象,这与用p创建临时对象不同,因为使用get返回的指针创建的临时对象与p是两个独立的shared_ptr(尽管它们指向相同的内存),所以该临时对象的引用计数为1,当调用语句结束后,这个临时对象被销毁,同时引用计数减1,变为0,故该临时对象指向的内存也被释放,而p也指向该内存,故p将变为一个空悬指针。
12.12
(a):合法,将智能指针sp传给函数process()
补充:process()被调用时智能指针ptr被创建并由sp初始化,故sp的引用计数加1,调用结束后,sp的引用计数减1
(b):不合法,new分配内存后返回的是一个普通指针,无法赋值到shared_ptr对象
(c):不合法,p是一个普通指针,不能赋值到shared_ptr对象
(d):合法,由p创建一个临时对象传给函数process
补充:因为不是由p.get()返回的指针创建的,故不会造成内存泄漏
12.13
sp指向的内存被释放(sp的引用计数仍为1),sp变为空悬指针。
12.14
#include <iostream> #include <memory> using namespace std; struct destination; void f(destination *d); struct destination { string addr = "192.168.87.130"; }; struct connection { bool isConnect = false; }; connection connect(destination *dest) { cout << "succeed to connect to : " << dest->addr << endl; connection conn; conn.isConnect = true; return conn; } void disconnect(connection *conn) { cout << "close connection...\n"; conn->isConnect = false; } void end_connection(connection *p) { disconnect(p); } int main() { destination destin; f(&destin); return 0; } void f(destination *d) { connection c = connect(d); shared_ptr<connection> pc(&c, end_connection); // throw exception(); }
12.15
void f(destination *d) { connection c = connect(d); auto func = [](connection *p)->void { disconnect(p); }; shared_ptr<connection> pc(&c, func); // 下同 // shared_ptr<connection> pc(&c, [](connection *p)->void{ disconnect(p); } ); // throw exception(); }
12.17
知识点:定义一个unique_str时,需要将其绑定到一个new返回的指针上,而且必须采用直接初始化形式
知识点:任一时刻只能有一个unique_ptr指向一个给定对象
(a):错误,ix不是new返回的指针
(b):错误,同上
(c):合法
(d):错误,同上
(e):合法
(f ):错误,p2.get()返回的确实可能是是一个指向动态内存的普通指针,但是unqiue_str指向的内存空间只能有它
12.18
release()的功能是:放弃对指针的控制权(即该智能指针不再指向该内存空间),而让别的uniqe_str指针可以指向该内存空间,这是由于unqie_str与内存空间一对一的关系所致,而shared_str并不需要这么麻烦地转接内存空间的所有权,故没必要设置这一函数。
12.19
1 #include <iostream> 2 #include <memory> 3 #include <string> 4 #include <initializer_list> 5 #include <vector> 6 #include <stdexcept> 7 8 using namespace std; 9 10 class StrBlobPtr; 11 12 class StrBlob { 13 friend class StrBlobPtr; 14 public: 15 using size_type = vector<string>::size_type; 16 StrBlob(); 17 StrBlob(initializer_list<string> il); 18 size_type size() const { return data->size(); } 19 bool empty() const { return data->empty(); } 20 void push_back(const string &s); 21 void pop_back(); 22 //返回string的引用,是因为调用点会使用该string 23 //如b.front() = "first"; 24 string& front(); 25 string& back(); 26 //只有const StrBlob对象才会调用以下函数 27 const string& front() const; 28 const string& back() const; 29 StrBlobPtr begin(); 30 StrBlobPtr end(); 31 private: 32 shared_ptr<vector<string>> data; 33 void check(size_type i, const string &msg) const; 34 }; 35 36 StrBlob::StrBlob(): data(make_shared<vector<string>>()) 37 { 38 } 39 40 StrBlob::StrBlob(initializer_list<string> il): data(make_shared<vector<string>>(il)) 41 { 42 } 43 44 void StrBlob::check(size_type i, const string &msg) const 45 { 46 if (i >= data->size()) 47 throw out_of_range(msg); 48 } 49 50 void StrBlob::push_back(const string &s) 51 { 52 data->push_back(s); 53 } 54 55 void StrBlob::pop_back() 56 { 57 check(0, "此StrBlob对象指向一个空vector!\n"); 58 data->pop_back(); 59 } 60 61 string& StrBlob::front() 62 { 63 check(0, "此StrBlob对象指向一个空vector!\n"); 64 return data->front(); 65 } 66 67 string& StrBlob::back() 68 { 69 check(0, "此StrBlob对象指向一个空vector!\n"); 70 return data->back(); 71 } 72 73 const string& StrBlob::front() const 74 { 75 check(0, "此StrBlob对象指向一个空vector!\n"); 76 cout << "调用对象为const StrBlob!\n"; 77 return data->front(); 78 } 79 80 const string& StrBlob::back() const 81 { 82 check(0, "此StrBlob对象指向一个空vector!\n"); 83 cout << "调用对象为const StrBlob!\n"; 84 return data->back(); 85 } 86 87 class StrBlobPtr { 88 public: 89 StrBlobPtr(): curr(0) {} 90 StrBlobPtr(StrBlob &b, size_t sz = 0): wptr(b.data), curr(sz) {} 91 string& deref() const; 92 StrBlobPtr& incr(); 93 private: 94 weak_ptr<vector<string>> wptr; 95 size_t curr; 96 shared_ptr<vector<string>> check(size_t i, const string &msg) const; 97 }; 98 99 shared_ptr<vector<string>> StrBlobPtr::check(size_t i, const string &msg) const 100 { 101 auto ret = wptr.lock(); 102 if (!ret) 103 throw runtime_error("要访问的vector<string>对象不存在!\n"); 104 if (i >= ret->size()) 105 throw out_of_range(msg); 106 return ret; 107 } 108 109 string& StrBlobPtr::deref() const 110 { 111 auto p = check(curr, "当前下标不合法!\n"); 112 return (*p)[curr]; 113 } 114 115 StrBlobPtr& StrBlobPtr::incr() 116 { 117 check(curr, "不能继续递增了\n"); 118 ++curr; 119 return *this; 120 } 121 122 StrBlobPtr StrBlob::begin() 123 { 124 return StrBlobPtr(*this); 125 } 126 127 StrBlobPtr StrBlob::end() 128 { 129 return StrBlobPtr(*this, data->size()); 130 } 131 132 int main() 133 { 134 StrBlob b1{"mon", "tue", "wed", "thu", "fri"}; 135 StrBlobPtr p(b1, 3); 136 cout << p.deref() << endl; //访问p当前指向的元素 137 cout << p.incr().deref() << endl; //先递增p,再访问元素 138 p = b1.begin(); 139 cout << p.deref() << endl; 140 return 0; 141 }
12.20
1 #include <iostream> 2 #include <fstream> 3 #include <memory> 4 #include <string> 5 #include <initializer_list> 6 #include <vector> 7 #include <stdexcept> 8 9 using namespace std; 10 11 class StrBlobPtr; 12 13 class StrBlob { 14 friend class StrBlobPtr; 15 public: 16 using size_type = vector<string>::size_type; 17 StrBlob(); 18 StrBlob(initializer_list<string> il); 19 size_type size() const { return data->size(); } 20 bool empty() const { return data->empty(); } 21 void push_back(const string &s); 22 void pop_back(); 23 //返回string的引用,是因为调用点会使用该string 24 //如b.front() = "first"; 25 string& front(); 26 string& back(); 27 //只有const StrBlob对象才会调用以下函数 28 const string& front() const; 29 const string& back() const; 30 StrBlobPtr begin(); 31 StrBlobPtr end(); 32 private: 33 shared_ptr<vector<string>> data; 34 void check(size_type i, const string &msg) const; 35 }; 36 37 StrBlob::StrBlob(): data(make_shared<vector<string>>()) 38 { 39 } 40 41 StrBlob::StrBlob(initializer_list<string> il): data(make_shared<vector<string>>(il)) 42 { 43 } 44 45 void StrBlob::check(size_type i, const string &msg) const 46 { 47 if (i >= data->size()) 48 throw out_of_range(msg); 49 } 50 51 void StrBlob::push_back(const string &s) 52 { 53 data->push_back(s); 54 } 55 56 void StrBlob::pop_back() 57 { 58 check(0, "此StrBlob对象指向一个空vector!\n"); 59 data->pop_back(); 60 } 61 62 string& StrBlob::front() 63 { 64 check(0, "此StrBlob对象指向一个空vector!\n"); 65 return data->front(); 66 } 67 68 string& StrBlob::back() 69 { 70 check(0, "此StrBlob对象指向一个空vector!\n"); 71 return data->back(); 72 } 73 74 const string& StrBlob::front() const 75 { 76 check(0, "此StrBlob对象指向一个空vector!\n"); 77 cout << "调用对象为const StrBlob!\n"; 78 return data->front(); 79 } 80 81 const string& StrBlob::back() const 82 { 83 check(0, "此StrBlob对象指向一个空vector!\n"); 84 cout << "调用对象为const StrBlob!\n"; 85 return data->back(); 86 } 87 88 class StrBlobPtr { 89 public: 90 StrBlobPtr(): curr(0) {} 91 StrBlobPtr(StrBlob &b, size_t sz = 0): wptr(b.data), curr(sz) {} 92 string& deref() const; 93 StrBlobPtr& incr(); 94 bool operator!=(const StrBlobPtr &rhs) const; 95 private: 96 weak_ptr<vector<string>> wptr; 97 size_t curr; 98 shared_ptr<vector<string>> check(size_t i, const string &msg) const; 99 }; 100 101 shared_ptr<vector<string>> StrBlobPtr::check(size_t i, const string &msg) const 102 { 103 auto ret = wptr.lock(); 104 if (!ret) 105 throw runtime_error("要访问的vector<string>对象不存在!\n"); 106 if (i >= ret->size()) 107 throw out_of_range(msg); 108 return ret; 109 } 110 111 string& StrBlobPtr::deref() const 112 { 113 auto p = check(curr, "当前下标不合法!\n"); 114 return (*p)[curr]; 115 } 116 117 StrBlobPtr& StrBlobPtr::incr() 118 { 119 check(curr, "不能继续递增了\n"); 120 ++curr; 121 return *this; 122 } 123 124 //判断两个StrBlobPtr对象是否相等 125 bool StrBlobPtr::operator!=(const StrBlobPtr &rhs) const 126 { 127 return wptr.lock() != rhs.wptr.lock() || curr != rhs.curr; 128 } 129 130 StrBlobPtr StrBlob::begin() 131 { 132 return StrBlobPtr(*this); 133 } 134 135 StrBlobPtr StrBlob::end() 136 { 137 return StrBlobPtr(*this, data->size()); 138 } 139 140 int main() 141 { 142 ifstream in("data.txt"); 143 string line; 144 StrBlob b; 145 while (getline(in, line)) { 146 b.push_back(line); 147 } 148 StrBlobPtr pb(b); 149 while (pb != b.end()) { 150 cout << pb.deref() << endl; 151 pb.incr(); 152 } 153 return 0; 154 }
12.21
前一个版本更好,因为它将合法性检查与元素获取的返回语句分离开来,代码更清晰易读,当执行到第二条语句时,已确保p是存在的vector,curr是合法的位置,可安全地获取元素并返回。这种清晰的结构也更有利于修改不同的处理逻辑。 而本题中的版本将合法性检查和元素获取及返回合在一条语句中,不易读,也不易修改。
12.22
class ConstStrBlobPtr{ friend bool eq(const ConstStrBlobPtr &, const ConstStrBlobPtr &); public: ConstStrBlobPtr(): curr(0) {} ConstStrBlobPtr(const StrBlob &a, size_t sz = 0): wptr(a.data), curr(sz) {} string &deref() const; //解引用 ConstStrBlobPtr &incr(); //递增StrBlobPtr private: weak_ptr<vector<string>> wptr; size_t curr; shared_ptr<vector<string>> check(size_t, const string &) const; };
12.23
1 #include <iostream> 2 #include <fstream> 3 #include <sstream> 4 #include <iterator> 5 #include <initializer_list> 6 #include <vector> 7 #include <string> 8 #include <cstring> 9 #include <deque> 10 #include <list> 11 #include <forward_list> 12 #include <array> 13 #include <stack> 14 #include <queue> 15 #include <algorithm> 16 #include <functional> 17 #include <map> 18 #include <set> 19 #include <cctype> 20 #include <unordered_map> 21 #include <unordered_set> 22 #include <memory> 23 #include <new> 24 25 using namespace std; 26 using namespace std::placeholders; 27 28 void func1() 29 { 30 char str1[] = "hello", str2[] = " world!"; 31 int len = strlen(str1) + strlen(str2); 32 char *p = new char[len + 1]; 33 strcpy(p, str1); 34 strcat(p, str2); 35 // for (int i = 0; i != len + 1; ++i) 36 // cout << *(p+i); 37 cout << p << endl; 38 } 39 40 void func2() 41 { 42 string s1 = "hello", s2 = " world!"; 43 int len = s1.size() + s2.size(); 44 char *p = new char[len + 1]; 45 strcpy(p, (s1+s2).c_str()); //必须转换为c类型字符串(c中无string类型) 46 cout << p << endl; 47 } 48 49 int main() 50 { 51 func1(); 52 func2(); 53 return 0; 54 }
忘记释放内存,引以为戒!!!
12.24
1 #include <iostream> 2 #include <fstream> 3 #include <sstream> 4 #include <iterator> 5 #include <initializer_list> 6 #include <vector> 7 #include <string> 8 #include <cstring> 9 #include <deque> 10 #include <list> 11 #include <forward_list> 12 #include <array> 13 #include <stack> 14 #include <queue> 15 #include <algorithm> 16 #include <functional> 17 #include <map> 18 #include <set> 19 #include <cctype> 20 #include <unordered_map> 21 #include <unordered_set> 22 #include <memory> 23 #include <new> 24 25 using namespace std; 26 using namespace std::placeholders; 27 28 void func() 29 { 30 char *p = new char[10]; 31 cin.get(p, 10); //只读取10个字符 32 cout << p << endl; 33 delete []p; 34 } 35 36 int main() 37 { 38 func(); 39 return 0; 40 }
我的程序只读取与分配长度相等长度的字符串,多出部分会自动忽略。
12.25
delete []pa;
12.26
1 #include <iostream> 2 #include <fstream> 3 #include <sstream> 4 #include <iterator> 5 #include <initializer_list> 6 #include <vector> 7 #include <string> 8 #include <cstring> 9 #include <deque> 10 #include <list> 11 #include <forward_list> 12 #include <array> 13 #include <stack> 14 #include <queue> 15 #include <algorithm> 16 #include <functional> 17 #include <map> 18 #include <set> 19 #include <cctype> 20 #include <unordered_map> 21 #include <unordered_set> 22 #include <memory> 23 #include <new> 24 25 using namespace std; 26 using namespace std::placeholders; 27 28 void func() 29 { 30 allocator<string> a; 31 auto const pa = a.allocate(5); //pa指向a分配的内存首部 32 auto q = pa; 33 string s; 34 while (q != pa + 5 && cin >> s) { 35 a.construct(q++, s); 36 } 37 //q指向最后构造的元素之后的位置 38 while (q != pa) { 39 --q; 40 cout << *q << endl; 41 a.destroy(q); //销毁元素 42 } 43 a.deallocate(pa, 5); //释放内存 44 } 45 46 int main() 47 { 48 func(); 49 return 0; 50 }
12.27~12.28
#include <iostream> #include <fstream> #include <sstream> #include <memory> #include <map> #include <set> #include <vector> #include <string> using namespace std; class QueryResult; //保存输入文件 class TextQuery { using lineNo = vector<string>::size_type; public: TextQuery(ifstream &is); QueryResult query(const string&) const; private: shared_ptr<vector<string>> file; map<string, shared_ptr<set<lineNo>>> imap; }; //保存查询结果 class QueryResult { using lineNo = vector<string>::size_type; friend ostream& operator<<(ostream&, const QueryResult&); public: QueryResult(const string &s, shared_ptr<vector<string>> f, shared_ptr<set<lineNo>> l) :sword(s), file(f), line(l) {} private: string sword; shared_ptr<vector<string>> file; shared_ptr<set<lineNo>> line; }; TextQuery::TextQuery(ifstream &is) :file(make_shared<vector<string>>()) { string line; while (getline(is, line)) { file->push_back(line); istringstream in(line); auto l = file->size() - 1; string word; while (in >> word) { shared_ptr<set<lineNo>> &r = imap[word]; if (!r) r.reset(new set<lineNo>); r->insert(l); } } } QueryResult TextQuery::query(const string &word) const { static shared_ptr<set<lineNo>> nodata(new set<lineNo>); auto it = imap.find(word); if (it != imap.end()) return QueryResult(word, file, it->second); else return QueryResult(word, file, nodata); } ostream& operator<<(ostream &os, const QueryResult &qr) { auto cnt = qr.line->size(); os << qr.sword << " occurs " << cnt << (cnt > 1 ? " times" : " time") << endl; for (auto l : *qr.line) { os << "\t(line " << l + 1 << ") " << *(qr.file->begin() + l) << endl; } return os; } int main() { ifstream in("data.txt"); TextQuery tq(in); cout << "请输入要查询的单词:\n"; string s; while (cin >> s) { cout << tq.query(s) << endl; cout << "请输入要查询的单词:\n"; } return 0; }
12.29
do { cout<<"enter word to look for ,or q to quit: "; string s; if (!(cin>>s) || s == "q") break; query_and_print(s, cout)<<endl; } while (true);
12.30
#include <iostream> #include <fstream> #include <sstream> #include <iterator> #include <vector> #include <string> #include <algorithm> #include <map> #include <set> #include <memory> using namespace std; using line_no = vector<string>::size_type; class QueryResult; class TextQuery { public: TextQuery(ifstream &is); QueryResult query(const string &word) const; private: shared_ptr<vector<string>> file; map<string, shared_ptr<set<line_no>>> pm; }; //考验你对关联容器和shared_ptr的掌握 TextQuery::TextQuery(ifstream &is): file(new vector<string>) { string line; while (getline(is, line)) { file->push_back(line); int lineNo = file->size() - 1; //当前行号 istringstream in(line); string word; while (in >> word) { shared_ptr<set<line_no>> &lines = pm[word]; if (!lines) //若map中无此单词 lines.reset(new set<line_no>); //分配一个新set lines->insert(lineNo); } } } class QueryResult { friend ostream& print(ostream &os, const QueryResult &qr); public: QueryResult(string s, shared_ptr<vector<string>> f, shared_ptr<set<line_no>> p): sought(s), file(f), lines(p) {} private: string sought; //查询的单词 shared_ptr<vector<string>> file; //文件内容 shared_ptr<set<line_no>> lines; //出现的行号 }; QueryResult TextQuery::query(const string &word) const { static shared_ptr<set<line_no>> nodata(new set<line_no>); auto ans = pm.find(word); if (ans == pm.end()) return QueryResult(word, file, nodata); else return QueryResult(word, file, ans->second); } string make_plural(size_t cnt, const string &s1, const string &s2) { return cnt > 1 ? s1 + s2 : s1; } ostream& print(ostream &os, const QueryResult &qr) { os << qr.sought << " occurs " << qr.lines->size() << " " << make_plural(qr.lines->size(), "time", "s") << endl; //打印单词的每一行 for (auto num : *qr.lines) //避免行号从0开始给用户带来的困惑 os << "\t(line " << num + 1 << ")" << *(qr.file->begin() + num) << endl; return os; } void runQueries(ifstream &infile) { TextQuery tq(infile); while (true) { cout << "Enter word to look for, or q to quit: "; string s; if (!(cin >> s) || s == "q") break; print(cout, tq.query(s)); cout << endl; } } int main() { ifstream in("data.txt"); runQueries(in); return 0; }
12.31
vector会保存相同的行号,故可能会打印同一行多次。
12.32
/* StrBlob.h */ #include <iostream> #include <fstream> #include <sstream> #include <vector> #include <string> #include <map> #include <set> #include <memory> #include <stdexcept> #include <initializer_list> using namespace std; class StrBlob { public: using size_type = vector<string>::size_type; StrBlob(); StrBlob(initializer_list<string> il); StrBlob(vector<string> *p) :data(p) {} size_type size() const { return data->size(); } bool empty() const { return data->empty(); } void push_back(const string &s); void pop_back(); //返回string的引用,是因为调用点会使用该string //如b.front() = "first"; string& front(); string& back(); //只有const StrBlob对象才会调用以下函数 const string& front() const; const string& back() const; vector<string>::iterator begin() const { return data->begin(); } private: shared_ptr<vector<string>> data; void check(size_type i, const string &msg) const; }; StrBlob::StrBlob(): data(make_shared<vector<string>>()) { } StrBlob::StrBlob(initializer_list<string> il): data(make_shared<vector<string>>(il)) { } void StrBlob::check(size_type i, const string &msg) const { if (i >= data->size()) throw out_of_range(msg); } void StrBlob::push_back(const string &s) { data->push_back(s); } void StrBlob::pop_back() { check(0, "此StrBlob对象指向一个空vector!\n"); data->pop_back(); } string& StrBlob::front() { check(0, "此StrBlob对象指向一个空vector!\n"); return data->front(); } string& StrBlob::back() { check(0, "此StrBlob对象指向一个空vector!\n"); return data->back(); } const string& StrBlob::front() const { check(0, "此StrBlob对象指向一个空vector!\n"); cout << "调用对象为const StrBlob!\n"; return data->front(); } const string& StrBlob::back() const { check(0, "此StrBlob对象指向一个空vector!\n"); cout << "调用对象为const StrBlob!\n"; return data->back(); }
/* TextQuery.h */ #include "StrBlob.h" using lineNo = vector<string>::size_type; class QueryResult; class TextQuery { public: TextQuery(ifstream&); QueryResult query(const string&) const; private: StrBlob file; map<string, shared_ptr<set<lineNo>>> imap; }; class QueryResult { friend ostream& operator<<(ostream&, const QueryResult&); public: QueryResult(const string &s, StrBlob f, shared_ptr<set<lineNo>> l) :qrword(s), qrfile(f), qrline(l) {} private: string qrword; StrBlob qrfile; shared_ptr<set<lineNo>> qrline; }; //TextQuery::TextQuery(ifstream &in) //此情况以默认构造函数构造一个空vector<string> TextQuery::TextQuery(ifstream &in) :file(new vector<string>) { string line; while (getline(in, line)) { file.push_back(line); int rowNo = file.size() - 1; istringstream is(line); string word; while (is >> word) { shared_ptr<set<lineNo>> &r = imap[word]; if (!r) r.reset(new set<lineNo>); r->insert(rowNo); } } } QueryResult TextQuery::query(const string &word) const { static shared_ptr<set<lineNo>> nodata(new set<lineNo>); auto it = imap.find(word); if (it == imap.end()) return QueryResult(word, file, nodata); else return QueryResult(word, file, it->second); } ostream& operator<<(ostream &os, const QueryResult &qr) { int cnt = qr.qrline->size(); os << qr.qrword << " occurs " << cnt << (cnt > 1 ? " times" : " time") << endl; for (auto &line : *qr.qrline) os << "\t(line " << line << ") " << *(qr.qrfile.begin() + line) << endl; return os; }
#include "TextQuery.h" int main() { string fileName; cin >> fileName; ifstream in; in.open(fileName); if (!in) { cerr << "No such file!\n"; return -1; } TextQuery tq(in); cout << "请输入要查询的单词:\n"; string word; while (cin >> word) { cout << tq.query(word) << endl; cout << "请输入要查询的单词:\n"; } in.close(); }
12.33
class QueryResult { using lineNo = vector<string>::size_type; friend ostream& operator<<(ostream&, const QueryResult&); public: QueryResult(const string &s, shared_ptr<vector<string>> f, shared_ptr<set<lineNo>> l) :qrword(s), qrfile(f), qrline(l) {} set<lineNo>::iterator begin() const { return qrline->begin(); } set<lineNo>::iterator end() const { return qrline->end(); } shared_ptr<vector<string>> get_file() const { return qrfile; } private: string qrword; shared_ptr<vector<string>> qrfile; shared_ptr<set<lineNo>> qrline; };