Primer C++笔记记录(第十九章)

Primer C++笔记记录(第十九章)

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

练习题笔记

在这里插入图片描述

class StrVec {
public:
    StrVec() : elements(NULL),first_free(NULL), cap(NULL) { }
    StrVec(const StrVec&);
    StrVec &operator=(const StrVec&);
    ~StrVec();
    void push_back(const std::string&);
    void push_back(std::string &&s) {
        chk_n_alloc();
        alloc.construct(first_free++, move(s));
    }
    size_t size() const {return first_free - elements;}
    size_t capacity() const {return cap - elements;}
    std::string *begin() const {return elements;}
    std::string *end() const {return first_free;}
    StrVec(StrVec &&s) noexcept : elements(s.elements), first_free(s.first_free), cap(s.cap) {
        s.elements = s.first_free = s.cap = NULL;
    }
    StrVec& operator=(StrVec &&s) noexcept {
        if(this != &s) {
            free();
            elements = s.elements;
            first_free = s.first_free;
            cap = s.cap;
            s.elements = s.first_free = s.cap = NULL;
        }
        return *this;
    }
private:
    static std::allocator<std::string> alloc;
    void chk_n_alloc() {
        if(size() == capacity()) reallocate();
    }
    std::pair<std::string*, std::string*> alloc_n_copy(const std::string*, const std::string*);
    void free();
    void reallocate();
    std::string *elements; //数组首元素
    std::string *first_free; //数组第一个空闲元素
    std::string *cap; //数组尾后位置
};

std::allocator<std::string> StrVec::alloc;

void StrVec::push_back(const std::string &s) {
    chk_n_alloc();
    alloc.construct(first_free++, s);
}

std::pair<std::string*, std::string*> StrVec::alloc_n_copy(const std::string *b, const std::string *e) {
    auto data = alloc.allocate(e - b);
    return {data, uninitialized_copy(b, e, data)};
}

void StrVec::free() {
    if(elements) {
        for(auto p = first_free; p != elements;) {
            alloc.destroy(--p);//1 析构函数
        }
        alloc.deallocate(elements, cap - elements);//释放内存
    }
}

StrVec::StrVec(const StrVec &s) {
    auto newdata = alloc_n_copy(s.begin(), s.end());
    elements = newdata.first;
    first_free = cap = newdata.second;
}

StrVec::~StrVec() {free(); }

StrVec &StrVec::operator=(const StrVec &s) {
    auto data = alloc_n_copy(s.begin(), s.end());
    free();
    elements = data.first;
    first_free = cap = data.second;
    return *this;
}

void StrVec::reallocate() {
    auto newcapacity = size() ? 2 * size() : 1;
    auto newdata = alloc.allocate(newcapacity); //分配内存
    auto dest = newdata;
    auto elem = elements;
    for(size_t i = 0; i != size(); ++i)
        alloc.construct(dest++,move(*elem++)); 
    free();
    elements = newdata;
    first_free = dest;
    cap = elements + newcapacity;
}

void *operator new(size_t size) {
    std::cout << "new\n";
    if(void *mem = malloc(size)) {
        return mem;
    }else {
        throw std::bad_alloc();
    }
}
void operator delete(void *mem) noexcept {
    std::cout << "delete\n";
    std::free(mem);
}

void test2() {
    StrVec s;
    s.push_back("zzz");
    s.push_back("lll");
    std::cout << *s.begin() << " " << s.size() << std::endl;
}

(19.1)
void *operator new(size_t size) {
    std::cout << "new\n";
    if(void *mem = malloc(size)) {
        return mem;
    }else {
        throw std::bad_alloc();
    }
}
void operator delete(void *mem) noexcept {
    std::cout << "delete\n";
    std::free(mem);
}
(19.2)
重写的函数需要定义在全局中, 如果定义在 StrVec 类中, allocator 将无法访问到.

在这里插入图片描述

namespace t3 {
class A {
public:
    A() {std::cout << "A()\n";}
    virtual ~A() {std::cout << "~A()\n";}
};
class B : public A {
public:
    B() {std::cout << "B()\n";}
    virtual ~B() {std::cout << "~B()\n";}
};
class C : public B {
public:
    C() {std::cout << "C()\n";}
    virtual ~C() {std::cout << "~C()\n";}
};
class D : public B, public A {
public:
    D() {std::cout << "D()\n";}
    virtual ~D() {std::cout << "~D()\n";}
};

void test() {
    int type = -1;
    while(std::cin>> type && type != 0) {
        if (type == 1) {
            A *pa = new C;
            B *pb = dynamic_cast<B*> (pa);
            if(pb) {std::cout << "successful\n";}
        }else if(type == 2) {
            B *pb = new B;
            C *pc = dynamic_cast<C*> (pb);
            if(pc) {std::cout << "successful\n";}
        }else if(type == 3) {
            // A *pa = new D;
            // B *pb = dynamic_cast<B*> (pa);
        }else if(type == 4) {
            A *pa = new C;
            if(C* pc = dynamic_cast<C*> (pa)) {
                {std::cout << "successful\n";}
            }else {
                {std::cout << "failed\n";}
            }
        }else if(type == 5) {
            B b;
            A &ra = b;
            try {
                C &rc = dynamic_cast<C&> (ra);
                std::cout << "succeeful\n";
            } catch (std::bad_cast) {
                std::cout << "failed\n";
            }
        }
    }
}
}

(19.3)
a> 编译成功, 动态转换成功, 将指针 pa 转换成 B 的指针.
b> 编译成功, 动态转换失败, 指针 pb 无法转换成 C 的指针.
c> 编译失败, D 会创建两个 A 的部分, 会造成二义性.
(19.4)
见程序代码
(19.5)
当无法使用虚函数时, 选择想要的函数版本.
例如,实现具有继承关系的类型去实现相等运算符
class Base {
    friend bool operator==(const Base& lhs, const Base& rhs);
public:
protected:
    virtual bool equal(const Base&) const;
};
class Derived : public Base {
public:
protected:
    bool equal(const Base&) const;
};
bool operator==(const Base& lhs, const Base& rhs){
    return typeid(lhs) == typeid(rhs) && lhs.equal(rhs);
}
bool Base::equal(const Base& rhs) const {
    //执行相等操作
}
bool Derived::equal(const Base& rhs) const {
    auto r = dynamic_cast<const Derived&> (rhs);
    //执行相等操作
}

在这里插入图片描述

(19.6)
ifstream fin;
fin.open("test.txt");
TextQuery tq(fin);
std::string s1 = "fiery", s2 = "bird";
Query_base::setDebug(false);

Query q1(s1), q2(s2);
AndQuery aq(q1, q2);

// 19.6
Query_base* qb = &aq;
if(AndQuery* aqt = dynamic_cast<AndQuery*> (qb)) {
    print(cout, aqt->eval(tq));
}else {
    cout << "dynamic_cast error\n";
}
(19.7)
// 19.7
Query_base& p = aq;
try{
    AndQuery& ap = dynamic_cast<AndQuery&>(p);
    print(cout, ap.eval(tq));
}
catch (bad_cast){
    cout << "dynamic_cast error\n";
}
(19.8)
// 19.8
cout << (typeid(*qb) == typeid(p)) << " " << (typeid(*qb) == typeid(AndQuery)) << endl;

在这里插入图片描述

namespace t9{
class Base {};
class Derived : public Base {};
void test1() {
    int arr[10];
    Derived d;
    Base *p = &d;
    std::cout << typeid(42).name() << ", "
              << typeid(arr).name() << ", "
              << typeid(std::string).name() << ", "
              << typeid(p).name() << ", "
              << typeid(d).name() << ", "
              << typeid(*p).name() << "\n";
              
}
class A {};
class B : public A {};
class C : public B {};
void test() {
    A* pa = new C;
    std::cout << typeid(pa).name() << "\n";
    C cobj;
    A& ra = cobj;
    std::cout << typeid(&ra).name() << "\n";
    B *pb = new B;
    A& ra1  = *pb;
    std::cout << typeid(ra1).name() << "\n";
}
}
(19.9)
(19.10)

在这里插入图片描述


namespace t11{
class Screen {
public:
    typedef std::string::size_type pos;
    Screen(int n) : cursor(n) {}
    pos cursor;
};
void test() {
    const std::string::size_type Screen::*pCur = &Screen::cursor;
    Screen s(18);
    auto pos = s.*pCur;
    std::cout << pos << std::endl;
}
}

(19.11)
在声明成员指针时用 * 表示当前声明的名字是一个指针.
成员指针必须包含成员所属的类.
(19.12)
(19.13)
const std::string Sales_data::*bookNo;

在这里插入图片描述

(19.14)
auto pmf = &Screen::get_cursor; -> 合法
pmf 是一个指针,他可以指向Screen的某个常量成员函数.前提是该函数不接受任何实参,且返回一个char.
pmf = &Screen::get; -> 不合法,Screen::get有两个实参.应修改为:
char (Screen::*pmf2)(Screen::pos, Screen::pos) const;
pmf2 = &Screen::get;
(19.15)
和普通函数指针不同的是,成员函数和指向改成员的指针直接不存在自动转换规则.
(19.16)
using Action = double(Sales_data::*)()const;
(19.17)
using Action = char (Screen::*)(Screen::pos, Screen::pos) const;

在这里插入图片描述


namespace t18{
void test() {
    std::vector<std::string>  vs = {"11", "", "22", "", "33", "", "44"};
    int size = count_if(vs.begin(), vs.end(), std::mem_fn(&std::string::empty));
    auto it = find_if(vs.begin(), vs.end(), std::bind(&std::string::empty, std::placeholders::_1));
    std::cout << "size: " << size << " , it = " << *(it+1) << std::endl;
}
class Sales_data {
    double price;
public:
    Sales_data(double p) : price(p) { }
    double get_pri() {return price;}
};
void test1() {
    Sales_data sd(8.0), sd1(12.5), sd2(22.5), sd3(10);
    std::vector<Sales_data> vsd = {sd, sd1, sd2, sd3};
    double M = 11;
    auto f = std::mem_fn(&Sales_data::get_pri);
    auto it = find_if(vsd.begin(), vsd.end(), [&](Sales_data& s) {return f(s)>M; });
    std::cout << it->get_pri() << std::endl;
}
}

(19.18)
(19.19)

在这里插入图片描述

(19.20)
#ifndef _TEXTQUERY_H_
#define _TEXTQUERY_H_
//#include "QueryResult.h"
#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 TextQuery {
public:
	class QueryResult;
    TextQuery(ifstream&);
    QueryResult query(const std::string &) const;
private:
    shared_ptr<vector<string>> file;
    map<string, shared_ptr<set<line_no>>> wm;
};

class TextQuery::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){ }
    const shared_ptr<vector<string>> get_file() const{return file;}
    std::set<line_no>::iterator
    begin(){ return lines->begin();}
    std::set<line_no>::iterator
    end(){ return lines->end();}
private:
    string sought;
    shared_ptr<set<line_no>> lines;
    shared_ptr<vector<string>> file;
};

std::ostream &print(ostream & os, const TextQuery::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);
        }
    }
}

TextQuery::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 TextQuery::QueryResult(sought, nodata, file);
    else
        return TextQuery::QueryResult(sought, loc->second, file);
}
#endif // _TEXTQUERY_H_

在这里插入图片描述

namespace t21 {
class Token {
public:
    Token(): tok(INT), ival(0) { }
    Token(const Token& t): tok(t.tok) { copyUnion(t); }
    Token &operator=(const Token&);
    ~Token(){ if(tok == STR) sval.std::string::~string(); }
    Token &operator=(const std::string&);
    Token &operator=(char);
    Token &operator=(int);
    Token &operator=(double);
    void print() {
        switch(tok) {
            case Token::INT: std::cout << ival <<"\n"; break;
            case Token::CHAR: std::cout << cval <<"\n"; break;
            case Token::DBL: std::cout << dval <<"\n"; break;
            case Token::STR: std::cout << sval <<"\n"; break;
        }
    }

    //t23
    Token(Token&& t) :tok(std::move(t.tok)) { moveUnion(std::move(t)); }
    Token& operator=(Token&&);
private:
    enum {INT, CHAR, DBL, STR} tok;
    union {
        char cval;
        int ival;
        double dval;
        std::string sval;
    };
    void copyUnion(const Token&);
    void moveUnion(const Token&&);
};

Token &Token::operator=(int i) {
    if(tok == STR) sval.std::string::~string();
    ival = i;
    tok = INT;
    return *this;
}
Token &Token::operator=(char i) {
    if(tok == STR) sval.std::string::~string();
    cval = i;
    tok = CHAR;
    return *this;
}
Token &Token::operator=(double i) {
    if(tok == STR) sval.std::string::~string();
    dval = i;
    tok = DBL;
    return *this;
}
Token &Token::operator=(const std::string& s) {
    if(tok == STR) sval = s;
    else new(&sval) std::string(s);
    tok = STR;
    return *this;
}
void Token::copyUnion(const Token& t) {
    switch(t.tok) {
        case Token::INT: ival = t.ival; break;
        case Token::CHAR: cval = t.cval; break;
        case Token::DBL: dval = t.dval; break;
        case Token::STR: new(&sval) std::string(t.sval); break;
    }
}
Token &Token::operator=(const Token& t) {
    if(tok == STR && t.tok != STR) sval.std::string::~string();
    if(tok == STR && t.tok == STR) sval = t.sval;
    else copyUnion(t);
    tok = t.tok;
    return *this;
}

//t23
Token& Token::operator=(Token&& t){
    if (this != &t) {
        if(tok == STR) sval.std::string::~string();
        moveUnion(std::move(t));
        std::move(t.tok);
    }
    return *this;
}
void Token::moveUnion(const Token&& t){
    switch (t.tok) {
    case Token::INT:ival = t.ival; break;
    case Token::CHAR:cval = t.cval; break;
    case Token::DBL:dval = t.dval; break;
    case Token::STR: new(&sval)std::string(std::move(t.sval)); break;
    }
}

void test() {
    Token t;
    t.print();
    t = 1;
    t.print();
    t = 1.56;
    t.print();
    t = 'z';
    t.print();
    t = "'zzz'";
    t.print();
}
}
(19.21)
(19.22)
 string 一样, 该构造的时候构造,该析构的时候析构.
(19.23)
(19.24)
(19.25)

在这里插入图片描述

namespace t26{
extern "C" int compute(int *, int) { printf("C\n");}
double compute(double *, double) {std::cout << "C++\n"; }
void test() { 
    int i = 1;
    double d = 1.2;
    compute(&i, i);
    compute(&d, d);
}
}
(19.26)
如果都声明成extern "C", 会报下面的错误
error: conflicting declaration of C function double t26::compute(double*, double)
 extern "C" double compute(double *, double);

打赏一个呗

取消

感谢您的支持,我会继续努力的!

扫码支持
扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦