第十二章 动态内存
12.1
b2被销毁了,但b2中的元素不能被销毁。b1和b2中都有4个元素。
12.2
ex12_02.h
#include <vector>
#include <string>
#include <initializer_list>
#include <memory>
#include <exception>
using std::vector;
using std::string;
class StrBlob {
public:
using size_type = vector<string>::size_type;
StrBlob() : data(std::make_shared<vector<string>>()) {}
StrBlob(std::initializer_list<string> il)
: data(std::make_shared<vector<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()
{
check(0, "pop_back on empty StrBlob");
data->pop_back();
}
std::string& front()
{
check(0, "front on empty StrBlob");
return data->front();
}
std::string& back()
{
check(0, "back on empty StrBlob");
return data->back();
}
const std::string& front() const
{
check(0, "front on empty StrBlob");
return data->front();
}
const std::string& back() const
{
check(0, "back on empty StrBlob");
return data->back();
}
private:
void check(size_type i, const string& msg) const
{
if (i >= data->size()) throw std::out_of_range(msg);
}
private:
std::shared_ptr<vector<string>> data;
};
ex12_02.h TEST
#include "ex12_02.h"
#include <iostream>
int main()
{
const StrBlob csb{"hello", "world", "pezy"};
StrBlob sb{"hello", "world", "Mooophy"};
std::cout << csb.front() << " " << csb.back() << std::endl;
sb.back() = "pezy";
std::cout << sb.front() << " " << sb.back() << std::endl;
}
12.3
一、看了@戳戳的解释后对exercise12.3的重新理解。看待这个问题,最重要的是要站在类的使用者的角度来看,而不是类的设计者的角度。虽然在类的具体实现中,数据成员是一个指向vector<string>的智能指针;但由于类的封装,在类的使用者看来,数据成员是vector<string>,他们并不知道具体的实现使用了智能指针。那么当类的使用者声明类的常量对象时,他们期待的结果是vector<string>的内容不会被改变。所以我们在设计这个类的时候,要考虑到类的使用者的真实意图,对于像push_back和pop_back这样会改变智能指针所指向的vector<string>内容的成员函数,我们不应该声明和定义成const版本。这样在类的使用者使用类的常对象时,就不能调用push_back和pop_back成员函数,不能改变智能指针所指向的vector<string>的内容了,这正好与类的使用者的意图相符。
二、对于我提出的初始问题的后半部分的解释。 后半部分说的其实是编译器的行为,当一个类的常量对象试图访问这个类的非const成员函数时,会遭到拒绝。原因是编译器认为一个类的非const成员函数可能有改变数据成员的风险,对于这个类的常量对象,这种风险是不能接受的。所有编译器只允许类的常量对象访问类的const成员函数。编译器这种行为的深层原因可以从报错信息看出来,当用一个类的常量对象访问这个类的非const成员函数时,常量对象会把它的this指针(在此是一个指向常量的指针)作为一个隐式参数传递给成员函数。而非const的成员函数接受的是普通的this指针,常量对象传递给它一个指向常量的this指针,这个指向常量的this指针是无法转换成普通this指针的,所有这种情况下会报出类似 “不能将“this”指针从“const wy_StrBlob”转换为“wy_StrBlob &”” 的错误。
12.4
size_type是一个无符号类型的值。
12.5
使用:
1.在构造函数中不能自动转换
2.我们可以知道使用的类型
不使用:
1.会构造一个临时的StrBlob对象
2.不易使用显式构造函数初始化的拷贝。 ?
12.6
#include <iostream>
#include <vector>
#include <string>
#include <memory>
std::vector<int>* dynamic_vector_generator();
void dynamic_vector_processor(std::vector<int>* ptr_v);
void dynamic_vector_printer(std::vector<int>* ptr_v);
int main()
{
/**
* testing the 3 functions
*/
std::vector<int>* ptr_vi = dynamic_vector_generator();
dynamic_vector_processor(ptr_vi);
dynamic_vector_printer(ptr_vi);
delete ptr_vi;
return 0;
}
/**
* @brief return a pointer to dynamicly allocated vector of ints
*/
std::vector<int>* dynamic_vector_generator()
{
std::vector<int>* ptr_v = new std::vector<int>();
return ptr_v;
}
/**
* @brief return a pointer to vector of ints
* @param ptr_v pointer to vector of ints
*/
void dynamic_vector_processor(std::vector<int>* ptr_v)
{
int i;
std::cout << "plz enter:\n";
while (std::cin >> i && i != 999) ptr_v->push_back(i);
}
/**
* @brief print the content of the vector that ptr_v points to
* @param ptr_v
*/
void dynamic_vector_printer(std::vector<int>* ptr_v)
{
for (const auto& e : *ptr_v) std::cout << e << " ";
std::cout << "\n";
}
12.7
#include <iostream>
#include <vector>
#include <string>
#include <memory>
/**
* @brief functions for ex12.6
*/
std::vector<int>* dynamic_vector_generator();
void dynamic_vector_processor(std::vector<int>* ptr_v);
void dynamic_vector_printer(std::vector<int>* ptr_v);
/**
* @brief functions for ex12.7
*/
std::shared_ptr<std::vector<int>> dynamic_vector_generator_sptr();
void dynamic_vector_processor_sptr(std::shared_ptr<std::vector<int>> sptr_vi);
void dynamic_vector_printer_sptr(
const std::shared_ptr<std::vector<int>> sptr_vi);
int main()
{
auto sptr = dynamic_vector_generator_sptr();
dynamic_vector_processor_sptr(sptr);
dynamic_vector_printer_sptr(sptr);
return 0;
}
/**
* @brief return a pointer to dynamicly allocated vector of ints
* ex12.6
*/
std::vector<int>* dynamic_vector_generator()
{
std::vector<int>* ptr_v = new std::vector<int>();
return ptr_v;
}
/**
* @brief return a pointer to vector of ints
* ex12.6
* @param ptr_v pointer to vector of ints
*/
void dynamic_vector_processor(std::vector<int>* ptr_v)
{
int i;
std::cout << "plz enter:\n";
while (std::cin >> i && i != 999) ptr_v->push_back(i);
}
/**
* @brief print the content of the vector that ptr_v points to
* ex12.6
* @param ptr_v
*/
void dynamic_vector_printer(std::vector<int>* ptr_v)
{
for (const auto& e : *ptr_v) std::cout << e << " ";
std::cout << "\n";
}
/**
* @brief return a shared_prt to vector of ints
*/
std::shared_ptr<std::vector<int>> dynamic_vector_generator_sptr()
{
return std::make_shared<std::vector<int>>();
}
/**
* @brief using std IO adding elelements into the vetor and
* return a shared_ptr pointing to it
* @param sptr_vi
*/
void dynamic_vector_processor_sptr(std::shared_ptr<std::vector<int>> sptr_vi)
{
int i;
std::cout << "plz enter:\n";
while (std::cin >> i && i != 999) sptr_vi->push_back(i);
}
void dynamic_vector_printer_sptr(
const std::shared_ptr<std::vector<int>> sptr_vi)
{
for (const auto& e : *sptr_vi) std::cout << e << " ";
std::cout << "\n";
}
12.8
p将转换为bol类型,这意味这分配的动态内存没有释放的机会,会发生内存泄露。
12.9
智能指针没有发生内存泄露,而r=q执行后,没有指针指向r曾指向的int,没有机会释放内存。
12.10
在process中引用计数为2,调用结束销毁递减为1.
#include <iostream>
#include <vector>
#include <string>
#include <memory>
void process(std::shared_ptr<int> ptr)
{
std::cout << "inside the process function:" << ptr.use_count() << "\n";
}
int main()
{
std::shared_ptr<int> p(new int(42));
process(std::shared_ptr<int>(p));
/**
* codes below shows how the reference count change.
*/
std::cout << p.use_count() << "\n";
auto q = p;
std::cout << p.use_count() << "\n";
std::cout << "the int p now points to is:" << *p << "\n";
return 0;
}
12.11
shared_ptr<int>(p.get())会创建一个临时的shared_ptr,它不是p的拷贝,调用结束,p指向的内存会被释放。
#include <iostream>
#include <vector>
#include <string>
#include <memory>
void process(std::shared_ptr<int> ptr)
{
std::cout << "inside the process function:" << ptr.use_count() << "\n";
}
int main()
{
std::shared_ptr<int> p(new int(42));
/**
* @brief std::shared_ptr<int>(p.get()) construct a temporary shared_ptr
* and copy it
* to the parameter.However it is not a copy of p. As a result, at
* end of this
* main function p will free the memory that has been freed inside
* process ().
* That's why "double freed or corruption" was generated.
*/
process(std::shared_ptr<int>(p.get()));
return 0;
}
12.12
(a)合法。拷贝sp增加引用次数。
(b)不合法。普通指针不能转换为智能指针。
(c)不合法。普通指针不能转换为智能指针。
(d)合法,但不好。调用结束后构建的临时对象会被销毁,所指向的内存会被释放。
#include <iostream>
#include <vector>
#include <string>
#include <memory>
void process(std::shared_ptr<int> ptr)
{
std::cout << "inside the process function:" << ptr.use_count() << "\n";
}
int main()
{
auto p = new int();
auto sp = std::make_shared<int>();
/** @brief
* legal. Just copy sp which is a shared_ptr to process().
*/
// process(sp);
/** @brief
* illegale.plain pointer can not convert to smart pointer implicitly.
*/
// process(new int());
/** @brief
* illegale.plain pointer can not convert to smart pointer implicitly.
*/
// process(p);
/** @brief
* Legal. But it's a bad practice to do so.
* Because using smart pointer together with raw pointer could potentially
* cause problems.
* For example double free as shown in #145.
*
* Check issue #145 for detail, thx @endyul for reporting
*/
// process(std::shared_ptr<int>(p));
return 0;
}
12.13
使用get返回的指针的代码不能delete此指针。
12.14
#include <iostream>
#include <string>
#include <memory>
struct connection {
std::string ip;
int port;
connection(std::string ip_, int port_) : ip(ip_), port(port_) {}
};
struct destination {
std::string ip;
int port;
destination(std::string ip_, int port_) : ip(ip_), port(port_) {}
};
connection connect(destination* pDest)
{
std::shared_ptr<connection> pConn(new connection(pDest->ip, pDest->port));
std::cout << "creating connection(" << pConn.use_count() << ")"
<< std::endl;
return *pConn;
}
void disconnect(connection pConn)
{
std::cout << "connection close(" << pConn.ip << ":" << pConn.port << ")"
<< std::endl;
}
void end_connection(connection* pConn)
{
disconnect(*pConn);
}
void f(destination& d)
{
connection conn = connect(&d);
std::shared_ptr<connection> p(&conn, end_connection);
std::cout << "connecting now(" << p.use_count() << ")" << std::endl;
}
int main()
{
destination dest("202.118.176.67", 3316);
f(dest);
}
12.15
#include <iostream>
#include <string>
#include <memory>
struct connection {
std::string ip;
int port;
connection(std::string ip_, int port_) : ip(ip_), port(port_) {}
};
struct destination {
std::string ip;
int port;
destination(std::string ip_, int port_) : ip(ip_), port(port_) {}
};
connection connect(destination* pDest)
{
std::shared_ptr<connection> pConn(new connection(pDest->ip, pDest->port));
std::cout << "creating connection(" << pConn.use_count() << ")"
<< std::endl;
return *pConn;
}
void disconnect(connection pConn)
{
std::cout << "connection close(" << pConn.ip << ":" << pConn.port << ")"
<< std::endl;
}
void f(destination& d)
{
connection conn = connect(&d);
std::shared_ptr<connection> p(&conn, [](connection* p) { disconnect(*p); });
std::cout << "connecting now(" << p.use_count() << ")" << std::endl;
}
int main()
{
destination dest("202.118.176.67", 3316);
f(dest);
}
12.16
#include <iostream>
#include <string>
#include <memory>
using std::string;
using std::unique_ptr;
int main()
{
unique_ptr<string> p1(new string("pezy"));
// unique_ptr<string> p2(p1); // copy
// ^
// Error: Call to implicitly-deleted copy constructor of
// 'unique_ptr<string>'
//
// unique_ptr<string> p3 = p1; // assign
// ^
// Error: Call to implicitly-deleted copy constructor of
// 'unique_ptr<string>'
std::cout << *p1 << std::endl;
p1.reset(nullptr);
}
12.17
(a) int 不能转换为 std::unique_ptr<int>的指针
(b)可以编译,但会在运行时出错。当p1离开作用域时会被销毁,指向的内存会被释放,但是并未使用new指向动态内存。
(c)可以编译,但会在运行时造成空悬指针,unique_ptr是释放掉原先指针所指向的对象。
(d)同(b)
(e)合法。
(f)两个独立的unique_ptr指向相同的内存,一旦离开作用域会释放两次,出错。
12.18
因为其他shared_ptr会指向相同的动态对象,可以使用delete来删除。因此这个成员是没有必要的。
12.19
ex12_10.h
#ifndef CP5_ex12_19_h
#define CP5_ex12_19_h
#include <vector>
#include <string>
#include <initializer_list>
#include <memory>
#include <exception>
using std::vector;
using std::string;
class StrBlobPtr;
class StrBlob {
public:
using size_type = vector<string>::size_type;
friend class StrBlobPtr;
StrBlobPtr begin();
StrBlobPtr end();
StrBlob() : data(std::make_shared<vector<string>>()) {}
StrBlob(std::initializer_list<string> il)
: data(std::make_shared<vector<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()
{
check(0, "pop_back on empty StrBlob");
data->pop_back();
}
std::string& front()
{
check(0, "front on empty StrBlob");
return data->front();
}
std::string& back()
{
check(0, "back on empty StrBlob");
return data->back();
}
const std::string& front() const
{
check(0, "front on empty StrBlob");
return data->front();
}
const std::string& back() const
{
check(0, "back on empty StrBlob");
return data->back();
}
private:
void check(size_type i, const string& msg) const
{
if (i >= data->size()) throw std::out_of_range(msg);
}
private:
std::shared_ptr<vector<string>> data;
};
class StrBlobPtr {
public:
StrBlobPtr() : curr(0) {}
StrBlobPtr(StrBlob& a, size_t sz = 0) : wptr(a.data), curr(sz) {}
bool operator!=(const StrBlobPtr& p) { return p.curr != curr; }
string& deref() const
{
auto p = check(curr, "dereference past end");
return (*p)[curr];
}
StrBlobPtr& incr()
{
check(curr, "increment past end of StrBlobPtr");
++curr;
return *this;
}
private:
std::shared_ptr<vector<string>> check(size_t i, const string& msg) const
{
auto ret = wptr.lock();
if (!ret) throw std::runtime_error("unbound StrBlobPtr");
if (i >= ret->size()) throw std::out_of_range(msg);
return ret;
}
std::weak_ptr<vector<string>> wptr;
size_t curr;
};
#endif
ex12_19.cpp
#include "ex12_19.h"
StrBlobPtr StrBlob::begin()
{
return StrBlobPtr(*this);
}
StrBlobPtr StrBlob::end()
{
return StrBlobPtr(*this, data->size());
}
12.20
#include "ex12_19.h"
#include <fstream>
#include <iostream>
int main()
{
std::ifstream ifs("../data/book.txt");
StrBlob blob;
for (std::string str; std::getline(ifs, str);) blob.push_back(str);
for (StrBlobPtr pbeg(blob.begin()), pend(blob.end()); pbeg != pend;
pbeg.incr())
std::cout << pbeg.deref() << std::endl;
}
12.21
原先的更好,易读且容易debug.
12.22
改为常量成员函数,构造函数改为接受const StrBlob对象的引用。
ex12_22.h
#ifndef CP5_ex12_22_h
#define CP5_ex12_22_h
#include <vector>
#include <string>
#include <initializer_list>
#include <memory>
#include <exception>
using std::vector;
using std::string;
class ConstStrBlobPtr;
class StrBlob {
public:
using size_type = vector<string>::size_type;
friend class ConstStrBlobPtr;
ConstStrBlobPtr begin() const; // should add const
ConstStrBlobPtr end() const; // should add const
StrBlob() : data(std::make_shared<vector<string>>()) {}
StrBlob(std::initializer_list<string> il)
: data(std::make_shared<vector<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()
{
check(0, "pop_back on empty StrBlob");
data->pop_back();
}
std::string& front()
{
check(0, "front on empty StrBlob");
return data->front();
}
std::string& back()
{
check(0, "back on empty StrBlob");
return data->back();
}
const std::string& front() const
{
check(0, "front on empty StrBlob");
return data->front();
}
const std::string& back() const
{
check(0, "back on empty StrBlob");
return data->back();
}
private:
void check(size_type i, const string& msg) const
{
if (i >= data->size()) throw std::out_of_range(msg);
}
private:
std::shared_ptr<vector<string>> data;
};
class ConstStrBlobPtr {
public:
ConstStrBlobPtr() : curr(0) {}
ConstStrBlobPtr(const StrBlob& a, size_t sz = 0) : wptr(a.data), curr(sz) {} // should add const
bool operator!=(ConstStrBlobPtr& p) { return p.curr != curr; }
const string& deref() const
{ // return value should add const
auto p = check(curr, "dereference past end");
return (*p)[curr];
}
ConstStrBlobPtr& incr()
{
check(curr, "increment past end of StrBlobPtr");
++curr;
return *this;
}
private:
std::shared_ptr<vector<string>> check(size_t i, const string& msg) const
{
auto ret = wptr.lock();
if (!ret) throw std::runtime_error("unbound StrBlobPtr");
if (i >= ret->size()) throw std::out_of_range(msg);
return ret;
}
std::weak_ptr<vector<string>> wptr;
size_t curr;
};
#endif
ex12_22.cpp
#include "ex12_22.h"
ConstStrBlobPtr StrBlob::begin() const // should add const
{
return ConstStrBlobPtr(*this);
}
ConstStrBlobPtr StrBlob::end() const // should add const
{
return ConstStrBlobPtr(*this, data->size());
}
12.23
#include <iostream>
#include <string>
#include <cstring>
int main() {
const char *c1 = "Hello ";
const char *c2 = "World";
unsigned len = strlen(c1) + strlen(c2) + 1;
char *r = new char[len]();
strcat_s(r, len, c1);
strcat_s(r, len, c2);
std::cout << r << std::endl;
std::string s1 = "Hello ";
std::string s2 = "World";
strcpy_s(r, len, (s1 + s2).c_str());
std::cout << r << std::endl;
delete[] r;
return 0;
}
12.24
#include <iostream>
int main()
{
// need to tell the size.
std::cout << "How long do you want the string? ";
int size{0};
std::cin >> size;
char* input = new char[size + 1]();
std::cin.ignore();
std::cout << "input the string: ";
std::cin.get(input, size + 1);
std::cout << input;
delete[] input;
// Test: if longer than the array size, we will lost the characters which
// are out of range.
}
12.25
delete [ ] pa;
12.26
#include <iostream>
#include <string>
#include <memory>
void input_reverse_output_string(int n)
{
std::allocator<std::string> alloc;
auto const p = alloc.allocate(n);
std::string s;
auto q = p;
while (std::cin >> s && q != p + n) alloc.construct(q++, s);
while (q != p) {
std::cout << *--q << " ";
alloc.destroy(q);
}
alloc.deallocate(p, n);
}
int main()
{
input_reverse_output_string(5);
}
12.27
Header
#ifndef CP5_ex12_27_h
#define CP5_ex12_27_h
#include <string>
using std::string;
#include <vector>
using std::vector;
#include <memory>
using std::shared_ptr;
#include <iostream>
#include <fstream>
#include <map>
#include <set>
class QueryResult;
class TextQuery {
public:
using LineNo = vector<string>::size_type;
TextQuery(std::ifstream&);
QueryResult query(const string&) const;
private:
shared_ptr<vector<string>> input;
std::map<string, shared_ptr<std::set<LineNo>>> result;
};
class QueryResult {
public:
friend std::ostream& print(std::ostream&, const QueryResult&);
public:
QueryResult(const string& s, shared_ptr<std::set<TextQuery::LineNo>> set,
shared_ptr<vector<string>> v)
: word(s), nos(set), input(v)
{
}
private:
string word;
shared_ptr<std::set<TextQuery::LineNo>> nos;
shared_ptr<vector<string>> input;
};
std::ostream& print(std::ostream&, const QueryResult&);
#endif
Implementation
#include "ex12_27_30.h"
#include <sstream>
#include <algorithm>
#include <iterator>
TextQuery::TextQuery(std::ifstream& ifs) : input(new vector<string>)
{
LineNo lineNo{0};
for (string line; std::getline(ifs, line); ++lineNo) {
input->push_back(line);
std::istringstream line_stream(line);
for (string text, word; line_stream >> text; word.clear()) {
// avoid read a word followed by punctuation(such as: word, )
std::remove_copy_if(text.begin(), text.end(),
std::back_inserter(word), ispunct);
// use reference avoid count of shared_ptr add.
auto& nos = result[word];
if (!nos) nos.reset(new std::set<LineNo>);
nos->insert(lineNo);
}
}
}
QueryResult TextQuery::query(const string& str) const
{
// use static just allocate once.
static shared_ptr<std::set<LineNo>> nodate(new std::set<LineNo>);
auto found = result.find(str);
if (found == result.end())
return QueryResult(str, nodate, input);
else
return QueryResult(str, found->second, input);
}
std::ostream& print(std::ostream& out, const QueryResult& qr)
{
out << qr.word << " occurs " << qr.nos->size()
<< (qr.nos->size() > 1 ? " times" : " time") << std::endl;
for (auto i : *qr.nos)
out << "\t(line " << i + 1 << ") " << qr.input->at(i) << std::endl;
return out;
}
TEST
#include "ex12_27_30.h"
#include <iostream>
void runQueries(std::ifstream& infile)
{
TextQuery tq(infile);
while (true) {
std::cout << "enter word to look for, or q to quit: ";
string s;
if (!(std::cin >> s) || s == "q") break;
print(std::cout, tq.query(s)) << std::endl;
}
}
int main()
{
std::ifstream file("../data/storyDataFile.txt");
runQueries(file);
}
12.28
#include <string>
using std::string;
#include <vector>
using std::vector;
#include <memory>
using std::shared_ptr;
#include <iostream>
#include <fstream>
#include <sstream>
#include <map>
#include <set>
#include <algorithm>
int main()
{
std::ifstream file("../data/letter.txt");
vector<string> input;
std::map<string, std::set<decltype(input.size())>> dictionary;
decltype(input.size()) lineNo{0};
for (string line; std::getline(file, line); ++lineNo) {
input.push_back(line);
std::istringstream line_stream(line);
for (string text, word; line_stream >> text; word.clear()) {
std::remove_copy_if(text.begin(), text.end(),
std::back_inserter(word), ispunct);
dictionary[word].insert(lineNo);
}
}
while (true) {
std::cout << "enter word to look for, or q to quit: ";
string s;
if (!(std::cin >> s) || s == "q") break;
auto found = dictionary.find(s);
if (found != dictionary.end()) {
std::cout << s << " occurs " << found->second.size()
<< (found->second.size() > 1 ? " times" : " time")
<< std::endl;
for (auto i : found->second)
std::cout << "\t(line " << i + 1 << ") " << input.at(i)
<< std::endl;
}
else
std::cout << s << " occurs 0 time" << std::endl;
}
}
12.29
do {
std::cout << "enter word to look for, or q to quit: ";
string s;
if (!(std::cin >> s) || s == "q") break;
print(std::cout, tq.query(s)) << std::endl;
} while ( true );
do while,因为与逻辑一致。
12.30
同12.27
12.31
vector会有重复,set保证了每行只出现一次且行号按升序保存。
12.32
Header
#ifndef CP5_ex12_32_h
#define CP5_ex12_32_h
#include "ex12_22.h"
using std::shared_ptr;
#include <iostream>
#include <fstream>
#include <map>
#include <set>
class QueryResult;
class TextQuery {
public:
TextQuery(std::ifstream&);
QueryResult query(const string&) const;
private:
shared_ptr<StrBlob> input;
std::map<string, shared_ptr<std::set<StrBlob::size_type>>> result;
};
class QueryResult {
public:
friend std::ostream& print(std::ostream&, const QueryResult&);
public:
QueryResult(const string& s, shared_ptr<std::set<StrBlob::size_type>> set,
shared_ptr<StrBlob> v)
: word(s), nos(set), input(v)
{
}
private:
string word;
shared_ptr<std::set<StrBlob::size_type>> nos;
shared_ptr<StrBlob> input;
};
std::ostream& print(std::ostream&, const QueryResult&);
#endif
Implementation
#include "ex12_32.h"
#include <sstream>
#include <algorithm>
TextQuery::TextQuery(std::ifstream& ifs) : input(new StrBlob)
{
StrBlob::size_type lineNo{0};
for (string line; std::getline(ifs, line); ++lineNo) {
input->push_back(line);
std::istringstream line_stream(line);
for (string text, word; line_stream >> text; word.clear()) {
// avoid read a word followed by punctuation(such as: word, )
std::remove_copy_if(text.begin(), text.end(),
std::back_inserter(word), ispunct);
// use reference avoid count of shared_ptr add.
auto& nos = result[word];
if (!nos) nos.reset(new std::set<StrBlob::size_type>);
nos->insert(lineNo);
}
}
}
QueryResult TextQuery::query(const string& str) const
{
// use static just allocate once.
static shared_ptr<std::set<StrBlob::size_type>> nodate(
new std::set<StrBlob::size_type>);
auto found = result.find(str);
if (found == result.end())
return QueryResult(str, nodate, input);
else
return QueryResult(str, found->second, input);
}
std::ostream& print(std::ostream& out, const QueryResult& qr)
{
out << qr.word << " occurs " << qr.nos->size()
<< (qr.nos->size() > 1 ? " times" : " time") << std::endl;
for (auto i : *qr.nos) {
ConstStrBlobPtr p(*qr.input, i);
out << "\t(line " << i + 1 << ") " << p.deref() << std::endl;
}
return out;
}
12.33
Header
#ifndef CP5_ex12_33_h
#define CP5_ex12_33_h
#include "ex12_22.h"
using std::shared_ptr;
#include <iostream>
#include <fstream>
#include <map>
#include <set>
class QueryResult;
class TextQuery {
public:
TextQuery(std::ifstream&);
QueryResult query(const string&) const;
private:
shared_ptr<StrBlob> input;
std::map<string, shared_ptr<std::set<StrBlob::size_type>>> result;
};
class QueryResult {
public:
using ResultIter = std::set<StrBlob::size_type>::iterator;
friend std::ostream& print(std::ostream&, const QueryResult&);
public:
QueryResult(const string& s, shared_ptr<std::set<StrBlob::size_type>> set,
shared_ptr<StrBlob> v)
: word(s), nos(set), input(v)
{
}
ResultIter begin() const { return nos->begin(); }
ResultIter end() const { return nos->end(); }
shared_ptr<StrBlob> get_file() const { return input; }
private:
string word;
shared_ptr<std::set<StrBlob::size_type>> nos;
shared_ptr<StrBlob> input;
};
std::ostream& print(std::ostream&, const QueryResult&);
#endif
Implementation
#include "ex12_33.h"
#include <sstream>
#include <algorithm>
TextQuery::TextQuery(std::ifstream& ifs) : input(new StrBlob)
{
StrBlob::size_type lineNo{0};
for (string line; std::getline(ifs, line); ++lineNo) {
input->push_back(line);
std::istringstream line_stream(line);
for (string text, word; line_stream >> text; word.clear()) {
// avoid read a word followed by punctuation(such as: word, )
std::remove_copy_if(text.begin(), text.end(),
std::back_inserter(word), ispunct);
// use reference avoid count of shared_ptr add.
auto& nos = result[word];
if (!nos) nos.reset(new std::set<StrBlob::size_type>);
nos->insert(lineNo);
}
}
}
QueryResult TextQuery::query(const string& str) const
{
// use static just allocate once.
static shared_ptr<std::set<StrBlob::size_type>> nodate(
new std::set<StrBlob::size_type>);
auto found = result.find(str);
if (found == result.end())
return QueryResult(str, nodate, input);
else
return QueryResult(str, found->second, input);
}
std::ostream& print(std::ostream& out, const QueryResult& qr)
{
out << qr.word << " occurs " << qr.nos->size()
<< (qr.nos->size() > 1 ? " times" : " time") << std::endl;
for (auto it = qr.begin(); it != qr.end(); ++it) {
ConstStrBlobPtr p(*qr.get_file(), *it);
out << "\t(line " << *it + 1 << ") " << p.deref() << std::endl;
}
return out;
}