第十二章 动态内存

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!
");
65     data->pop_back();
66 }
67 
68 string& StrBlob::front()
69 {
70     check(0, "此StrBlob对象指向一个空vector!
");
71     return data->front();
72 }
73 
74 string& StrBlob::back()
75 {
76     check(0, "此StrBlob对象指向一个空vector!
");
77     return data->back();
78 }
79 
80 const string& StrBlob::front() const
81 {
82     check(0, "此StrBlob对象指向一个空vector!
");
83     cout << "调用对象为const StrBlob!
";
84     return data->front();
85 }
86 
87 const string& StrBlob::back() const
88 {
89     check(0, "此StrBlob对象指向一个空vector!
");
90     cout << "调用对象为const StrBlob!
";
91     return data->back();
92 }
93 #endif
StrBlob.h
 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 }
StrBlobCpp.cpp

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 }
View Code

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 }
View Code

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...
";
	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!
");
 58     data->pop_back();
 59 }
 60 
 61 string& StrBlob::front()
 62 {
 63     check(0, "此StrBlob对象指向一个空vector!
");
 64     return data->front();
 65 }
 66 
 67 string& StrBlob::back()
 68 {
 69     check(0, "此StrBlob对象指向一个空vector!
");
 70     return data->back();
 71 }
 72 
 73 const string& StrBlob::front() const
 74 {
 75     check(0, "此StrBlob对象指向一个空vector!
");
 76     cout << "调用对象为const StrBlob!
";
 77     return data->front();
 78 }
 79 
 80 const string& StrBlob::back() const
 81 {
 82     check(0, "此StrBlob对象指向一个空vector!
");
 83     cout << "调用对象为const StrBlob!
";
 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>对象不存在!
");
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, "当前下标不合法!
");
112     return (*p)[curr]; 
113 }
114 
115 StrBlobPtr& StrBlobPtr::incr()
116 {
117     check(curr, "不能继续递增了
");
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 }
View Code 

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!
");
 59     data->pop_back();
 60 }
 61 
 62 string& StrBlob::front()
 63 {
 64     check(0, "此StrBlob对象指向一个空vector!
");
 65     return data->front();
 66 }
 67 
 68 string& StrBlob::back()
 69 {
 70     check(0, "此StrBlob对象指向一个空vector!
");
 71     return data->back();
 72 }
 73 
 74 const string& StrBlob::front() const
 75 {
 76     check(0, "此StrBlob对象指向一个空vector!
");
 77     cout << "调用对象为const StrBlob!
";
 78     return data->front();
 79 }
 80 
 81 const string& StrBlob::back() const
 82 {
 83     check(0, "此StrBlob对象指向一个空vector!
");
 84     cout << "调用对象为const StrBlob!
";
 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>对象不存在!
");
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, "当前下标不合法!
");
114     return (*p)[curr]; 
115 }
116 
117 StrBlobPtr& StrBlobPtr::incr()
118 {
119     check(curr, "不能继续递增了
");
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 }
View Code

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 }
View Code

忘记释放内存,引以为戒!!! 

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 }
View Code

我的程序只读取与分配长度相等长度的字符串,多出部分会自动忽略。

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 }
View Code

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 << "	(line " << l + 1 << ") " << *(qr.file->begin() + l) << endl; 
	}
	return os;
}

int main()  
{  
	ifstream in("data.txt");
	TextQuery tq(in);
	cout << "请输入要查询的单词:
";
	string s;
	while (cin >> s) {
		cout << tq.query(s) << endl;
		cout << "请输入要查询的单词:
";
	}
	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 << "	(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!
");
	data->pop_back();
}

string& StrBlob::front()
{
	check(0, "此StrBlob对象指向一个空vector!
");
	return data->front();
}

string& StrBlob::back()
{
	check(0, "此StrBlob对象指向一个空vector!
");
	return data->back();
}

const string& StrBlob::front() const
{
	check(0, "此StrBlob对象指向一个空vector!
");
	cout << "调用对象为const StrBlob!
";
	return data->front();
}

const string& StrBlob::back() const
{
	check(0, "此StrBlob对象指向一个空vector!
");
	cout << "调用对象为const StrBlob!
";
	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 << "	(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!
";
		return -1;
	}
	TextQuery tq(in);
	cout << "请输入要查询的单词:
";
	string word;
	while (cin >> word) {
		cout << tq.query(word) << endl;
		cout << "请输入要查询的单词:
";
	}
	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;
};

 

原文地址:https://www.cnblogs.com/xzxl/p/7706088.html