C++ primer(第五版)第12章习题答案

第十二章 动态内存

 

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;
}

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值