部分习题解答:
12.1:
StrBlob的data成员是一个指向string的vector的shared_ptr,因此StrBlob的赋值不会拷贝vector的内容,而是多个StrBlob对象共享同一个vector对象。因此经过一些列操作以后,b1和b2均包含4个string。
12.2:
#include <iostream>
#include <algorithm>
#include <memory>
#include "myStrBlob.h"
using namespace std;
int main(int argc, char**argv)
{
StrBlob b1;
{ //新作用域
StrBlob b2 = { "a", "an", "the" };
b1 = b2;
b2.push_back("about");
cout << b2.size() << endl;
}
cout << b1.size() << endl;
cout << b1.front() << " " << b1.back() << endl;
StrBlob b3 = b1;
cout << b3.front() << " " << b3.back() << endl;
/*StrBlob sb;
sb.push_back("ssss");
sb.push_back("ssss2");
sb.push_back("ssss3");
sb.push_back("ssss4");
cout << sb.back() << " " << sb.front();
sb.pop_back();
cout << "\n" << sb.back() << endl;
cout << sb.empty() << endl;*/
system("pause");
return 0;
}
//myStrBlob.h
#ifndef MY_STRBLOB_H
#define MY_STRBLOB_H
#include <iostream>
#include <vector>
#include <string>
#include <initializer_list>
#include <memory>
using namespace std;
class StrBlob {
public:
typedef vector<string>::size_type size_type;
StrBlob();
StrBlob(initializer_list<string> il);
size_type size() const { return data->size(); }
bool empty() const { return data->empty(); }
//添加和删除元素
void push_back(const string &t) { data->push_back(t); }
void pop_back();
//元素访问
string& front();
string& back();
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);
}
}
//得到首元素
string& StrBlob::front()
{
//如果vector为空,check会抛出一个异常
check(0, "front on empty StrBlob");
return data->front();
}
//得到尾元素
string& StrBlob::back()
{
check(0, "back on empty StrBlob");
return data->back();
}
//将元素弹出栈
void StrBlob::pop_back()
{
check(0, "pop_back on empty StrBlob");
data->pop_back();
}
#endif
12.3:
不需要,push_back和pop_back会改变对象的内容,而const对象是只读的,因此不需要const版本
12.4:
因为size_type是一个无符号整数,当传递给check的参数小于0的时候,参数值会转换成一个正整数
12.5:
构造函数不是 explicit 的,意味着可以从 initializer_list 隐式转换为 StrBlob。在 StrBlob 对象中,只有一个数据成员 data,而 StrBlob 对象本身的含义,也是一个管理字符串的序列。因此,从 initializer_list 到 StrBlob 的转换,在逻辑上是可行的。而这个设计策略的缺点,可能在某些地方我们确实需要 initializer_list,而编译器仍会将之转换为 StrBlob。
12.6:
#include <iostream>
#include <algorithm>
#include <memory>
#include <vector>
#include <new>
using namespace std;
vector<int>* new_vector()
{
return new(nothrow) vector<int>;
}
void read_vector(vector<int> *pv)
{
int n;
while (cin >> n)
{
pv->push_back(n);
}
}
void output_vector(vector<int> *pv)
{
for (auto w : *pv)
{
cout << w << " ";
}
}
int main(int argc, char**argv)
{
vector<int> *pv = new_vector();
read_vector(pv);
output_vector(pv);
delete pv;
pv = nullptr;
system("pause");
return 0;
}
12.7:
#include <iostream>
#include <algorithm>
#include <memory>
#include <vector>
#include <new>
using namespace std;
shared_ptr<vector<int>> new_vector()
{
return make_shared<vector<int>>();
}
void read_vector(shared_ptr<vector<int>> pv)
{
int n;
while (cin >> n)
{
pv->push_back(n);
}
}
void output_vector(shared_ptr<vector<int>> pv)
{
for (auto &w : *pv)
{
cout << w << " ";
}
cout << endl;
}
int main(int argc, char**argv)
{
auto pv = new_vector();
read_vector(pv);
output_vector(pv);
system("pause");
return 0;
}
12.8:
该程序通过new返回的指针值来区分内存分配成功或失败——成功返回一个合法指针,转换为整型是一个非零值,可转换为bool值true;分配失败,p得到nullptr,其整型值是0,可转换为bool值false.
但普通new调用在分配失败时抛出一个异常bad_alloc,而不是返回nullptr,因此程序不能达到预想目的。
可将new int改为new (nothrow) int 来令new在分配失败时不抛出异常,而是返回nullptr。但这仍然不是一个好方法,应该通过捕获异常或是判断返回的指针来判断true或false,而不是依赖类型转换。
12.9:
q,r为内置类型指针,保存动态内存的地址,进行二次赋值之后,r原来指向的内存空间将得不到释放,造成内存泄漏
q2,r2为智能指针,r2进行赋值之后,其计数器将减一,由于r2是指向该内存空间的唯一智能指针,所以该内存会得到释放
12.10:
此调用是正确的,利用p创建一个临时的shared_ptr赋予了process的参数ptr,p和ptr都指向相同的int对象,引用计数被正确地置为2。process执行完毕后,ptr被销毁,引用计数减1,这是正确的——只有p指向它。
12.11:
此调用是错误的。p.get()获得一个普通指针,指向p所共享的int对象。利用此指针创建一个shared_ptr,而不是利用p创建一个shared_ptr,将不会形成正确的动态对象共享。编译器会认为p和ptr是使用两个地址(虽然他们相等)创建的两个不相干的shared_ptr,而非共享同一个动态对象。这样,两者的引用计数均为1。当process执行完毕后,ptr的引用计数减为0,所管理的内存地址被释放,而此内存就是p所管理的。p成为一个管理空悬指针的shared_ptr。
12.12:
(a)合法 (b)不合法,参数必须是智能指针类型,不能用构造函数隐式转换
(c)不合法,同上 (d)合法,处理完内存被释放
12.13:
删除p之后,导致p指向的内存被释放,此时sp就会变成空悬指针,在sp指针被销毁时,该块内存会被二次delete
12.14、12.15:
#include <iostream>
#include <string>
#include <memory>
using namespace std;
//表示我们正在连接什么
struct destination{
string ip;
int port;
destination(string i,int p):ip(i),port(p){}
};
struct connection{
string ip;
int port;
connection(string i,int p):ip(i),port(p){}
}; //使用连接所需的信息
//打开连接
connection connect(destination* pv)
{
shared_ptr<connection> sp(new connection(pv->ip, pv->port));
cout << "start create connection to " << pv->ip << ":" << pv->port << endl;
return *sp;
}
//关闭给定的连接
void disconnect(connection c)
{
cout << "you have disconnect to " << c.ip << ":" << c.port << endl;
}
//函数相当于delete操作
void end_connection(connection *p)
{
disconnect(*p);
}
//使用shared_ptr来管理一个connection
void f(destination &d)
{
connection c = connect(&d);
shared_ptr<connection> p(&c, end_connection);
//12.15 shared_ptr<connection> p(&c, [](connection *p) {disconnect(*p); });
cout << "you have connect to " << d.ip << ":" << d.port << endl;
}
int main(int argc,char *argv[])
{
destination pv("192.168.1.5", 3430);
f(pv);
system("pause");
return 0;
}
12.18:
unique_ptr独占对象的所有权,不能拷贝和赋值。release操作是用来将对象的所有权转移给另一个unique_ptr的。
而多个shared_ptr可以共享对象的所有权。需要共享时,可以简单拷贝和赋值。因此,并不需要release这样的操作来转移所有权。
12.19:
//myStrBlob.h
#ifndef MY_STRBLOB_H
#define MY_STRBLOB_H
#include <iostream>
#include <vector>
#include <string>
#include <initializer_list>
#include <memory>
using namespace std;
class StrBlobPtr;
class StrBlob {
friend class StrBlobPtr;
public:
typedef vector<string>::size_type size_type;
StrBlob();
StrBlob(initializer_list<string> il);
size_type size() const { return data->size(); }
bool empty() const { return data->empty(); }
//添加和删除元素
void push_back(const string &t) { data->push_back(t); }
void pop_back();
//元素访问
string& front();
string& back();
StrBlobPtr begin();
StrBlobPtr end();
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);
}
}
//得到首元素
string& StrBlob::front()
{
//如果vector为空,check会抛出一个异常
check(0, "front on empty StrBlob");
return data->front();
}
//得到尾元素
string& StrBlob::back()
{
check(0, "back on empty StrBlob");
return data->back();
}
//将元素弹出栈
void StrBlob::pop_back()
{
check(0, "pop_back on empty StrBlob");
data->pop_back();
}
class StrBlobPtr {
public:
StrBlobPtr() : curr(0) { }
StrBlobPtr(StrBlob &a, size_t sz = 0) : wptr(a.data), curr(sz) { }
string& deref() const;
StrBlobPtr& incr(); //前缀递增
private:
// 若检查成功,check返回一个指向vector的shared_ptr
shared_ptr<vector<string>> check(size_t, const string&) const;
// 保存一个weak_ptr,意味着底层vector可能会被销毁
weak_ptr<vector<string>> wptr;
size_t curr;
};
inline shared_ptr<vector<string>> StrBlobPtr::check(size_t i, const string &msg) const
{
auto ret = wptr.lock(); //vector还存在吗?
if (!ret)
throw runtime_error("unbound StrBlobPtr");
if (i >= ret->size())
throw out_of_range(msg);
return ret; //否则,返回指向vector的shared_ptr
}
inline string& StrBlobPtr::deref() const
{
auto p = check(curr, "dereference past end");
return (*p)[curr]; //(*p)是对象所指的vector
}
// 前缀递增:返回递增后的对象的引用
inline StrBlobPtr& StrBlobPtr::incr()
{
// 如果curr已经指向容器的尾后位置,就不递增它
check(curr, "increment psat end of StrBlobPtr");
++curr; //推进当前位置
return *this;
}
StrBlobPtr StrBlob::begin() { return StrBlobPtr(*this); }
StrBlobPtr StrBlob::end() { return StrBlobPtr(*this, data->size()); }
#endif
12.20:
#include <iostream>
#include <fstream>
#include "my_StrBlob.h"
using namespace std;
int main(int argc, char **argv)
{
ifstream in(argv[1]);
if (!in) {
cout<<"Open input file failed"<<endl;
return -1;
}
StrBlob b;
string s;
while (getline(in, s))
b.push_back(s);
for (auto it = b.begin(); neq(it, b.end()); it.incr())
cout<<it.deref()<<endl;
syatem("pause");
return 0;
}
12.21:
原版本更好,将合法性检查语句与元素获取的返回语句分离开来,代码更清晰
12.23:
#include <iostream>
#include <cstring>
#include <string>
using namespace std;
/*char* connect(const char *a, const char *b)
{
auto p = new char[strlen(a) + strlen(b)];
int i = 0;
for (; i < strlen(a); ++i)
{
p[i] = a[i];
}
for (int j = 0; j < strlen(b); ++j)
{
p[i + j] = b[j];
}
return p;
}*/
string connect(string a, string b)
{
return a + b;
}
int main(int argc,char *argv[])
{
auto p = connect("hello", "world");
/*for (int i = 0; i < strlen(p);++i)
{
cout << p[i] << " ";
}
delete[] p;*/
cout << p;
system("pause");
return 0;
}
12.24:
char* input()
{
int i = 0, size = 10;
char c;
auto p = new char[10];
while (cin >> c)
{
p[i] = c;
++i;
if (i >= size) //如果输入的字符大于原来的字符长度,则将长度重新分配
{
size *= 2;
auto tmp = new char[size]; //重新分配内存
for (int j = 0; j < size; ++j)
{
tmp[j] = p[j];
}
delete[] p; //将原来分配的内存删除,将p指向新分配的内存
p = tmp;
}
}
p[i] = '\0';
return p;
}
int main(int argc,char *argv[])
{
auto p = input();
cout << p << endl;
system("pause");
return 0;
}
12.26:
#include <iostream>
#include <cstring>
#include <string>
#include <memory>
using namespace std;
#define n 120
int main(int argc,char *argv[])
{
allocator<string> alloc;
auto const p = alloc.allocate(n);
string s;
auto q = p;
while (cin >> s && q != p + n)
{
alloc.construct(q++, s);
}
const size_t size = q - p;
for (auto i = 0; i < size; ++i)
{
cout << p[i]<< " ";
}
while (q != p)
{
alloc.destroy(--q);
}
alloc.deallocate(p, size);
system("pause");
return 0;
}
12.27:
#ifndef TEXTQUERY_H
#define TEXTQUERY_H
#include <iostream>
#include <vector>
#include <string>
#include <map>
#include <set>
#include <fstream>
#include <sstream>
#include "QueryResult.h"
using namespace std;
class QueryResult;
class TextQuery {
public:
TextQuery(ifstream& input);
QueryResult query(const string s);
private:
vector<string>* text;
map<string, set<int>> mp;
};
TextQuery::TextQuery(ifstream& input)
{
text = new vector<string>();
string s;
string word;
int line = 0;
while (getline(input, s))
{
text->push_back(s);
istringstream in(s);
while (in >> word)
{
mp[word].insert(line);
}
++line;
}
}
QueryResult TextQuery::query(const string s)
{
return QueryResult(*this, s);
}
#endif
#ifndef QUERYRESULT_H
#define QUERYRESULT_H
#include <iostream>
#include <vector>
#include <string>
#include <map>
#include <set>
#include <fstream>
#include <sstream>
#include "TextQuery.h"
using namespace std;
class QueryResult {
friend ostream& print(ostream& os, QueryResult& p);
public:
QueryResult(TextQuery& t, string& s) {
l = t.lines[s];
text = t.text;
};
private:
set<int> st; //出现的行号
vector<string>* text; //输入文件
};
ostream& print(ostream& os, QueryResult& q)
{
if (q.st.size() == 0)
os << "word not found" << endl;
else
for (auto i : q.st)
cout << "(line " << i << ") " << (*q.text)[i] << endl;
return os;
}
#endif // !QUERYRESULT_H
12.28:
//功能不完善,有漏洞
#include <iostream>
#include <vector>
#include <string>
#include <map>
#include <set>
#include <fstream>
#include <sstream>
using namespace std;
int main(int argc,char *argv[])
{
vector<string> text;
map<string, set<int>> mp;
ifstream infile("Data.txt");
string s;
int line = 0;
while (infile >> s)
{
text.push_back(s);
string tmp;
istringstream im(s);
while (im >> tmp)
{
mp[tmp].insert(line);
}
++line;
}
while (true)
{
cout << "enter word to look for, or q to quit: ";
string s;
if (!(cin >> s) || s == "q") break;
auto ret = mp[s];
if (ret.size() == 0)
{
cout << "word " << s << " not found!" << endl;
}
else
{
for (auto w : ret)
{
cout << "(line " << w << ")" << text[w] << endl; //单词出现在第几位
}
}
}
system("pause");
return 0;
}
12.31:
用set,自动去重排序
12.30、12.31、12.32:
#ifndef TEXTQUERY_H
#define TEXTQUERY_H
#include <iostream>
#include <vector>
#include <string>
#include <map>
#include <set>
#include <fstream>
#include <sstream>
#include "myStrBlob.h"
#include "QueryResult.h"
using namespace std;
class QueryResult;
class TextQuery {
public:
using line_no = StrBlob::size_type;
TextQuery(ifstream&); //读取文本,建立联系
QueryResult query(const string&) const;
private:
shared_ptr<StrBlob> file; //输入文件
map<string, shared_ptr<set<line_no>>> wm; //单词与行号关联的map
};
TextQuery::TextQuery(ifstream& is) : file(new StrBlob)
{
string text;
while (getline(is, text)) //文件中的每一行,按行读取
{
file->push_back(text); //保存该行文本
int n = file->size() - 1; //当前文本行号
istringstream line(text); //对于行中的每一个单词
string word;
while (line >> word)
{
auto &lines = wm[word]; //得到一个shared_ptr指针
if (!lines) //如果该指针为空,则分配一个新的set
lines.reset(new set<line_no>);
lines->insert(n); //将行号插入set中
}
}
}
QueryResult TextQuery::query(const string &s) const
{
//如果未找到给定单词,则返回一个指针
static shared_ptr<set<line_no>> nodata(new set<line_no>);
//使用find查找,避免将单词插入到wm中
auto loc = wm.find(s);
if (loc == wm.end())
{
return QueryResult(s, nodata, file);
}
else
return QueryResult(s, loc->second, file);
}
#endif
#ifndef QUERYRESULT_H
#define QUERYRESULT_H
#include <iostream>
#include <vector>
#include <string>
#include <map>
#include <set>
#include <fstream>
#include <sstream>
#include "myStrBlob.h"
using namespace std;
string make_plural(size_t ctr, const string &word, const string &ending)
{
return (ctr > 1) ? word + ending : word;
}
class StrBlob;
class QueryResult {
friend ostream& print(ostream&,const QueryResult&);
public:
using line_no = vector<string>::size_type;
QueryResult(string s, shared_ptr<set<line_no>> p, shared_ptr<StrBlob> f)
:sought(s), lines(p), file(f) {}
set<line_no>::iterator begin() const { //12.33
return lines->begin();
}
set<line_no>::iterator end() const { //12.33
return lines->end();
}
shared_ptr<StrBlob> get_file() const { //12.33
return file;
}
string sought; //查询的单词
shared_ptr<set<line_no>> lines; //出现的行号
shared_ptr<StrBlob> file; //输入文件
};
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)
{
os << "\t(line " << num + 1 << ")" << qr.file->begin().deref(num) << endl; //调用重载的deref输出保存的语句
}
return os;
}
#endif // !QUERYRESULT_H
#ifndef MY_STRBLOB_H
#define MY_STRBLOB_H
#include <iostream>
#include <vector>
#include <string>
#include <initializer_list>
#include <memory>
using namespace std;
class StrBlobPtr;
class StrBlob {
friend class StrBlobPtr;
public:
typedef vector<string>::size_type size_type;
StrBlob();
StrBlob(initializer_list<string> il);
size_type size() const { return data->size(); }
bool empty() const { return data->empty(); }
//添加和删除元素
void push_back(const string &t) { data->push_back(t); }
void pop_back();
//元素访问
string& front();
string& back();
StrBlobPtr begin();
StrBlobPtr end();
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);
}
}
//得到首元素
string& StrBlob::front()
{
//如果vector为空,check会抛出一个异常
check(0, "front on empty StrBlob");
return data->front();
}
//得到尾元素
string& StrBlob::back()
{
check(0, "back on empty StrBlob");
return data->back();
}
//将元素弹出栈
void StrBlob::pop_back()
{
check(0, "pop_back on empty StrBlob");
data->pop_back();
}
class StrBlobPtr {
public:
StrBlobPtr() : curr(0) { }
StrBlobPtr(StrBlob &a, size_t sz = 0) : wptr(a.data), curr(sz) { }
string& deref() const;
string& deref(int off) const;
StrBlobPtr& incr(); //前缀递增
private:
// 若检查成功,check返回一个指向vector的shared_ptr
shared_ptr<vector<string>> check(size_t, const string&) const;
// 保存一个weak_ptr,意味着底层vector可能会被销毁
weak_ptr<vector<string>> wptr;
size_t curr;
};
inline shared_ptr<vector<string>> StrBlobPtr::check(size_t i, const string &msg) const
{
auto ret = wptr.lock(); //vector还存在吗?
if (!ret)
throw runtime_error("unbound StrBlobPtr");
if (i >= ret->size())
throw out_of_range(msg);
return ret; //否则,返回指向vector的shared_ptr
}
inline string& StrBlobPtr::deref() const
{
auto p = check(curr, "dereference past end");
return (*p)[curr]; //(*p)是对象所指的vector
}
inline string& StrBlobPtr::deref(int off) const
{
auto p = check(curr + off, "dereference past end");
return (*p)[curr + off]; //(*p)是对象所指的vector
}
// 前缀递增:返回递增后的对象的引用
inline StrBlobPtr& StrBlobPtr::incr()
{
// 如果curr已经指向容器的尾后位置,就不递增它
check(curr, "increment psat end of StrBlobPtr");
++curr; //推进当前位置
return *this;
}
StrBlobPtr StrBlob::begin() { return StrBlobPtr(*this); }
StrBlobPtr StrBlob::end() { return StrBlobPtr(*this, data->size()); }
#endif
#include<iostream>
#include<string>
#include<vector>
#include<map>
#include<set>
#include<sstream>
#include<fstream>
#include<memory>
#include<stdexcept>
#include "TextQuery.h"
using namespace std;
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)) << endl;
}
}
int main(int argc, char *argv[])
{
ifstream infile("Data.txt");
runQueries(infile);
system("pause");
return 0;
}