Primer C++ 学习笔记(第十二章)

本书为Primer C++ 中文第五版

练习题笔记

在这里插入图片描述在这里插入图片描述
在这里插入图片描述

(12.1)
#include "strBlob.h"

void test1() {
    StrBlob b1;
    {
        StrBlob b2 = {"a", "an", "the"};
        b1 = b2;
        b2.push_back("about");
        cout << b2.size() << endl;
    }
    cout << b1.size() << endl;
}
b1和b2共享动态内存vector<string>,代码的结尾,b2离开作用域,b2被销毁,b1包含四个元素。
(12.2)
//可以编写这样一个头文件,到时候使用的时候如上面的12.1
#include <iostream>
#include <vector>
#include <string>
#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 &s) { data->push_back(s);}
    void pop_back();
    string &front();
	const string &front() const;
    string &back();
	const string &back() const;
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() {
    check(0, "front on empty StrBlob");
    return data->front();
}

const string & StrBlob::front() const{
    check(0, "front on empty StrBlob");
    return data->front();
}

string & StrBlob::back() {
    check(0, "back on empty StrBlob");
    return data->back();
}

const string & StrBlob::back() const{
    check(0, "back on empty StrBlob");
    return data->back();
}

void StrBlob::pop_back() {
    check(0, "pop_back on empty StrBlob");
    return data->pop_back();
}
(12.3)
不需要,因为push_back和pop_back都需要对vector元素进行增删操作,并不仅仅只是访问元素。
(12.4)
因为check函数是private的,只有StrBlob内部的函数才能调用它,并且在函数内部使用的时候已经确保i的值不会出现问题
(12.5)
未编写接受一个初始化列表参数的显示构造函数,意味着可以进行列表向StrBlob的隐式类型转换,也就是,在需要StrBlob的地方(如函数的参数),可以使用列表进行替代。而且,可以进行拷贝形式的初始化(如赋值)。这令程序编写更为简单方便。
但这种隐式转换并不总是好的。例如,列表中可能并非都是合法的值。再如,对于接受StrBlob的函数,传递给它一个列表,会创建一个临时的StrBlob对象,用列表对其初始化,然后将其传递给函数,当函数完成后,此对象将被丢弃,再也无法访问了。对于这些情况,我们可以定义显式的构造函数,禁止隐式类类型转换。

在这里插入图片描述

(12.6)
vector<int>* write(vector<int>* v) {
    int n;
    while(cin >> n && n != -1) {
        v->push_back(n);
    }
    return v;
}
void read(vector<int> *v) {
    for(auto i = v->begin(); i != v->end(); ++i) cout << *i << " ";
    cout << endl;
}
vector<int>* creat() {
    vector<int> *v = new vector<int>;
    return v;
}
void test6() {
    vector<int> *v = write(creat());
    read(v);
    delete(v);
}
(12.7)
shared_ptr<vector<int>> creatPtr() {
    auto p = make_shared<vector<int>>();
    return p;
}
shared_ptr<vector<int>> writePtr(shared_ptr<vector<int>> p) {
    int n;
    while(cin >> n && n != -1) p->push_back(n);
    return p;
}
void readPtr(shared_ptr<vector<int>> p) {
    for(auto i = p->begin(); i != p->end(); ++i) cout << *i << " ";
    cout << endl;
}
void test7() {
    auto p = writePtr(creatPtr());
    readPtr(p);
}
(12.8)
当一个程序用光了可用的内存,new表达式就会失败。如果不能分配内存失败,则会抛出一个类型bad_alloc的异常,可用使用下面的方式阻止抛出异常:
int *p2 = new (nothrow) int;//分配失败的时候返回一个空指针
如果该函数是想返回一个类型为int的指针,应该修改为int *b() {return new int;}
如果该函数是想判断是否new成功,应该改为 bool b() {return new (nothrow) int;}
(12.9)
int *q = new int(42)/*new一个指针q指向42*/, *r = new int(100)/*new一个指针指向100*/;
r = q;//指针r指向q
//由于r指向q之后,之前分配指向100的内存存在但无法再访问,并且也没有delete,造成内存泄漏
auto q2 = make_shared<int>(42), r2 = make_shared<int>(100);//创建两个智能指针q2和r2分别指向42和100
r2 = q2;//r2指向q2,r2之前指向的内存被销毁释放

在这里插入图片描述

(12.10)
正确。显示构造智能指针,计数加1
函数调用时会创建一个临时的智能指针,计数加1
函数结束,临时指针被销毁,计数减1
最终计数还是1,不改变
(12.11)
错误,因为p.get()不会使计数加1,但是函数结束会被销毁计数减1计数为0,内存被释放
(12.12)
void process(shared_ptr<int> ptr) {
}
void test12() {
    auto p = new int(5);
    auto sp = make_shared<int>(5);
    process(sp);//a
    cout << *sp << endl; 
    //process(new int()); //b
    //process(p); //c
    process(shared_ptr<int>(p));//d
    cout << *p << endl;
}
a 合法,正确的用法
b 不合法,不允许隐式替换
c 不合法,不允许隐式替换
d 合法,但是内存会被释放,p变成一个空悬指针
(12.13)
会删除sp管理的内存,使得sp变成空悬指针

在这里插入图片描述

/**
*智能指针使用规范
*不使用相同的内置指针初始化或reset多个智能指针
*不delete get()返回的指针
*不使用get()初始化或reset另一个智能指针
*如果使用get()返回的指针,当最后一个对应的智能指针销毁后,你的指针就变为无效了
*如果使用的智能指针管理的资源不是new分配的内存,记住传递给它一个删除器
*/
(12.14)
struct destination;
struct connection;
connection connect(destination*);
void disconnect(connection);
void end_connection(connection *p) {disconnect(*p);}

void f(destination &d) {
    connection c = connect(&d);
    shared_ptr<connection> p (&c, end_connection);
}
(12.15)
void f(destination &d) {
    connection c = connect(&d);
    shared_ptr<connection> p (&c, [](connection *p){disconnect(*p););
}

在这里插入图片描述

(12.16)
error: use of deleted function ‘std::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = int; _Dp = std::default_delete<int>]’
正确用法为:
void test16() {
    unique_ptr<int> q1(new int(1));
    cout << *q1 << endl;
    unique_ptr<int> q2(q1.release());//release将q1置空
    cout << *q2 << endl;
    unique_ptr<int> q3(new int(3));
    cout << *q3 << endl;
    q2.reset(q3.release());//reset释放了q2原来指向的内存
    cout << *q2 << endl;
}
(12.17)
void test17() {
    int ix = 1024, *pi = &ix, *pi2 = new int(2048);
    typedef unique_ptr<int> IntP;
    //IntP p0(ix);
    //IntP p1(pi);
    IntP p2(pi2);
    //IntP p3(&ix);
    IntP p4(new int(2048));
    IntP p5(p2.get());
    //p2.reset();
    cout << "*p0" << "*p1" << *p2 << "*p3" << *p4 << *p5 << endl;
}
(a) 不合法,不能用非指针类型初始化。
(b) 合法。编译成功,运行出错。因为它用一个普通的int变量的地址初始化p1,p1销毁时会释放此内存,其行为是未定义的。
(c) 合法。
(d) 合法。和b一样的问题
(e) 合法。
(f) 合法。但是有问题。会造成两个unique_ptr指向相同的内存地址。当其中一个unique_ptr被销毁时(或者被reset释放对象时),该内存被释放,另一个unique_ptr会变成空悬指针。
(12.18)
因为shared_ptr是允许多个指针管理一个内存,为了对其他指针负责,所以不允许release()

在这里插入图片描述

(12.19)
//strBlobPtr.h
#include "strBlob.h"

class StrBlobPtr {
public:
    StrBlobPtr() : curr(0) { }
    StrBlobPtr(StrBlob& a, size_t sz = 0):
            wptr(a.data), curr(sz) { }
    string& deref() const;
    StrBlobPtr& incr();
private:
    shared_ptr<vector<string>> check(size_t, const string&) const;
    weak_ptr<vector<string>> wptr;
    size_t curr;
};

shared_ptr<vector<string>> StrBlobPtr::check(size_t i, const string &msg) const {
    auto ret = wptr.lock();
    if(!ret) {
        throw runtime_error("unbound StrBlobPtr");
    }
    if(i >= ret->size())
        throw out_of_range(msg);
    return ret;
}

string& StrBlobPtr::deref() const {
    auto p = check(curr, "dereference past end");
    return (*p)[curr];  
}

StrBlobPtr& StrBlobPtr::incr() {
    check(curr, "increment past end of StrBlobPtr");
    ++curr;
    return *this;
}

StrBlobPtr StrBlob::begin() {
    return StrBlobPtr(*this); 
}

StrBlobPtr StrBlob::end() {
    auto ret = StrBlobPtr( *this, data->size() );
    return ret;
}
//strBlob.h
#include <iostream>
#include <vector>
#include <string>
#include <memory>

using namespace std;

class StrBlobPtr;
class StrBlob {
public:
    friend class StrBlobPtr;
    //这两个在strBlobPtr.h里面实现
    StrBlobPtr begin(); 
    StrBlobPtr end(); 
    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 &s) { data->push_back(s);}
    void pop_back();
    string &front();
    const string &front() const;
    string &back();
    const string &back() const;
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() {
    check(0, "front on empty StrBlob");
    return data->front();
}

const string & StrBlob::front() const{
    check(0, "front on empty StrBlob");
    return data->front();
}

string & StrBlob::back() {
    check(0, "back on empty StrBlob");
    return data->back();
}

const string & StrBlob::back() const{
    check(0, "back on empty StrBlob");
    return data->back();
}

void StrBlob::pop_back() {
    check(0, "pop_back on empty StrBlob");
    return data->pop_back();
}
(12.20)
#include "StrBlobPtr.h"
void test20() {
    StrBlob *file = new StrBlob();
    string name;
    int n = 0;
    //因为没有重载operate== 和 operate!=,用n偷个懒
    while(cin >> name && name != "q") {
        file->push_back(name);
        n++;
    }
    auto p = file->begin();
    while(n != 0) {
        cout << p.deref() << endl;
        p.incr();
        n--;
    }
}
(12.21)
这样写尽可能的缩短了代码,但是可读性降低了。
(12.22)
#include "strBlob.h"

class ConstStrBlobPtr {
public:
    ConstStrBlobPtr() : curr(0) { }
    ConstStrBlobPtr(const StrBlob& a, size_t sz = 0):
            wptr(a.data), curr(sz) { }
    string& deref() const;
    ConstStrBlobPtr& incr();
private:
    shared_ptr<vector<string>> check(size_t, const string&) const;
    weak_ptr<vector<string>> wptr;
    size_t curr;
};

shared_ptr<vector<string>> ConstStrBlobPtr::check(size_t i, const string &msg) const {
    auto ret = wptr.lock();
    if(!ret) {
        throw runtime_error("unbound ConstStrBlobPtr");
    }
    if(i >= ret->size())
        throw out_of_range(msg);
    return ret;
}

string& ConstStrBlobPtr::deref() const {
    auto p = check(curr, "dereference past end");
    return (*p)[curr];  
}

ConstStrBlobPtr& ConstStrBlobPtr::incr() {
    check(curr, "increment past end of StrBlobPtr");
    ++curr;
    return *this;
}

ConstStrBlobPtr StrBlob::begin() const{
    return ConstStrBlobPtr(*this); 
}

ConstStrBlobPtr StrBlob::end() const{
    auto ret = ConstStrBlobPtr( *this, data->size() );
    return ret;
}

在这里插入图片描述

(12.23)
void test23() {
    const char *c1 = "hello,";
    const char *c2 = "world!";
    char *res = new char[strlen(c1)+strlen(c2)+1];
    strcpy(res, c1);
    strcat(res, c2);
    cout << res << endl;
    string s1 = "HELLO,";
    string s2 = "WORLD!";
    s1 = s1.append(s2);
    res = &s1[0];
    cout << res << endl;
}
(12.24)
//好像没有问题,但是这样总是不好的,可能会导致其他的错误
void test24() {
    char *r = new char[3];
    char c;
    char *q = r;
    int n = 0;
    while(cin >> c && c != 'q') {
        *q++ = c;
        n++;
    }
    char *s = r;
    while(s != q) {
        cout << *s++ << endl;
    }
    delete[] r;
}
(12.25)
delete [] pa;

在这里插入图片描述

(12.26)
void test26() {
    int n = 10;
    allocator<string> alloc;
    auto const p = alloc.allocate(n);
    string s;
    auto q = p;
    while(cin >> s && q != p + n && s != "q") {
        alloc.construct(q++, s);
    }
    const size_t size = q - p;
    cout << size << endl;
    while(q != p)
        alloc.destroy(--q);
    alloc.deallocate(p, n);
}

在这里插入图片描述

(12.27)
//TextQuery和QueryResult
#include <fstream>
#include <sstream>
#include <iostream>
#include <vector>
#include <string>
#include <memory>
#include <map>
#include <set>

using namespace std;
typedef vector<string>::size_type line_no;

class QueryResult;
class TextQuery {
public:
    TextQuery(ifstream&);
    QueryResult query(const string &) const;
private:
    shared_ptr<vector<string>> file;
    map<string, shared_ptr<set<line_no>>> wm;
};

class QueryResult {
friend std::ostream& print(std::ostream&, const QueryResult&);
public:
    QueryResult(string s, shared_ptr<set<line_no>> p,
        shared_ptr<vector<string>> f):
        sought(s), lines(p), file(f){ }
private:
    string sought;
    shared_ptr<set<line_no>> lines;
    shared_ptr<vector<string>> file;
};

ostream &print(ostream & os, const QueryResult &qr) {
    os << qr.sought << " occurs " << qr.lines->size() << " "
       << (((qr.lines->size()) > 1)? " times " : " time ") << endl;
    for(auto num : *qr.lines) {
        os << "\t(line " << num + 1 << ") "
           << *(qr.file->begin() + num) << endl;
    }
    return os;
}

TextQuery::TextQuery(ifstream &is) : file(new vector<string>) {
    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];
            if(!lines)
                lines.reset(new set<line_no>);
            lines->insert(n);
        }
    }
}

QueryResult TextQuery::query(const string &sought) const {
    static shared_ptr<set<line_no>> nodata(new set<line_no>);
    auto loc = wm.find(sought);
    if(loc == wm.end())
        return QueryResult(sought, nodata, file);
    else
        return QueryResult(sought, loc->second, file);
}

(12.28)
#include "TextQuery.h"

int main() {
    ifstream fin;
    fin.open("test.txt");
    TextQuery tq(fin);
    string s;
    while(cin>>s && s != "q") {
        QueryResult qr = tq.query(s);
        print(cout, qr);
    }
}
(12.29)
#include "TextQuery.h"

int main() {
    ifstream fin;
    fin.open("test.txt");
    TextQuery tq(fin);
    string s;
    cin >> s;
   	do{
        QueryResult qr = tq.query(s);
        print(cout, qr);
    } while(cin>>s && s != "q") 
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值