C++Prime 第十九章
练习19.1
void* operator new(size_t size)
{
if (void* mem = malloc(size))
return mem;
else
throw bad_alloc();
}
void operator delete(void* mem)noexcept
{
free(mem);
}
练习19.2
略
练习19.3
(a) 失败,基类不可向派生类转化
(b) 成功
© 成功
存疑.
练习19.4
略
练习19.5
并非任何时候都能定义一个虚函数,假设我们无法使用虚函数,则可以使用一个RTTI运算符.
练习19.6
Query_base* p;
if (AndQuert* q = dynamic_cast<AndQuery*> p)
cout << "yes";
else
{
cout << "no";
}
练习19.7
Query_base obj;
try
{
AndQuery& tmp = dynamic_cast<AndQuery&> obj;
}
catch (bad_cast c) {
;//
}
练习19.8
Query_base q1, q2;
if (typeid(q1) == typeid(q2))
;//do something
if (typeid(q1) == typeid(AndQuery))
;//do something
练习19.9
int main()
{
cout << typeid(int).name() << endl;
cout << typeid(long).name() << endl;
cout << typeid(string[]).name() << endl;
cout << typeid(char&).name() << endl;
return 0;
}
练习19.10
class A { };
class B : public A { };
class C : public B { };
using namespace std;
void testA() {
cout << "A:" << endl;
A* pa = new C;
cout << typeid(pa).name() << endl;
}
void testB() {
cout << "B:" << endl;
C cobj;
A& ra = cobj;
cout << typeid(&ra).name() << endl;
}
void testC() {
cout << "C:" << endl;
B* px = new B;
A& ra = *px;
cout << typeid(ra).name() << endl;
}
int main(int argc, char** argv)
{
testA();
testB();
testC();
return 0;
}
(a) class A*
(b) class A*
© class A
练习19.11
普通指针和指向数据成员的指针主要的区别:
语法上:
首先,定义时指向数据成员的指针必须标出类型名,如Screen::
其次,使用时, .* 和 ->* 这两种
意义上:
普通指针的值是它所指向的对象在内存中的地址,0表示空
数据成员指针的值是它所指向的数据成员相对于对象起始地址的偏移量,-1表示空.
练习19.12
class Screen {
public:
typedef std::string::size_type pos;
char get_cursor()const { return contents[cursor]; }
char get()const;
char get(pos ht, pos wd)const;
static const string Screen::* data()
{
return &Screen::contents;
}
static const pos Screen::* pcursor() {
return &Screen::cursor;
}
private:
pos cursor;
pos height, width;
string contents;
};
int main(int argc, char** argv)
{
auto p = Screen::pcursor();
return 0;
}
练习19.13
typedef const std::string Sales_data::* bookNo PBN;//PBN即为题目要求的类型
练习19.14
合法.
练习19.15
和普通函数不同的是,在成员函数和指向该成员的指针之间不存在自动转换的规则.
练习19.16
lve
练习19.17
using pmf1 = char (Screen::*)() const;
using pmf2 = char (Screen::*)() const;
using pmf3 = char (Screen::*)(pos, pos)const;
练习19.18
int main(int argc, char** argv)
{
vector<string> svec{ "","","dsf","","sdf" };
function<bool (const string&)> fcn = &string::empty;
int cnt = count_if(svec.begin(), svec.end(), fcn);
cout << cnt << endl;
return 0;
}
练习19.19
略
练习19.20
class TextQuery {//简略版
public:
class QueryResult;
};
练习19.21
class Token {
public:
//因为union含有一个string成员,所以Token必须定义拷贝控制成员
//定义移动构造函数和移动赋值运算符的任务
Token() :tok(INT), ival{ 0 }{ }
Token(const Token& t) :tok(t.tok) { copyUnion(t); }
Token& operator=(const Token&);
//如果union含有一个string成员,则我们必须销毁它
~Token() { if (tok == STR) sval.~string(); }
Token& operator=(const string&);
Token& operator=(int);
Token& operator=(double);
Token& operator=(char);
private:
enum {INT,CHAR,DBL,STR} tok; //判别式
union {//匿名union
char cval;
int ival;
double dval;
string sval;
};//每个Token对象含有一个该未命名union类型的未命名成员
//检查判别式,然后酌情拷贝union成员
void copyUnion(const Token&);
};
Token& Token::operator=(const Token& t)
{
//如果此对象的值是string而t的值不是,而我们必须释放原来的string
if (tok == STR && t.tok != STR) sval.~string();
if (tok == STR && t.tok == STR)
sval = t.sval;
else
copyUnion(t);
tok = t.tok;
return *this;
}
Token& Token::operator=(const string& s)
{
if (tok == STR)
sval = s;
else
new(&sval) string(s);
tok = STR;
return *this;
}
Token& Token::operator=(int i)
{
if (tok == STR) sval.~string();
tok = INT;
ival = i;
return *this;
}
Token& Token::operator=(double d)
{
if (tok == STR) sval.~string();
tok = DBL;
dval = d;
return *this;
}
Token& Token::operator=(char c)
{
if (tok == STR) sval.~string();
tok = CHAR;
cval = c;
return *this;
}
void Token::copyUnion(const Token& t)
{
switch (t.tok) {
case INT:ival = t.ival; break;
case STR:new(&sval) string(t.sval); break;
case CHAR:cval = t.cval; break;
case DBL:dval = t.dval; break;
}
}
练习19.22
class Sales_data {
};
class Token {
public:
//因为union含有一个string成员,所以Token必须定义拷贝控制成员
//定义移动构造函数和移动赋值运算符的任务
Token() :tok(INT), ival{ 0 }{ }
Token(const Token& t) :tok(t.tok) { copyUnion(t); }
Token& operator=(const Token&);
//如果union含有一个string成员,则我们必须销毁它
~Token() { if (tok == STR) sval.~string(); }
Token& operator=(const string&);
Token& operator=(int);
Token& operator=(double);
Token& operator=(char);
Token& operator=(const Sales_data& sd);//新加入
private:
enum {INT,CHAR,DBL,STR,SALES} tok; //判别式
union {//匿名union
char cval;
int ival;
double dval;
string sval;
Sales_data sdval;//新加入
};//每个Token对象含有一个该未命名union类型的未命名成员
//检查判别式,然后酌情拷贝union成员
void copyUnion(const Token&);
};
Token& Token::operator=(const Token& t)
{
//如果此对象的值是string而t的值不是,而我们必须释放原来的string
if (tok == STR && t.tok != STR) sval.~string();
//如果此对象的值是sales_data而t的值不是,而我们必须释放原来的sales_data
if (tok == SALES && t.tok != SALES) sdval.~Sales_data();//新加入
if (tok == STR && t.tok == STR)
sval = t.sval;
else if (tok == SALES && t.tok == SALES)
sdval = t.sdval;
else
copyUnion(t);
tok = t.tok;
return *this;
}
Token& Token::operator=(const string& s)
{
if (tok == STR)
sval = s;
else
new(&sval) string(s);
tok = STR;
return *this;
}
Token& Token::operator=(int i)
{
if (tok == STR) sval.~string();
tok = INT;
ival = i;
return *this;
}
Token& Token::operator=(double d)
{
if (tok == STR) sval.~string();
tok = DBL;
dval = d;
return *this;
}
Token& Token::operator=(char c)
{
if (tok == STR) sval.~string();
tok = CHAR;
cval = c;
return *this;
}
Token& Token::operator=(const Sales_data& sd)
{
if (tok == STR)//释放str
sval.~string();
sdval = sd;//调用Sales_data的赋值运算符
tok = SALES;
return *this;
}
void Token::copyUnion(const Token& t)
{
switch (t.tok) {
case INT:ival = t.ival; break;
case STR:new(&sval) string(t.sval); break;
case CHAR:cval = t.cval; break;
case DBL:dval = t.dval; break;
case SALES:sdval = t.sdval; break;//新加入
}
}
练习19.23
Token(Token&& t);
Token& operator=(Token&& t);
练习19.24
直接赋值.
练习19.25
同上,已经完成了.
练习19.26
使用C语言函数compute,参数的指针也是C语言的指针.
是不合法的,因为C语言没有重载,而这两个函数名称相同.
总结和展望
至此,历时40多天,全部4个部分,19个章节算是基本完成了.
其实看这本C++prime(第五版) 之前我已经看完了两遍C++PrimePlus(第六版)(题解没有发博客,重装系统后丢失了).
但是此次通读C++Prime依旧是收获颇丰,感觉这本书真的是经典,值得反复诵读.下面是我的一些感悟.
part1部分共7章,讲述了语法方面最基本内容,一些内容看似简单,其实需要反复诵读.比如,auto和decltype的类型推断,这些内容在C++PrimePlus里并没有过多地讲解,C语言里则不使用,但是在C++11里却是异常的重要.decltype一个函数会得到什么类型,auto自动推断一个函数又会得到什么类型?能想出来几种返回数组指针的方式?看书之时跟着例子很是清楚,但是当我现在看完这19章后,很多方法又忘了,需要调试,再过段时间就会遗忘,所以我会在未来三个月内,重点掌握一到两种方法,拿来使用,其余的重复的读,甚至背诵(应付面试),以及对照例子再去记忆这些易错点.
part2部分讲述了标准库的内容,6个顺序容器,3个容器适配器,8个关联容器,泛型算法,智能指针,这些内容都是重中之重,必读必会反复多练.同时可以参考C++PrimePlus上的第十六章一起学习本部分知识,可以学习到某些具体的泛型算法的使用.
part3部分结合第一部分的第7章,讲解了C++极其重要的 类(class/struct).好几种构造函数,拷贝控制函数,交换函数,右值/左值的引用,对运算符的重载继而介绍了函数对象,继而扩展了可调用对象引出了function,类的继承,虚函数,全是重中之重.之后第十六章介绍了模板,函数模板,类模板,也是必看.
part4第17章讲了boost里的tuple,bitset,regex,随机数,io格式控制.现在看完一遍后,以后多加温习,用到是再深入学习.18章的异常处理是重点,命名空间和多重继承虚继承感觉知道点理论,习题做完就可以了,用到再学.19章的内容多看看温习一下,用到时能找到就好了.
下一步的学习计划是用C++做一些练手项目,目前打算是自己用QT独立完成大约5个常玩的小游戏(已经跟视频做完了一个简陋的飞机大战,明天开始添加新功能),完善复试前的web服务器项目,这个项目C/C++混用,重构一下,全部使用C++,以及开始leetcode疯狂刷题,直到1年半后找工作.
那么从7月13号明天开始,大约在半年内,2021年前,我会在经过大量练手项目,重构以前的一个重点项目(顺带复习一遍网络面试),以及本书重点背2/3遍,面试题(C++部分)背熟之后,开始读第二遍这本书,届时会按面试的重要程度来学习,对每个知识点重新学习.
----来自一个已经自学计算机7个月的小白