第十二章 动态内存

练习12.1

在此代码的结尾,b1b2 各包含多少个元素?

  1. StrBlob b1;
  2. {
  3. StrBlob b2 = {"a", "an", "the"};
  4. b1 = b2;
  5. b2.push_back("about");
  6. }

解:

它们实际操作的是同一个vector,都包含4个元素。在代码的结尾,b2 被析构了,不影响 b1 的元素。

练习12.2

编写你自己的StrBlob 类,包含const 版本的 frontback

解:

头文件:

  1. #include <vector>
  2. #include <string>
  3. #include <initializer_list>
  4. #include <memory>
  5. #include <exception>
  6. using std::vector; using std::string;
  7. class StrBlob {
  8. public:
  9. using size_type = vector<string>::size_type;
  10. StrBlob():data(std::make_shared<vector<string>>()) { }
  11. StrBlob(std::initializer_list<string> il):data(std::make_shared<vector<string>>(il)) { }
  12. size_type size() const { return data->size(); }
  13. bool empty() const { return data->empty(); }
  14. void push_back(const string &t) { data->push_back(t); }
  15. void pop_back() {
  16. check(0, "pop_back on empty StrBlob");
  17. data->pop_back();
  18. }
  19. std::string& front() {
  20. check(0, "front on empty StrBlob");
  21. return data->front();
  22. }
  23. std::string& back() {
  24. check(0, "back on empty StrBlob");
  25. return data->back();
  26. }
  27. const std::string& front() const {
  28. check(0, "front on empty StrBlob");
  29. return data->front();
  30. }
  31. const std::string& back() const {
  32. check(0, "back on empty StrBlob");
  33. return data->back();
  34. }
  35. private:
  36. void check(size_type i, const string &msg) const {
  37. if (i >= data->size()) throw std::out_of_range(msg);
  38. }
  39. private:
  40. std::shared_ptr<vector<string>> data;
  41. };

主函数:

  1. #include "ex12_02.h"
  2. #include <iostream>
  3. int main()
  4. {
  5. const StrBlob csb{ "hello", "world", "pezy" };
  6. StrBlob sb{ "hello", "world", "Mooophy" };
  7. std::cout << csb.front() << " " << csb.back() << std::endl;
  8. sb.back() = "pezy";
  9. std::cout << sb.front() << " " << sb.back() << std::endl;
  10. }

练习12.3

StrBlob 需要const 版本的push_backpop_back吗?如需要,添加进去。否则,解释为什么不需要。

解:

不需要。push_backpop_back 会改变对象的内容。而 const 对象是只读的,因此不需要。

练习12.4

在我们的 check 函数中,没有检查 i 是否大于0。为什么可以忽略这个检查?

解:

因为 size_type 是一个无符号整型,当传递给 check 的参数小于 0 的时候,参数值会转换成一个正整数。

练习12.5

我们未编写接受一个 initializer_list explicit 参数的构造函数。讨论这个设计策略的优点和缺点。

解:

构造函数不是 explicit 的,意味着可以从 initializer_list 隐式转换为 StrBlob。在 StrBlob 对象中,只有一个数据成员 data,而 StrBlob 对象本身的含义,也是一个管理字符串的序列。因此,从 initializer_listStrBlob 的转换,在逻辑上是可行的。而这个设计策略的缺点,可能在某些地方我们确实需要 initializer_list,而编译器仍会将之转换为 StrBlob

练习12.6

编写函数,返回一个动态分配的 intvector。将此vector 传递给另一个函数,这个函数读取标准输入,将读入的值保存在 vector 元素中。再将vector传递给另一个函数,打印读入的值。记得在恰当的时刻delete vector

解:

  1. #include <iostream>
  2. #include <vector>
  3. using std::vector;
  4. vector<int>* alloc_vector()
  5. {
  6. return new vector<int>();
  7. }
  8. void assign_vector(vector<int>* p)
  9. {
  10. int i;
  11. while (std::cin >> i)
  12. {
  13. p->push_back(i);
  14. }
  15. }
  16. void print_vector(vector<int>* p)
  17. {
  18. for (auto i : *p)
  19. {
  20. std::cout << i << std::endl;
  21. }
  22. }
  23. int main()
  24. {
  25. auto p = alloc_vector();
  26. assign_vector(p);
  27. print_vector(p);
  28. delete p;
  29. return 0;
  30. }

练习12.7

重做上一题,这次使用 shared_ptr 而不是内置指针。

解:

  1. #include <iostream>
  2. #include <vector>
  3. #include <memory>
  4. using std::vector;
  5. std::shared_ptr<vector<int>> alloc_vector()
  6. {
  7. return std::make_shared<vector<int>>();
  8. }
  9. void assign_vector(std::shared_ptr<vector<int>> p)
  10. {
  11. int i;
  12. while (std::cin >> i)
  13. {
  14. p->push_back(i);
  15. }
  16. }
  17. void print_vector(std::shared_ptr<vector<int>> p)
  18. {
  19. for (auto i : *p)
  20. {
  21. std::cout << i << std::endl;
  22. }
  23. }
  24. int main()
  25. {
  26. auto p = alloc_vector();
  27. assign_vector(p);
  28. print_vector(p);
  29. return 0;
  30. }

练习12.8

下面的函数是否有错误?如果有,解释错误原因。

  1. bool b() {
  2. int* p = new int;
  3. // ...
  4. return p;
  5. }

解:

有错误。p会被强制转换成bool,继而没有释放指针 p 指向的对象。

练习12.9

解释下面代码执行的结果。

  1. int *q = new int(42), *r = new int(100);
  2. r = q;
  3. auto q2 = make_shared<int>(42), r2 = make_shared<int>(100);
  4. r2 = q2;

解:

rq 指向 42,而之前 r 指向的 100 的内存空间并没有被释放,因此会发生内存泄漏。r2q2 都是智能指针,当对象空间不被引用的时候会自动释放。

练习12.10

下面的代码调用了第413页中定义的process 函数,解释此调用是否正确。如果不正确,应如何修改?

  1. shared_ptr<int> p(new int(42));
  2. process(shared_ptr<int>(p));

解:

正确。shared_ptr<int>(p) 会创建一个临时的智能指针,这个智能指针与 p 引用同一个对象,此时引用计数为 2。当表达式结束时,临时的智能指针被销毁,此时引用计数为 1。

练习12.11

如果我们像下面这样调用 process,会发生什么?

  1. process(shared_ptr<int>(p.get()));

解:

这样会创建一个新的智能指针,它的引用计数为 1,这个智能指针所指向的空间与 p 相同。在表达式结束后,这个临时智能指针会被销毁,引用计数为 0,所指向的内存空间也会被释放。而导致 p 所指向的空间被释放,使得 p` 成为一个空悬指针。

练习12.12

psp 的定义如下,对于接下来的对 process 的每个调用,如果合法,解释它做了什么,如果不合法,解释错误原因:

  1. auto p = new int();
  2. auto sp = make_shared<int>();
  3. (a) process(sp);
  4. (b) process(new int());
  5. (c) process(p);
  6. (d) process(shared_ptr<int>(p));

解:

  • (a) 合法。将sp 拷贝给 process函数的形参,在函数里面引用计数为 2,函数结束后引用计数为 1。
  • (b) 不合法。不能从内置指针隐式转换为智能指针。
  • (c) 不合法。不能从内置指针隐式转换为智能指针。
  • (d) 合法。但是智能指针和内置指针一起使用可能会出现问题,在表达式结束后智能指针会被销毁,它所指向的对象也被释放。而此时内置指针 p 依旧指向该内存空间。之后对内置指针 p 的操作可能会引发错误。

练习12.13

如果执行下面的代码,会发生什么?

  1. auto sp = make_shared<int>();
  2. auto p = sp.get();
  3. delete p;

解:

智能指针 sp 所指向空间已经被释放,再对 sp 进行操作会出现错误。

练习12.14

编写你自己版本的用 shared_ptr 管理 connection 的函数。

解:

  1. #include <iostream>
  2. #include <memory>
  3. #include <string>
  4. struct connection
  5. {
  6. std::string ip;
  7. int port;
  8. connection(std::string i, int p) : ip(i), port(p) {}
  9. };
  10. struct destination
  11. {
  12. std::string ip;
  13. int port;
  14. destination(std::string i, int p) : ip(i), port(p) {}
  15. };
  16. connection connect(destination* pDest)
  17. {
  18. std::shared_ptr<connection> pConn(new connection(pDest->ip, pDest->port));
  19. std::cout << "creating connection(" << pConn.use_count() << ")" << std::endl;
  20. return *pConn;
  21. }
  22. void disconnect(connection pConn)
  23. {
  24. std::cout << "connection close(" << pConn.ip << ":" << pConn.port << ")" << std::endl;
  25. }
  26. void end_connection(connection* pConn)
  27. {
  28. disconnect(*pConn);
  29. }
  30. void f(destination &d)
  31. {
  32. connection conn = connect(&d);
  33. std::shared_ptr<connection> p(&conn, end_connection);
  34. std::cout << "connecting now(" << p.use_count() << ")" << std::endl;
  35. }
  36. int main()
  37. {
  38. destination dest("220.181.111.111", 10086);
  39. f(dest);
  40. return 0;
  41. }

练习12.15

重写上一题的程序,用 lambda 代替end_connection 函数。

解:

  1. #include <iostream>
  2. #include <memory>
  3. #include <string>
  4. struct connection
  5. {
  6. std::string ip;
  7. int port;
  8. connection(std::string i, int p) : ip(i), port(p) {}
  9. };
  10. struct destination
  11. {
  12. std::string ip;
  13. int port;
  14. destination(std::string i, int p) : ip(i), port(p) {}
  15. };
  16. connection connect(destination* pDest)
  17. {
  18. std::shared_ptr<connection> pConn(new connection(pDest->ip, pDest->port));
  19. std::cout << "creating connection(" << pConn.use_count() << ")" << std::endl;
  20. return *pConn;
  21. }
  22. void disconnect(connection pConn)
  23. {
  24. std::cout << "connection close(" << pConn.ip << ":" << pConn.port << ")" << std::endl;
  25. }
  26. void f(destination &d)
  27. {
  28. connection conn = connect(&d);
  29. std::shared_ptr<connection> p(&conn, [] (connection* p){ disconnect(*p); });
  30. std::cout << "connecting now(" << p.use_count() << ")" << std::endl;
  31. }
  32. int main()
  33. {
  34. destination dest("220.181.111.111", 10086);
  35. f(dest);
  36. return 0;
  37. }

练习12.16

如果你试图拷贝或赋值 unique_ptr,编译器并不总是能给出易于理解的错误信息。编写包含这种错误的程序,观察编译器如何诊断这种错误。

解:

  1. #include <iostream>
  2. #include <string>
  3. #include <memory>
  4. using std::string; using std::unique_ptr;
  5. int main()
  6. {
  7. unique_ptr<string> p1(new string("pezy"));
  8. // unique_ptr<string> p2(p1); // copy
  9. // ^
  10. // Error: Call to implicitly-deleted copy constructor of 'unique_ptr<string>'
  11. //
  12. // unique_ptr<string> p3 = p1; // assign
  13. // ^
  14. // Error: Call to implicitly-deleted copy constructor of 'unique_ptr<string>'
  15. std::cout << *p1 << std::endl;
  16. p1.reset(nullptr);
  17. }

练习12.17

下面的 unique_ptr 声明中,哪些是合法的,哪些可能导致后续的程序错误?解释每个错误的问题在哪里。

  1. int ix = 1024, *pi = &ix, *pi2 = new int(2048);
  2. typedef unique_ptr<int> IntP;
  3. (a) IntP p0(ix);
  4. (b) IntP p1(pi);
  5. (c) IntP p2(pi2);
  6. (d) IntP p3(&ix);
  7. (e) IntP p4(new int(2048));
  8. (f) IntP p5(p2.get());

解:

  • (a) 不合法。在定义一个 unique_ptr 时,需要将其绑定到一个new 返回的指针上。
  • (b) 不合法。理由同上。
  • (c) 合法。但是也可能会使得 pi2 成为空悬指针。
  • (d) 不合法。当 p3 被销毁时,它试图释放一个栈空间的对象。
  • (e) 合法。
  • (f) 不合法。p5p2 指向同一个对象,当 p5p2 被销毁时,会使得同一个指针被释放两次。

练习12.18

shared_ptr 为什么没有 release 成员?

release 成员的作用是放弃控制权并返回指针,因为在某一时刻只能有一个 unique_ptr 指向某个对象,unique_ptr 不能被赋值,所以要使用 release 成员将一个 unique_ptr 的指针的所有权传递给另一个 unique_ptr。而 shared_ptr 允许有多个 shared_ptr 指向同一个对象,因此不需要 release 成员。

练习12.19

定义你自己版本的 StrBlobPtr,更新 StrBlob 类,加入恰当的 friend 声明以及 beginend 成员。

解:

  1. #include <string>
  2. #include <vector>
  3. #include <initializer_list>
  4. #include <memory>
  5. #include <stdexcept>
  6. using std::vector; using std::string;
  7. class StrBlobPtr;
  8. class StrBlob
  9. {
  10. public:
  11. using size_type = vector<string>::size_type;
  12. friend class StrBlobPtr;
  13. StrBlobPtr begin();
  14. StrBlobPtr end();
  15. StrBlob() : data(std::make_shared<vector<string>>()) {}
  16. StrBlob(std::initializer_list<string> il) : data(std::make_shared<vector<string>>(il)) {}
  17. size_type size() const { return data->size(); }
  18. bool empty() const { return data->empty(); }
  19. void push_back(const string& s) { data->push_back(s); }
  20. void pop_back()
  21. {
  22. check(0, "pop_back on empty StrBlob");
  23. data->pop_back();
  24. }
  25. std::string& front()
  26. {
  27. check(0, "front on empty StrBlob");
  28. return data->front();
  29. }
  30. std::string& back()
  31. {
  32. check(0, "back on empty StrBlob");
  33. return data->back();
  34. }
  35. const std::string& front() const
  36. {
  37. check(0, "front on empty StrBlob");
  38. return data->front();
  39. }
  40. const std::string& back() const
  41. {
  42. check(0, "back on empty StrBlob");
  43. return data->back();
  44. }
  45. private:
  46. void check(size_type i, const string& msg) const
  47. {
  48. if (i >= data->size())
  49. throw std::out_of_range(msg);
  50. }
  51. private:
  52. std::shared_ptr<vector<string>> data;
  53. };
  54. class StrBlobPtr
  55. {
  56. public:
  57. StrBlobPtr() :curr(0) {}
  58. StrBlobPtr(StrBlob &a, size_t sz = 0) :wptr(a.data), curr(sz) {}
  59. bool operator!=(const StrBlobPtr& p) { return p.curr != curr; }
  60. string& deref() const
  61. {
  62. auto p = check(curr, "dereference past end");
  63. return (*p)[curr];
  64. }
  65. StrBlobPtr& incr()
  66. {
  67. check(curr, "increment past end of StrBlobPtr");
  68. ++curr;
  69. return *this;
  70. }
  71. private:
  72. std::shared_ptr<vector<string>> check(size_t i, const string &msg) const
  73. {
  74. auto ret = wptr.lock();
  75. if (!ret) throw std::runtime_error("unbound StrBlobPtr");
  76. if (i >= ret->size()) throw std::out_of_range(msg);
  77. return ret;
  78. }
  79. std::weak_ptr<vector<string>> wptr;
  80. size_t curr;
  81. };
  82. StrBlobPtr StrBlob::begin()
  83. {
  84. return StrBlobPtr(*this);
  85. }
  86. StrBlobPtr StrBlob::end()
  87. {
  88. return StrBlobPtr(*this, data->size());
  89. }

练习12.20

编写程序,逐行读入一个输入文件,将内容存入一个 StrBlob 中,用一个 StrBlobPtr 打印出 StrBlob 中的每个元素。

解:

  1. #include <iostream>
  2. #include <fstream>
  3. #include "exercise12_19.h"
  4. using namespace std;
  5. int main()
  6. {
  7. ifstream ifs("books.txt");
  8. StrBlob sb;
  9. string s;
  10. while (getline(ifs, s))
  11. {
  12. sb.push_back(s);
  13. }
  14. for (StrBlobPtr sbp = sb.begin(); sbp != sb.end(); sbp.incr())
  15. {
  16. cout << sbp.deref() << endl;
  17. }
  18. return 0;
  19. }

练习12.21

也可以这样编写 StrBlobPtrderef 成员:

  1. std::string& deref() const {
  2. return (*check(curr, "dereference past end"))[curr];
  3. }

你认为哪个版本更好?为什么?

解:

原来的版本更好,可读性更高。

练习12.22

为了能让 StrBlobPtr 使用 const StrBlob,你觉得应该如何修改?定义一个名为ConstStrBlobPtr 的类,使其能够指向 const StrBlob

解:

构造函数改为接受 const Strblob & , 然后给 Strblob 类添加两个 const 成员函数 cbegincend,返回 ConstStrBlobPtr

练习12.23

编写一个程序,连接两个字符串字面常量,将结果保存在一个动态分配的char数组中。重写这个程序,连接两个标准库string对象。

解:

  1. #include <iostream>
  2. #include <string>
  3. #include <cstring>
  4. #include <memory>
  5. int main() {
  6. const char *c1 = "Hello ";
  7. const char *c2 = "World";
  8. unsigned len = strlen(c1) + strlen(c2) + 1;
  9. char *r = new char[len]();
  10. strcat_s(r, len, c1);
  11. strcat_s(r, len, c2);
  12. std::cout << r << std::endl;
  13. std::string s1 = "Hello ";
  14. std::string s2 = "World";
  15. strcpy_s(r, len, (s1 + s2).c_str());
  16. std::cout << r << std::endl;
  17. delete[] r;
  18. return 0;
  19. }

练习12.24

编写一个程序,从标准输入读取一个字符串,存入一个动态分配的字符数组中。描述你的程序如何处理变长输入。测试你的程序,输入一个超出你分配的数组长度的字符串。

解:

  1. #include <iostream>
  2. int main()
  3. {
  4. std::cout << "How long do you want the string? ";
  5. int size{ 0 };
  6. std::cin >> size;
  7. char *input = new char[size + 1]();
  8. std::cin.ignore();
  9. std::cout << "input the string: ";
  10. std::cin.get(input, size + 1);
  11. std::cout << input;
  12. delete[] input;
  13. return 0;
  14. }

练习12.25

给定下面的new表达式,你应该如何释放pa

  1. int *pa = new int[10];

解:

  1. delete [] pa;

练习12.26

allocator 重写第427页中的程序。

  1. #include <iostream>
  2. #include <string>
  3. #include <memory>
  4. using namespace std;
  5. int main()
  6. {
  7. int n = 5;
  8. allocator<string> alloc;
  9. auto p = alloc.allocate(n);
  10. string s;
  11. auto q = p;
  12. while (cin >> s && q != p + n)
  13. {
  14. alloc.construct(q++, s);
  15. }
  16. while (q != p)
  17. {
  18. std::cout << *--q << " ";
  19. alloc.destroy(q);
  20. }
  21. alloc.deallocate(p, n);
  22. return 0;
  23. }

练习12.27

TextQueryQueryResult 类只使用了我们已经介绍过的语言和标准库特性。不要提前看后续章节内容,只用已经学到的知识对这两个类编写你自己的版本。

解:

头文件:

  1. #ifndef EX12_27_H
  2. #define EX12_27_H
  3. #include <fstream>
  4. #include <memory>
  5. #include <vector>
  6. #include <string>
  7. #include <map>
  8. #include <set>
  9. class QueryResult;
  10. class TextQuery
  11. {
  12. public:
  13. using line_no = std::vector<std::string>::size_type;
  14. TextQuery(std::ifstream&);
  15. QueryResult query(const std::string& s) const;
  16. private:
  17. std::shared_ptr<std::vector<std::string>> file;
  18. std::map<std::string, std::shared_ptr<std::set<line_no>>> wm;
  19. };
  20. class QueryResult
  21. {
  22. public:
  23. friend std::ostream& print(std::ostream&, const QueryResult&);
  24. QueryResult(std::string s,
  25. std::shared_ptr<std::set<TextQuery::line_no>> p,
  26. std::shared_ptr<std::vector<std::string>> f) :
  27. sought(s), lines(p), file(f)
  28. {}
  29. private:
  30. std::string sought;
  31. std::shared_ptr<std::set<TextQuery::line_no>> lines;
  32. std::shared_ptr<std::vector<std::string>> file;
  33. };
  34. std::ostream& print(std::ostream&, const QueryResult&);
  35. #endif

实现:

  1. #include "ex_12_27.h"
  2. #include <sstream>
  3. #include <fstream>
  4. #include <vector>
  5. #include <string>
  6. using namespace std;
  7. TextQuery::TextQuery(ifstream& ifs) : file(new vector<string>)
  8. {
  9. string text;
  10. while (getline(ifs, text))
  11. {
  12. file->push_back(text);
  13. int n = file->size() - 1;
  14. istringstream line(text);
  15. string word;
  16. while (line >> word)
  17. {
  18. auto &lines = wm[word];
  19. if (!lines)
  20. lines.reset(new set<line_no>);
  21. lines->insert(n);
  22. }
  23. }
  24. }
  25. QueryResult TextQuery::query(const string& s) const
  26. {
  27. static shared_ptr<set<line_no>> nodata(new set<line_no>);
  28. auto loc = wm.find(s);
  29. if (loc == wm.end())
  30. return QueryResult(s, nodata, file);
  31. else
  32. return QueryResult(s, loc->second, file);
  33. }
  34. std::ostream& print(std::ostream& os, const QueryResult& qr)
  35. {
  36. os << qr.sought << " occurs " << qr.lines->size() << " "
  37. << "time" << (qr.lines->size() > 1 ? "s" : "") << endl;
  38. for (auto num : *qr.lines)
  39. os << "\t(line " << num + 1 << ") " << *(qr.file->begin() + num) << endl;
  40. return os;
  41. }

主函数:

  1. #include <iostream>
  2. #include <string>
  3. #include <fstream>
  4. #include "ex_12_27.h"
  5. using namespace std;
  6. void runQueries(ifstream& infile)
  7. {
  8. TextQuery tq(infile);
  9. while (true)
  10. {
  11. cout << "enter word to look for, or q to quit: ";
  12. string s;
  13. if (!(cin >> s) || s == "q") break;
  14. print(cout, tq.query(s)) << endl;
  15. }
  16. }
  17. int main()
  18. {
  19. ifstream ifs("storyDataFile.txt");
  20. runQueries(ifs);
  21. return 0;
  22. }

练习12.28

编写程序实现文本查询,不要定义类来管理数据。你的程序应该接受一个文件,并与用户交互来查询单词。使用vectormapset 容器来保存来自文件的数据并生成查询结果。

解:

  1. #include <string>
  2. using std::string;
  3. #include <vector>
  4. using std::vector;
  5. #include <memory>
  6. using std::shared_ptr;
  7. #include <iostream>
  8. #include <fstream>
  9. #include <sstream>
  10. #include <map>
  11. #include <set>
  12. #include <algorithm>
  13. int main()
  14. {
  15. std::ifstream file("H:/code/C++/Cpp_Primer_Answers/data/storyDataFile.txt");
  16. vector<string> input;
  17. std::map<string, std::set<decltype(input.size())>> dictionary;
  18. decltype(input.size()) lineNo{ 0 };
  19. for (string line; std::getline(file, line); ++lineNo)
  20. {
  21. input.push_back(line);
  22. std::istringstream line_stream(line);
  23. for (string text, word; line_stream >> text; word.clear())
  24. {
  25. std::remove_copy_if(text.begin(), text.end(), std::back_inserter(word), ispunct);
  26. dictionary[word].insert(lineNo);
  27. }
  28. }
  29. while (true)
  30. {
  31. std::cout << "enter word to look for, or q to quit: ";
  32. string s;
  33. if (!(std::cin >> s) || s == "q") break;
  34. auto found = dictionary.find(s);
  35. if (found != dictionary.end())
  36. {
  37. std::cout << s << " occurs " << found->second.size() << (found->second.size() > 1 ? " times" : " time") << std::endl;
  38. for (auto i : found->second)
  39. std::cout << "\t(line " << i + 1 << ") " << input.at(i) << std::endl;
  40. }
  41. else std::cout << s << " occurs 0 time" << std::endl;
  42. }
  43. }

练习12.29

我们曾经用do while 循环来编写管理用户交互的循环。用do while 重写本节程序,解释你倾向于哪个版本,为什么?

解:

  1. do {
  2. std::cout << "enter word to look for, or q to quit: ";
  3. string s;
  4. if (!(std::cin >> s) || s == "q") break;
  5. print(std::cout, tq.query(s)) << std::endl;
  6. } while ( true );

我更喜欢 while,这可能是习惯的问题。

练习12.30

定义你自己版本的 TextQueryQueryResult 类,并执行12.3.1节中的runQueries 函数。

解:

同12.27。

练习12.31

如果用vector 代替 set 保存行号,会有什么差别?哪个方法更好?为什么?

如果用 vector 则会有单词重复的情况出现。而这里保存的是行号,不需要重复元素,所以 set 更好。

练习12.32

重写 TextQueryQueryResult类,用StrBlob 代替 vector<string> 保存输入文件。

解:

TextQueryQueryResult 类中的 file 成员,改为 指向 StrBlob 的智能指针。在访问 StrBlob 时,要使用 StrBlobPtr

练习12.33

在第15章中我们将扩展查询系统,在 QueryResult 类中将会需要一些额外的成员。添加名为 beginend 的成员,返回一个迭代器,指向一个给定查询返回的行号的 set 中的位置。再添加一个名为 get_file 的成员,返回一个 shared_ptr,指向 QueryResult 对象中的文件。

解:

  1. class QueryResult{
  2. public:
  3. using Iter = std::set<line_no>::iterator;
  4. // ...
  5. Iter begin() const { return lines->begin(); }
  6. Iter end() const { return lines->end(); }
  7. shared_ptr<std::vector<std::string>> get_file() const
  8. {
  9. return std::make_shared<std::vector<std::string>>(file);
  10. }
  11. private:
  12. // ...
  13. };