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