先看一个例子:

#include <string>
#include <memory>
#include <iostream>
using namespace std;
class A{private:int * d;size_t * use_count;public:A(int n):d(new int(n)),use_count(new size_t(1)){}A(const A & a):d(a.d),use_count(a.use_count){++ * use_count;}A& operator=(const A& a){++ *(a.use_count);if(-- *use_count == 0){delete use_count;delete d;             }use_count  = a.use_count;d = a.d;    return *this;}~A(){if(--*use_count == 0){delete use_count;delete d;}use_count = nullptr;d = nullptr;}void ret_use(){cout << *use_count << endl;}};int main()
{
A a(1234);
a.ret_use();
A b(a);
a.ret_use();
b.ret_use();
A *c = new A(a);
c->ret_use();
a.ret_use();
b.ret_use();return 0;
}

运行结果:

1
2
2
3
3
3

结论:new 动态分配对象和普通对象调用的拷贝构造函数成员或者构造函数完全一致。不过就是存储的方式不同罢了。创建对象所调用的那些函数(构造函数是一样样的)。

delete 一个指类类型的动态类型指针会发生什么?

class Quote{
public:std::string isbn() const;virtual double new_price(size_t n) const;
};
class Bulk_quote : public Quote {
public:double net_price(size_t )const override;
};

虚函数:对于基类的某些函数,基类希望它的派生类能各自定义适合自己的版本,此时基类就将这些函数声明成虚函数(virtual function)。

访问说明符:比如private,public,protected;

override:c++11允许派生类显式地注明它使用哪个成员函数改写基类的虚函数,具体措施是在该类的形参列表之后增加一个override关键字。override位于&,const,等之后。

动态绑定:当通过一个基类的指针或者引用调用一个虚函数时候,这个虚函数会根据调用的实际对象类型选择使用对应的版本,这个过程叫做动态绑定。因为是在运行时选择函数的版本,所以动态绑定又叫做运行时绑定。

访问控制与继承:继承类的成员函数可以直接访问基类的public成员和protected成员,但是不能直接访问基类的private成员。

习题15.2.1练习

练习15.1

答:类中被声明为virtual的成员,基类希望这种成员在派生类中重新定义。除了构造函数外,任意非static成员都可以定义为虚成员。

练习15.2

答:protected是受保护的成员,可以被该类的成员、友元和派生类成员(非友元)访问,而不可以被该类的普通用户访问。而private成员只能被基类的友元和成员访问,不能被派生类访问。

练习15.3

答:

定义你自己的quote类和print_total成员函数。

15.4

b对,a,c错误。

15.5
答:

15.6

class Bulk_quote : public Quote //Bulk_quote derived from Quote
{
public:Bulk_quote() = default;Bulk_quote(const string &,double,size_t ,double);//覆盖基类的版本以实现大量购买的折扣和政策double net_price(size_t n)const override;   //override:显式地用这个成员函数改写基类的虚函数}
private:size_t min_qty;double discount = 0.0;};

15.7

class Limitted_quote: public Quote
{public:double net_price(size_t cnt)const override{if(cnt <= min_qty){return cnt * (1 - discount) * price;          }elsereturn min_qty * (1-discount)*price + (cnt - min_qty) * price;    }private:size_t min_qty;double discount;};

15.8

静态类型:变量声明的类型或者表达式生成的类型,静态类型在编译期就已经决定了。动态类型:变量或者表达式表示的内存中的对象的类型,动态类型直到运行时才能知道。如 Quote * pQuote =

new Bulk_quote;指针pQuote的静态类型是Quote,而动态类型是Bulk_quote,直到运行时才能知道它指向的类型是基类还是派生类。如果一个变量非指针也非引用,则它的静态类型和动态类型永远一致,但基类的指针或者引用的动态类型可能与其静态类型不一致。

15.9

当一个对象是非指针也非引用,静态类型和动态类型永远一致。当给基类对象的指针或者引用赋值或者初始化时,静态类型和动态类型可能不一致。

Bulk_quote bulk;
Quote * pQuote = & bulk;
Quote & rQuote =   bulk;
传递给item的如果是派生类对象,即是静态类型和动态类型不同的情况。
double print_total(ostream &os,const Quote &item,size_t n);

15.10

可能传递给iostream对象,也可以传递派生类对象,比如fstream对象。

15.3虚函数

习题15.11

void Quote::debug_1()const
{
cout << "bookNo:" << bookNo << endl;
cout << "price:" << price << endl;
}void Bulk_quote::debug_1()const
{
cout << "min_qty:" << min_qty << endl;
cout << "discount:" << discount << endl;
}

15.12

有必要

override:c++11新标准中我们可以使用override关键字来说明派生类中的虚函数,这么做的好处是使得我们的意图更加清洗即明确地告诉编译器我们想要覆盖已存在的虚函数。如果定义个了函数与基类中的名字相同但是形参列表不同,在不使用override关键字的时候这种函数定义是合法的,在使用了override关键字之后这种行为是非法的,编译器会提示出错。

final:如果我们将某个函数定义成final,则不允许后续的派生类来覆盖这个函数,否则会报错。

因此,将一个成员函数声明称为override和final能够使得我们的意图更加清晰。

15.13

该派生类的虚函数想要调用它覆盖的基类的虚函数版本,但是没有使用使用作用域运算符,会导致无限递归。

fixed: void print(ostream &os) {base::print(os); os << "  " << i;}

15.15

#include <string>
#include <iostream>
using namespace std;class Bulk_quote;
class Quote{
public:Quote() = default;Quote(const string &book,double sales_price):bookNo(book),price(sales_price){}string isbn()const;virtual double net_price(size_t n)const;double print_total(ostream &os,const Quote &item,size_t n);virtual ~Quote();virtual void debug_1()const;
private:string bookNo;
protected:double price = 0.0;
};class Limitted_quote: public Quote
{public:double net_price(size_t cnt)const override{if(cnt <= min_qty){return cnt * (1 - discount) * price;          }elsereturn min_qty * (1-discount)*price + (cnt - min_qty) * price;    }private:size_t min_qty;double discount;};class Disc_quote: public Quote{public:Disc_quote() = default;Disc_quote(const string &book,double price,size_t qty,double disc):Quote(book,price),quantity(qty),discount(disc){}double net_price(size_t)const = 0;private:
size_t quantity = 0;
double discount = 0;};
class Bulk_quote : public Disc_quote //Bulk_quote derived from Quote
{
public:Bulk_quote() = default;Bulk_quote(const string &,double,size_t ,double);//覆盖基类的版本以实现大量购买的折扣和政策double net_price(size_t n)const override;   //override:显式地用这个成员函数改写基类的虚函数void debug_1()const override;private:size_t min_qty = 0;double discount = 0.0;};

15.17

编译器会给出错误信息:Disc_quote is an abstract type......意思是Disc_quote是一个抽象基类。不能定义抽象基类的对象。

15.18

只有d1和dd1可以。因为只有派生类共有地继承基类时,用户代码才能使用派生类到基类的转换。只有d1和dd1是共有地继承,所以只有这2个是可以的。

或者可以这样理解,只有当基类的共有成员是可访问的,才能使用派生类到基类的转换。目前对于用户来说,共有成员只对d1和dd1是可见的。

15.19

如果派生类继承基类是共有的,那么所有用户都可以使用从派生类到基类的转换。

如果派生类继承基类是共有的或者是受保护的,那么派生类的成员函数和友元可以使用从派生类到基类的转换,普通用户不可以。

如果D继承B是共有的或者受保护的,那么D的派生类或的成员或友元可以使用从D到B的转换,如果D到B的继承是私有的,那么D的派生类的成员或者友元将不能使用D到B的转换。

一户话:如果基类的共有成员对某个用户(对象用户或者代码实现用户)是可见的,那么从派生类到基类是可以转换的,反之则不可以。

Derived_from_private: private Priv_Derv这个类的函数不合法。

15.20

#include <string>
#include <iostream>
using namespace std;
class Base{public:void pub_mem();protected:int prot_mem = -1234;private:char priv_mem;};struct Pub_Derv: public Base
{
int f(){return prot_mem;}void memfcn(Base &b){b = *this;cout << "Pub_Derv" << endl;}
};struct Priv_Derv: private Base
{
int f1() const{return prot_mem;}
void memfcn(Base &b)
{b = *this;cout << "Priv_Derv" << endl;
}
};struct Prot_Derv:protected Base
{
int f2(){return prot_mem;}
void memfcn(Base &b)
{
b = *this;
cout  << "Prot_Derv" << endl;
}
};struct Derived_from_Public:public Pub_Derv
{
int use_base(){return prot_mem;}
void memfcn(Base &b){b= *this;cout << "Derived_from_Public" << endl;
}
};struct Derived_from_Protected:protected Prot_Derv
{
int use_base()
{return prot_mem;
}
void memfcn(Base &b)
{b = *this;cout << "Derived_from_Protected" << endl;
}
};int main()
{
Pub_Derv d1;
Priv_Derv d2;
Prot_Derv d3;
Derived_from_Public dd1;
//Derived_from_Private dd2;
Derived_from_Protected dd3;
Base base;
Base *p = new Base;
p = &d1;
//p = &d2;
//p = &d3;
p = &dd1;
//p = &dd2;
//p = &dd3;
d1.memfcn(base);
d2.memfcn(base);
d3.memfcn(base);dd1.memfcn(base);
//dd2.memfcn(base);
dd3.memfcn(base);
return 0;return 0;
}

15.24

作为基类使用的类应该具有虚析构函数,以保证在删除指向动态分配对象的基类指针时,根据基类的实际指向的对象所属的类型运行时当的析构函数。

虚析构函数可以为空,即不执行任何操作。一般而言,析构函数的主要作用是清除本类中定义的数据成员。如果该类没有定义指针类的成员,则使用合成版本即可;如果该类定义了指针成员,则一般需要自定义析构函数以保证对指针成员进行适当的清除。因此,如果有虚析构函数必须执行的操作,则就是清除本类中定义的数据成员的操作。

15.26

自己编写版本(后面有书上的答案部分)

#include <string>
#include <iostream>
using namespace std;
class Quote{public:Quote(){cout << "Quote()"<< endl;};Quote(const Quote & rhs){cout << "Quote(const Quote &rhs)" << endl;}Quote(Quote &&rhs){ cout << "Quote(Quote &&rhs)" << endl;}Quote &operator=(const Quote &rhs) {cout << "Quote& operator=(const Quote&)" << endl;bookNo = rhs.bookNo;price = rhs.price;return *this;}Quote &operator=(Quote &&rhs){bookNo = std::move(rhs.bookNo);price = std::move(rhs.price);cout <<"Quote& operator(Quote &&)" << endl;return *this;}virtual ~Quote() {cout << "~Quote()" << endl;}private:string bookNo; double price = 0.0;
};
class Disc_quote : public Quote
{public:Disc_quote(){ cout << "Disc_quote()" << endl;}Disc_quote(const Disc_quote &rhs):Quote(rhs),quantity(),discount(){cout << "Disc_quote(const Disc_quote&)" <<endl;    }Disc_quote(Disc_quote && rhs):Quote(std::move(rhs)),quantity(rhs.quantity),discount(rhs.discount){cout << "Disc_quote(Disc_quote(Disc_quote &&rhs))" << endl;}Disc_quote& operator=(const Disc_quote & rhs){quantity = rhs.quantity;discount = rhs.discount;cout << "Disc_count&operator=(const Disc_quote &rhs) " << endl;return *this;   }~Disc_quote()override {cout <<"~Disk_quote()"<< endl;}protected:size_t quantity = 0;double discount = 0;};class Bulk_quote : public Disc_quote
{public:Bulk_quote(){cout  << "Bulk_quote()" <<endl;}Bulk_quote(const Bulk_quote & rhs):Disc_quote(rhs){}Bulk_quote(Bulk_quote && rhs):Disc_quote(rhs){}Bulk_quote& operator=(const Bulk_quote &rhs){Disc_quote::operator=(rhs);cout << "operator=(const Bulk_quote) " << endl;return *this; }Bulk_quote& operator=(Bulk_quote && rhs){Disc_quote::operator=(std::move(rhs));cout << "Bulk_quote(Bulk_quote && rhs)"   << endl;  return *this;}~Bulk_quote() override
{cout << "~Bulk_quote()" << endl;
}};int main()
{Bulk_quote  b1;return 0;
}
Quote()
Disc_quote()
Bulk_quote()
~Bulk_quote()
~Disk_quote()
~Quote()

书上的答案(15.26)Quote部分:

class Quote{public:Quote(const string &book = "",double sales_price = 0):bookNo(book),price(sales_price){cout << "call Quote(const string &book,double sales_price = 0)" << endl;}Quote(const Quote & rhs){cout << "Quote(const Quote &rhs)" << endl;}Quote(Quote &&rhs){ cout << "Quote(Quote &&rhs)" << endl;}Quote &operator=(const Quote &rhs) {cout << "Quote& operator=(const Quote&)" << endl;bookNo = rhs.bookNo;price = rhs.price;return *this;}Quote &operator=(Quote &&rhs){bookNo = std::move(rhs.bookNo);price = std::move(rhs.price);cout <<"Quote& operator(Quote &&)" << endl;return *this;}virtual double net_price(size_t n)const{return n * price;      }virtual ~Quote() {cout << "~Quote()" << endl;}friend ostream & operator<<(ostream &os,const Quote & rhs){os << "bookNo:"<<rhs.bookNo << "price:" << rhs.price;return os;}private:string bookNo; double price = 0.0;
};

习题15.27

#include <string>
#include <iostream>
using namespace std;
class Quote{public:Quote(){cout << "Quote()"<< endl;};Quote(const Quote & rhs){cout << "Quote(const Quote &rhs)" << endl;}Quote(const string &book,double sales_price):bookNo(book),price(sales_price){}Quote(Quote &&rhs){ cout << "Quote(Quote &&rhs)" << endl;}Quote &operator=(const Quote &rhs) {cout << "Quote& operator=(const Quote&)" << endl;bookNo = rhs.bookNo;price = rhs.price;return *this;}Quote &operator=(Quote &&rhs){bookNo = std::move(rhs.bookNo);price = std::move(rhs.price);cout <<"Quote& operator(Quote &&)" << endl;return *this;}virtual ~Quote() {cout << "~Quote()" << endl;}protected:string bookNo; double price = 0.0;
};
class Disc_quote : public Quote
{public:using Quote::Quote;Disc_quote(){ cout << "Disc_quote()" << endl;}Disc_quote(const Disc_quote &rhs):Quote(rhs),quantity(),discount(){cout << "Disc_quote(const Disc_quote&)" <<endl; }Disc_quote(Disc_quote && rhs):Quote(std::move(rhs)),quantity(rhs.quantity),discount(rhs.discount){cout << "Disc_quote(Disc_quote(Disc_quote &&rhs))" << endl;}Disc_quote& operator=(const Disc_quote & rhs){quantity = rhs.quantity;discount = rhs.discount;cout << "Disc_count&operator=(const Disc_quote &rhs) " << endl;return *this;   }~Disc_quote()override {cout <<"~Disk_quote()"<< endl;}friend ostream &operator<<(ostream &os,const Disc_quote & q){os << "bookNo:" << q.bookNo << " " << "price:" << q.price << " quantity:" << q.quantity << " discount:" << q.discount << endl;return os;}protected:size_t quantity = 0;double discount = 0;};class Bulk_quote : public Disc_quote
{public:Bulk_quote(){cout  << "Bulk_quote()" <<endl;}Bulk_quote(const Bulk_quote & rhs):Disc_quote(rhs){}Bulk_quote(Bulk_quote && rhs):Disc_quote(rhs){}Bulk_quote& operator=(const Bulk_quote &rhs){Disc_quote::operator=(rhs);cout << "operator=(const Bulk_quote) " << endl;return *this; }Bulk_quote& operator=(Bulk_quote && rhs){Disc_quote::operator=(std::move(rhs));cout << "Bulk_quote(Bulk_quote && rhs)"   << endl;  return *this;}~Bulk_quote() override
{cout << "~Bulk_quote()" << endl;
}};int main()
{Bulk_quote  b1;
Disc_quote d("isbn-123-345-456",12.12);
cout << d << endl;return 0;
}

15.28

#include <string>
#include <iostream>
#include <vector>
#include <memory>
using namespace std;
class Quote{public:Quote(){cout << "Quote()"<< endl;};Quote(const Quote & rhs){cout << "Quote(const Quote &rhs)" << endl;}Quote(const string &book,double sales_price):bookNo(book),price(sales_price){}Quote(Quote &&rhs){ cout << "Quote(Quote &&rhs)" << endl;}Quote &operator=(const Quote &rhs) {cout << "Quote& operator=(const Quote&)" << endl;bookNo = rhs.bookNo;price = rhs.price;return *this;}Quote &operator=(Quote &&rhs){bookNo = std::move(rhs.bookNo);price = std::move(rhs.price);cout <<"Quote& operator(Quote &&)" << endl;return *this;}virtual double net_price(size_t n)const{cout << "Quote::net_price(size_t)" << endl;return n * price;}virtual ~Quote() {cout << "~Quote()" << endl;}protected:string bookNo; double price = 0.0;
};
class Disc_quote : public Quote
{public:using Quote::Quote;Disc_quote(){ cout << "Disc_quote()" << endl;}Disc_quote(const Disc_quote &rhs):Quote(rhs),quantity(),discount(){cout << "Disc_quote(const Disc_quote&)" <<endl; }Disc_quote(const string &book,double sales_price,size_t qty,double disc):Quote(book,sales_price),quantity(qty),discount(disc){cout << "Disc_quote(const string&,double,size_t ,double)" << endl;}Disc_quote(Disc_quote && rhs):Quote(std::move(rhs)),quantity(rhs.quantity),discount(rhs.discount){cout << "Disc_quote(Disc_quote(Disc_quote &&rhs))" << endl;}Disc_quote& operator=(const Disc_quote & rhs){quantity = rhs.quantity;discount = rhs.discount;cout << "Disc_count&operator=(const Disc_quote &rhs) " << endl;return *this;    }~Disc_quote()override {cout <<"~Disk_quote()"<< endl;}double net_price(size_t n)const override{cout << "Disc_quote::net_price(size_t)" << endl;return n * price;}friend ostream &operator<<(ostream &os,const Disc_quote & q){os << "bookNo:" << q.bookNo << " " << "price:" << q.price << " quantity:" << q.quantity << " discount:" << q.discount << endl;return os;}protected:size_t quantity = 0;double discount = 0;};class Bulk_quote : public Disc_quote
{public:Bulk_quote(){cout  << "Bulk_quote()" <<endl;}Bulk_quote(const Bulk_quote & rhs):Disc_quote(rhs){}Bulk_quote(Bulk_quote && rhs):Disc_quote(rhs){}Bulk_quote& operator=(const Bulk_quote &rhs){Disc_quote::operator=(rhs);cout << "operator=(const Bulk_quote) " << endl;return *this; }Bulk_quote& operator=(Bulk_quote && rhs){Disc_quote::operator=(std::move(rhs));cout << "Bulk_quote(Bulk_quote && rhs)"   << endl;  return *this;}~Bulk_quote() override
{cout << "~Bulk_quote()" << endl;
}};int main()
{
/*
//根据打印结果,如果vector类型为vector<Quote>,那么存储的类型都是Quote类型,即使赋值时候的类型是
//Disc_quote类型,赋值的过程中也会把继承部分切掉。//自己编写的例子
vector<Quote> v1;
v1.push_back(Quote("isbn-001-v1",12.4));
v1.push_back(Disc_quote("isbn-002-v1",12.6,100,14.2));
cout << v1[0].net_price(10) << endl;
cout << v1[1].net_price(20) << endl;//自己编写的例子:v2是Quote*类型
vector<shared_ptr<Quote>> v2;
v2.push_back(make_shared<Quote>(Quote("isbn1-net1-0001",12.5)));
v2.push_back(make_shared<Disc_quote>(Disc_quote("isbn2-net2-0002",12.5,100,14)));
cout << v2[0]->net_price(10) << endl;
cout << v2[1]->net_price(10) << endl;
*///结果发现,定义时候的类型是Quote,即使插入的元素类型是Disc_quote类型,那么
//调用的全部都是Quote::net_price(size_t )
vector<Quote> itemVec;
for(size_t i = 0;i != 10; ++i){Disc_quote item("isbn-001-v1",12.5,100,12);itemVec.push_back(item);    }
double sum = 0;
for(vector<Quote>::iterator it = itemVec.begin(); it != itemVec.end(); ++ it){sum += it->net_price(10);  }
cout << sum << endl;return 0;
}

15.29

int main()
{
//结果发现,定义时候的类型是Quote,即使插入的元素类型是Disc_quote类型,那么
//调用的全部都是Quote::net_price(size_t )
vector<shared_ptr<Quote>> itemVec;
for(size_t i = 0;i != 10; ++i){Disc_quote item("isbn-001-v1",12.5,100,12);itemVec.push_back(make_shared<Disc_quote>(item)); }
double sum = 0;
for(vector<shared_ptr<Quote>>::iterator it = itemVec.begin(); it != itemVec.end(); ++ it){sum += (*it)->net_price(10); }
cout << sum << endl;return 0;
}

15.8.1节基础内容

#include <string>
#include <vector>
#include <iostream>
#include <memory>
#include <set>
using namespace std;
class Quote{
public:Quote() = default;Quote(const string &book,double sales_price):bookNo(book),b(sales_price){}const string & isbn()const{return bookNo;       }   private://static可以用户不完全类型,如果不定义为static类型会报错//猜测,名字解析先编译申明部分,但是此时compareIsbn是不完全类型//不完全类型有时候不能当做一个类型使用static bool compareIsbn(const Quote &lhs,const Quote &rhs){return lhs.isbn() < rhs.isbn();}string bookNo;int b;multiset<shared_ptr<Quote>,decltype(compareIsbn)*> item{compareIsbn};
//上面不用用圆括号()};
int main()
{return 0;
}

上面的代码有2个问题需要思考:

(1)为何compareIsbn一定定义成static的,如果去掉static会报错。如下错误:

2.cc:23:50: error: invalid use of non-static member function ‘bool Quote::compareIsbn(const Quote&, const Quote&)’23 |   multiset<shared_ptr<Quote>,decltype(compareIsbn)*> item{compareIsbn};|                                                  ^
2.cc:17:9: note: declared here17 |    bool compareIsbn(const Quote &lhs,const Quote &rhs)|         ^~~~~~~~~~~
2.cc:23:52: error: template argument 2 is invalid23 |   multiset<shared_ptr<Quote>,decltype(compareIsbn)*> item{compareIsbn};|                                                    ^
2.cc:23:70: error: cannot convert ‘<brace-enclosed initializer list>’ to ‘int’ in initialization23 |   multiset<shared_ptr<Quote>,decltype(compareIsbn)*> item{compareIsbn};

猜测,可能是解析multiset<shared_ptr<Quote>,decltype(compareIsbn)*>时候,compare是不完全类型。

(2) .......item{compareIsbn},这里不能用圆括号。如果换成(),会按照如下方式报错:

2.cc:26:59: error: ‘compareIsbn’ is not a type26 |   multiset<shared_ptr<Quote>,decltype(compareIsbn)*> item(compareIsbn);

练习15.23

// 15.7.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <string>
#include <iostream>
#include <memory>
using namespace std;
class Base{
public:virtual int fcn(){cout << "Base::fcn()" << endl;return 0;}private:};class D1 : public Base{
public:int fcn()override{cout << "D1::fcn()" << endl;return 0;}private:};class D2 : public D1{
public:int fun(int){ cout << "D2::fcn(int)" << endl; }virtual int fcn()override{cout << "D2:fcn()" << endl;return 0;}private:};int _tmain(int argc, _TCHAR* argv[])
{Base bobj; D1 d1obj; D2 d2obj;Base *bp1 = &bobj, *bp2 = &d1obj, *bp3 = &d2obj;bp1->fcn();bp2->fcn();bp3->fcn();D1 *d1p = &d1obj; D2 *d2p = &d2obj;cout << "can you see it?" << endl;system("pause");return 0;
}

15.24哪种类需要虚析构函数?虚析构函数需要执行什么样的操作?

解:

作为基类使用的类应该具有虚析构函数,以保证在删除指向动态分配的对象的基础类指针时,根据指针实际指向的对象所属的类型运行适当的析构函数。

虚析构函数可以为空,即不执行任何操作。一般而言,析构函数的主要作用是清楚本类中定义的数据成员。如果该类没有定义指针类成员,则使用合成的版本即可;如果该类定义了指针成员,则一般需要自定义析构函数以对指针成员进行适当的清楚。因此,如果有虚析构函数必须执行的操作,则就是清楚本类中定义的数据成员的操作。

练习15.26答案

//文件 main.cc
#include <string>
#include <iostream>
#include "quote.h"
using namespace std;int main()
{
Quote q1;
Bulk_quote d1;return 0;
}
//文件functions.cc
#include <string>
#include <iostream>
#include <memory>
#include "quote.h"
using namespace  std;string Quote::isbn()const{return bookNo;
}double Quote::net_price(size_t n)const
{return n * price;
}double print_total(ostream &os,const Quote &item,size_t n)    //override:显式地用这个成员函数改写基类的虚函数
{
//根据传入的item的形参的对象类型调用Quote::net_price 或者 Bulk_quote::net_price
//共有成员访问的时候必须用对象去访问。
double ret = item.net_price(n);
os << "ISBN: " << item.isbn() << "# sold: " << n << " total due: " << ret << endl;return ret;
}//destructor
Quote::~Quote(){cout << "Quote::~Quote()" << endl;}Bulk_quote::Bulk_quote(const string &book,double p,size_t qty,double disk):Disc_quote(book,p,qty,disk)
{cout << "Bulk_quote(const string &,double ,size_t ,double)" << endl;
}double Bulk_quote::net_price(size_t n)const
{return 1.1;
}//test
void call_fun(Quote q)
{cout << q.net_price(12) << endl;}
//文件quote.h
#ifndef QUOTE_INCLUDE_H
#define QUOTE_INCLUDE_H#include <string>
#include <iostream>
using namespace std;class Bulk_quote;
class Quote{
public:Quote(){cout << "Quote()" << endl;};Quote(const string &book,double sales_price):bookNo(book),price(sales_price){cout << "Quote(const string &,double )" << endl;}Quote(const Quote & q):bookNo(q.bookNo),price(q.price){cout << "Quote(const Quote &)" << endl;}Quote(Quote &&q):bookNo(std::move(q.bookNo)),price(q.price){cout << "Quote(Quote &&)" << endl;}Quote& operator=(const Quote &q){bookNo = q.bookNo;price = q.price;cout << "Quote::operator=" << endl;return *this;        }Quote& operator=(Quote &&q){bookNo = std::move(q.bookNo);price = q.price;cout << "Quote::operator=(Quote&&)" << endl;return *this;       }string isbn()const;virtual double net_price(size_t n)const;friend double print_total(ostream &os,const Quote &item,size_t n);virtual ~Quote();
private:string bookNo;
protected:double price = 0.0;
};//abstract class
class Disc_quote: public Quote{public:Disc_quote(){cout << "Disc_quote()" << endl;}Disc_quote(const string &book,double price,size_t qty,double disc):Quote(book,price),quantity(qty),discount(disc){cout << "Disc_quote(const string&,double ,size_t ,double) " <<endl;}Disc_quote(const Disc_quote &q):Quote(q),quantity(q.quantity),discount(q.discount){cout << "Disc_quote(const Disc_quote &)" << endl;     }Disc_quote(Disc_quote &&q):Quote(std::move(q)),quantity(q.quantity),discount(q.discount){}Disc_quote & operator=(const Disc_quote &d){Quote::operator=(d);quantity = d.quantity;discount = d.discount; cout << "Disc_quote::operator=" << endl;return *this;}Disc_quote& operator=(Disc_quote &&d){Quote(std::move(d));quantity = d.quantity;discount = d.discount;return *this;}//pure virtual functiondouble net_price(size_t)const = 0;~Disc_quote()override{cout << "~Disc_quote()" << endl;      }private:
size_t quantity = 0;
double discount = 0;};
class Bulk_quote : public Disc_quote //Bulk_quote derived from Quote
{
public:Bulk_quote(){cout << "Bulk_quote()" << endl;       }Bulk_quote(const string &,double,size_t ,double);//拷贝控制成员Bulk_quote(const Bulk_quote &b):Disc_quote(b){cout << "Bulk_quote(const Bulk_quote &)" << endl;}Bulk_quote(Bulk_quote && b):Disc_quote(std::move(b)){cout << "Bulk_quote(Bulk_quote&&)" << endl;}Bulk_quote& operator=(const Bulk_quote &b){Disc_quote::operator=(b);   cout <<"Bulk_quote(const Bulk_quote &)" << endl;return *this;}Bulk_quote& operator=(Bulk_quote &&q){Disc_quote::operator=(std::move(q));return *this;       }~Bulk_quote()override{cout << "~Bulk_quote()" << endl;}//覆盖基类的版本以实现大量购买的折扣和政策double net_price(size_t n)const override;   //override:显式地用这个成员函数改写基类的虚函数};double print_total(ostream &,const Quote &,size_t );
void call_fun(Quote );
#endif

15.30

//main.cc
#include <string>
#include <iostream>
#include "quote.h"
#include <vector>
#include <memory>using namespace std;
int main()
{return 0;
}
//functions.cc
#include <string>
#include <iostream>
#include <memory>
#include "quote.h"
using namespace  std;string Quote::isbn()const{return bookNo;
}double Quote::net_price(size_t n)const
{cout << "Quote::net_price(size_t)" << endl;return n * price;
}double print_total(ostream &os,const Quote &item,size_t n)    //override:显式地用这个成员函数改写基类的虚函数
{
//根据传入的item的形参的对象类型调用Quote::net_price 或者 Bulk_quote::net_price
//共有成员访问的时候必须用对象去访问。
double ret = item.net_price(n);
os << "ISBN: " << item.isbn() << "# sold: " << n << " total due: " << ret << endl;return ret;
}//destructor
Quote::~Quote(){cout << "Quote::~Quote()" << endl;}/*
Bulk_quote::Bulk_quote(const string &book,double p,size_t qty,double disk):Disc_quote(book,p,qty,disk)
{cout << "Bulk_quote(const string &,double ,size_t ,double)" << endl;
}
*/double Bulk_quote::net_price(size_t n)const
{cout << "Bulk_quote::net_price(size_t)" << endl;
return 1.1;
}//test
void call_fun(Quote q)
{cout << q.net_price(12) << endl;}//Basket类成员函数
double Basket::total_receipt(ostream &os)const
{
double sum = 0.0;
for(auto iter = items.cbegin();iter != items.end();iter = items.upper_bound(*iter)){sum += print_total(os,**iter,items.count(*iter));}
os << "Total sale: " <<sum <<endl;return sum;
}
//quote.h
#ifndef QUOTE_INCLUDE_H
#define QUOTE_INCLUDE_H#include <string>
#include <iostream>
#include <vector>
#include <memory>
#include <set>
using namespace std;class Bulk_quote;
class Quote{
public:Quote(){cout << "Quote()" << endl;};Quote(const string &book,double sales_price):bookNo(book),price(sales_price){cout << "Quote(const string &,double )" << endl;}Quote(const Quote & q):bookNo(q.bookNo),price(q.price){cout << "Quote(const Quote &)" << endl;}Quote(Quote &&q):bookNo(std::move(q.bookNo)),price(q.price){cout << "Quote(Quote &&)" << endl;}Quote& operator=(const Quote &q){bookNo = q.bookNo;price = q.price;cout << "Quote::operator=" << endl;return *this;        }Quote& operator=(Quote &&q){bookNo = std::move(q.bookNo);price = q.price;cout << "Quote::operator=(Quote&&)" << endl;return *this;       }string isbn()const;virtual double net_price(size_t n)const;friend double print_total(ostream &os,const Quote &item,size_t n);virtual ~Quote();virtual Quote* clone() const &{return new Quote(*this);  }virtual Quote* clone()&&{return new Quote(std::move(*this));}private:string bookNo;
protected:double price = 0.0;
};//abstract class
class Disc_quote: public Quote{public:Disc_quote(){cout << "Disc_quote()" << endl;}Disc_quote(const string &book,double price,size_t qty,double disc):Quote(book,price),quantity(qty),discount(disc){cout << "Disc_quote(const string&,double ,size_t ,double) " <<endl;}Disc_quote(const Disc_quote &q):Quote(q),quantity(q.quantity),discount(q.discount){cout << "Disc_quote(const Disc_quote &)" << endl;     }Disc_quote(Disc_quote &&q):Quote(std::move(q)),quantity(q.quantity),discount(q.discount){}Disc_quote & operator=(const Disc_quote &d){Quote::operator=(d);quantity = d.quantity;discount = d.discount; cout << "Disc_quote::operator=" << endl;return *this;}Disc_quote& operator=(Disc_quote &&d){Quote(std::move(d));quantity = d.quantity;discount = d.discount;return *this;}//pure virtual functiondouble net_price(size_t)const = 0;~Disc_quote()override{cout << "~Disc_quote()" << endl;      }private:
size_t quantity = 0;
double discount = 0;};
class Bulk_quote : public Disc_quote //Bulk_quote derived from Quote
{
public:Bulk_quote* clone()const & override{return new Bulk_quote(*this);        }Bulk_quote* clone()&& override{return new Bulk_quote(std::move(*this));}using Disc_quote::Disc_quote;Bulk_quote(){cout << "Bulk_quote()" << endl;        }//Bulk_quote(const string &,double,size_t ,double);//拷贝控制成员Bulk_quote(const Bulk_quote &b):Disc_quote(b){cout << "Bulk_quote(const Bulk_quote &)" << endl;}Bulk_quote(Bulk_quote && b):Disc_quote(std::move(b)){cout << "Bulk_quote(Bulk_quote&&)" << endl;}Bulk_quote& operator=(const Bulk_quote &b){Disc_quote::operator=(b); cout <<"Bulk_quote(const Bulk_quote &)" << endl;return *this;}Bulk_quote& operator=(Bulk_quote &&q){Disc_quote::operator=(std::move(q));return *this;       }~Bulk_quote()override{cout << "~Bulk_quote()" << endl;}//覆盖基类的版本以实现大量购买的折扣和政策double net_price(size_t n)const override;   //override:显式地用这个成员函数改写基类的虚函数};class Basket
{public:void add_item(const Quote & sale){items.insert(shared_ptr<Quote>(sale.clone()));  }void add_item(Quote && sale){items.insert(shared_ptr<Quote>((std::move(sale)).clone()));}//打印每本书的总价格和篮中所有书的总价double total_receipt(ostream &)const;private:static bool compare(const shared_ptr<Quote> &lhs,const shared_ptr<Quote> &rhs){return lhs->isbn() < rhs->isbn();}
// items定义成一个multiset,可以将同一本书的多条交易信息保存到一个multiset中,
// 这里不能用圆括号,因为default member initializer来初始化成员有2
// 个方法,list initialization,即一对花括号.第二种是copy initializa//tion,即 等号.如果尝试用圆括号,编译器会误以为是函数声明,所以/报告compare is not a type..multiset<shared_ptr<Quote>,decltype(compare)*> items{compare};
};double print_total(ostream &,const Quote &,size_t );
void call_fun(Quote );#endif

练习15.32

Query未定义自己的拷贝、移动、赋值和销毁成员,当进行这些操作时,执行默认的语义。而其唯一的数据成员是Query_base的shared_ptr,因此,当拷贝、移动、赋值、或者销毁一个Query对象时,会调用shared_ptr的对应控制成员,从而实现多个Query对象正确共享一个Query_base。而shared_ptr的控制成员调用Query_base的控制成员时,由于指向的可能是Query_base的派生类对象,因此可能是类层次中进行相应的拷贝、移动操作,调用Query_base的派生类的相应控制成员。

练习15.34

(a)Query(const string &),WordQuery(const string &),NotQuery(const Query &),AndQuery(const Query &,const Query &),OrQuery(const Query &,const Query &)。

(b)OrQuerry::rep()

(c)OrQuery::eval(const TextQuery &)

15.35

15.9节

先看一些代码的基础,下面是查询类的完整代码

//文件query.h#ifndef CLASS_H
#define CLASS_H#include <string>
#include <iostream>
#include <vector>
#include <set>
#include <memory>
#include <fstream>
#include <map>
#include <algorithm>using namespace std;class QueryResult;
class Query_base;
class BinaryQuery;
class AndQuery;
class OrQuery;
class NotQuery;
class Query;
class WordQuery;Query operator~(const Query &q);
Query operator&(const Query &,const Query &);
Query operator|(const Query &,const Query &);class TextQuery
{friend class Query;public:using line_no = vector<string>::size_type;TextQuery() = default;TextQuery(ifstream &is);QueryResult query(const string &s)const;size_t size()const{return file->size();}vector<string>::iterator begin()const{return file->begin();}vector<string>::iterator end()const{return file->end();}private:shared_ptr<vector<string>> file;shared_ptr<set<line_no>> lines;map<string,shared_ptr<set<line_no>>> wm;};
class QueryResult{friend void print(ostream &,const QueryResult &);public:using line_no = TextQuery::line_no;QueryResult(shared_ptr<vector<string>> f,shared_ptr<set<line_no>> l,const string &s):file(f),lines(l),query_word(s){}set<line_no>::iterator begin()const{return lines->begin();}set<line_no>::iterator end()const{return lines->end();}shared_ptr<vector<string>> get_file()const{return file;}private:shared_ptr<vector<string>> file;shared_ptr<set<line_no>> lines;string query_word;};class Query_base{friend class Query;protected:using line_no = TextQuery::line_no;virtual ~Query_base() = default;private:virtual QueryResult eval(const TextQuery &)const= 0;virtual string rep()const = 0;};class WordQuery:public Query_base{
friend class Query;WordQuery(const string &s):query_word(s){}QueryResult eval(const TextQuery &)const;
string rep()const;string query_word;
};inline
QueryResult WordQuery::eval(const TextQuery &t)const
{return t.query(query_word);
}inline
string WordQuery::rep()const
{return query_word;
}class Query{
friend Query operator~(const Query &q);
friend Query operator&(const Query &,const Query &);
friend Query operator|(const Query &,const Query &);public:QueryResult eval(const TextQuery &t)const{return query->eval(t);}string rep()const{return query->rep();}Query(const string &w):query(new WordQuery(w)){}private:Query(shared_ptr<Query_base> p):query(p){}shared_ptr<Query_base> query;};class BinaryQuery: public Query_base
{
protected:
BinaryQuery(const Query &l,const Query &r,const string &s):lhs(l),rhs(r),opSym(s){}Query lhs,rhs;
string opSym;string rep()const;
};inline
string BinaryQuery::rep()const{
return  "(" + lhs.rep() + opSym + rhs.rep()+")";
}class AndQuery: public BinaryQuery
{
friend Query operator&(const Query &,const Query &);
AndQuery(const Query &l,const Query &r):BinaryQuery(l,r,"&"){}
QueryResult eval(const TextQuery &)const;};inline
QueryResult AndQuery::eval(const TextQuery &t)const
{
//先利用Query的成员函数eval查询AndQuery的两个操作数的
//查询结果
QueryResult left = lhs.eval(t),right = rhs.eval(t);
shared_ptr<set<line_no>> ret = make_shared<set<line_no>>();
set_intersection(left.begin(),left.end(),right.begin(),right.end(),inserter(*ret,ret->begin()));return QueryResult(left.get_file(),ret,rep());}class OrQuery:public BinaryQuery
{friend Query operator|(const Query &,const Query &);OrQuery(const Query &l,const Query &r):BinaryQuery(l,r,"|"){}QueryResult eval(const TextQuery &)const;
};
inline
QueryResult OrQuery::eval(const TextQuery &t)const
{
auto left = lhs.eval(t),right = rhs.eval(t);
auto ret = make_shared<set<line_no>>(left.begin(),left.end());
ret->insert(right.begin(),right.end());return QueryResult(left.get_file(),ret,rep());}
inline
Query operator&(const Query &l,const Query &r)
{
return shared_ptr<Query_base>(new AndQuery(l,r));
}inline
Query operator|(const Query &l,const Query &r)
{
return shared_ptr<Query_base>(new OrQuery(l,r));}class NotQuery:public Query_base{
friend class Query;
friend Query operator~(const Query &);NotQuery(const Query &s):query(s){}QueryResult eval(const TextQuery &t)const;
string rep()const;
private:
Query query;
};
inline
QueryResult NotQuery::eval(const TextQuery &t)const
{
auto r = query.eval(t);
auto ret = make_shared<set<line_no>>(r.begin(),r.end());
auto beg = ret->begin();
auto end = ret->end();
auto result = make_shared<set<line_no>>();
for(int i = 0; i != r.get_file()->size();++ i){if(*beg != i && beg != end){result->insert(i);}elseif(beg != end){++ beg;             }}
return QueryResult(r.get_file(),result,rep());
}inline
string NotQuery::rep()const
{return "~(" + query.rep() + ")";
}inline
Query operator~(const Query &q)
{return shared_ptr<Query_base>(new NotQuery(q));
}#endif
//functions.cc#include <string>
#include "class.h"
#include <iostream>
#include <fstream>
#include <set>
#include <vector>
#include <memory>
#include <sstream>
#include <map>
#include "query.h"
using namespace std;
/* class TextQuery's member functions */
TextQuery::TextQuery(ifstream &is):file(new vector<string>()),lines(new set<line_no>())
{
string line;
string word;
size_t num = 0;
while(getline(is,line)){file->push_back(line);istringstream in(line);while(in >> word){auto &loc = wm[word];if(!loc){loc.reset(new set<line_no>());         }loc->insert(num);}++ num;}
}/* QueryResult 's member functions */QueryResult TextQuery::query(const string &s)const
{
//map的find成员,查找成功返回一个找到元素的迭代器,找不到返回尾后迭代器.
auto pos = wm.find(s);
if(pos == wm.end()){shared_ptr<set<line_no>> nodata(new set<line_no>());return QueryResult(file,nodata,s);}else{//慎重用map的下标操作,因为元素如果不存在会添加元素return QueryResult(file,pos->second,s);}}void print(ostream &os,const QueryResult &q)
{
cout << q.query_word << " occurs " << q.lines->size() << " times:" << endl;
for(auto i : *(q.lines)){cout << "\t(line " << i+1 << "):" << (*q.file)[i] << endl;  }}
#include <string>
#include <iostream>
#include "class.h"
#include <fstream>
#include <map>
#include <set>
#include "query.h"using namespace std;int main()
{//假设数据文件放在data.txt中
ifstream in("data.txt");
TextQuery t(in);
Query q = Query("good") | Query("you");
q.eval(t);
print(cout,q.eval(t));return 0;
}

练习15.34答案:

(a)执行的构造函数如下:

WordQuery(const string &)Query(const string &)WordQuery(const string &)Query(const string &)WordQuery(const string &)Query(const string &)AndQuery(const Query &,const Query &,const string &)BinaryQuery(const Query &,const Query &,const string  &)#下面这个构造函数形式参数类型不可以是const的Query(shard_ptr<Query_base> )OrQuery(const Query &,const Quyer &)BinaryQuery(const Query &,const Query &,const string &)

(b)

首先执行Query::rep()
然后直行OrQuery::rep()
...Query::rep()
...Query::rep()
...AndQuery::rep()
...Query::rep()
...WordQuery::rep()
...WordQuery::rep()
...WordQuery::rep()

(c)

Query::eval()
OrQuery::eval()
Query::eval()
Query::eval()
AndQuery::eval()
Query::eval()
Query::eval()
Query::eval()
WordQuery::eval()
WordQuery::eval()
WordQuery::eval()

15.37(这个题答案是自己想的,做了一下午,从下午1点做到5点30)

自己随便弄了个测试的读取单词的数据文件:

文件data.txt

you are  my good friend , and you are so good too .
i am glad to see you , if you do not mind
i will take some time to study from you .wahaha . sometimes i was so moody .
//main.cc #include <string>
#include <iostream>
#include "textquery.h"
#include "query.h"
#include <fstream>
using namespace std;
int main()
{ifstream in("data.txt");
TextQuery t(in);shared_ptr<Query_base> p = ~shared_ptr<Query_base>(new WordQuery("you"))|shared_ptr<Query_base>(new WordQuery("are"));print(cout,p->eval(t));print(cout,p) << endl;
return 0;
}
//textquery.cc#include <string>
#include <iostream>
#include <fstream>
#include <vector>
#include <map>
#include <set>
#include "textquery.h"
#include <memory>
#include <sstream>
using namespace std;
TextQuery::TextQuery(ifstream &in):file(new vector<string>())
{
string line;
//cnt 表示读入文件的行号
int cnt = 0;
while(getline(in,line)){//首先把行插入到文件file中file->push_back(line);istringstream in_s(line);string word;while(in_s >> word){//在wm中插入单词的行号//注意这里ret是引用类型auto &ret = wm[word];if(!ret){//为ret分配内存空间,这里无论有没有圆括号都是空集合初始化ret所指向的空间ret.reset(new set<line_no>());   }ret->insert(cnt);}  ++ cnt; //行号递增    }
}QueryResult TextQuery::query(const string &s)const
{
//不能用下标操作查询单词s是否存在,因为下标操作在单词不存在的
//情况下会往map中添加单词,正确的做法用find
auto  ret = wm.find(s); //find返回的是迭代器
if(ret == wm.end()){//这里必须新建一个set<line_no>对象,内容为空,不然当单词查不到时,对返回的QueryResult对象中的有关wm对象的一切操作均会报错,因为没有分配内存,是不能使用的,包括调用size()成员函数。//智能指针使用前必须要里面有内容,也就是分配了内存auto nodata = make_shared<set<line_no>>();return QueryResult(file,nodata,s);}//注意迭代器其实就是一个指针,用->访问迭代器所指的pair
else//ret->second 不能改成下标操作,也就是wm[s],原因我也不知道,大概是下标式子使用受限return QueryResult(file,ret->second,s);
}
//textquery.h#ifndef  TEXTQUERY_H
#define  TEXTQUERY_H
#include <string>
#include <iostream>
#include <fstream>
#include <vector>
#include <map>
#include <set>
#include <memory>
using namespace std;
class QueryResult;
class TextQuery{public:
using line_no = vector<string>::size_type;
TextQuery() = default;
TextQuery(ifstream &in);
QueryResult query(const string &s)const;private:
map<string,shared_ptr<set<line_no>>> wm;
shared_ptr<vector<string>> file;
};class QueryResult
{
friend ostream& print(ostream &,const QueryResult &);public:
using line_no = TextQuery::line_no;
QueryResult() = default;
QueryResult(shared_ptr<vector<string>> f,shared_ptr<set<line_no>> l,const string &w):file(f),lines(l),query_word(w){}
shared_ptr<vector<string>> get_file()const{return file;}
set<line_no>::iterator begin()const{return  lines->begin();}
set<line_no>::iteratorend()const{return lines->end();}
size_t size()const{return lines->size();}private:
//编译器对于智能指针只管择机释放,不管分配,所以使用前务必自己分配。
shared_ptr<set<line_no>> lines; //lines使用前必须手动分配空间
shared_ptr<vector<string>> file;//file 使用前必须手动分配空间
string query_word;};
inline
ostream& print(ostream & os,const QueryResult &q)
{
os << q.query_word << " occurs "<<q.lines->size() << " times:" << endl;
auto file = q.get_file();
for(auto i : *(q.lines)){os << "(line " << i+1 << ") " << (*file)[i] << endl;}return os;
}
#endif
//query.h#ifndef QUERY_H
#define QUERH_H#include <string>
#include <iostream>
#include <memory>#include <set>
#include <algorithm>
#include "textquery.h"
using namespace std;class Query_base{public:Query_base() = default;virtual QueryResult eval(const TextQuery &t)const=0;virtual string rep()const=0;virtual ~Query_base(){}protected:using line_no = TextQuery::line_no;
};
//没有覆盖eval,所以BinaryQuery还是一个抽象基类
class BinaryQuery : public Query_base
{
public:BinaryQuery(shared_ptr<Query_base> l,shared_ptr<Query_base> r,const string & ope):lhs(l),rhs(r),opSym(ope){}string rep()const override;protected:
shared_ptr<Query_base> lhs;  //使用前必须分配空间,或者是用另一个指针赋值
shared_ptr<Query_base> rhs;  //同上
private:
string opSym;
};
inline
string BinaryQuery::rep()const
{return "(" + lhs->rep() +" " +opSym + " "+rhs->rep() + ")";
}class WordQuery:public Query_base
{public:WordQuery(const string &s):query_word(s){}QueryResult eval(const TextQuery &t)const override{ return t.query(query_word);}string rep()const override{return query_word;}        private:string query_word;};class AndQuery:public BinaryQuery
{public:
//BinaryQuery()的各个实参必须都正确,如果写成lhs和rhs,那么使用时候就会失去实参,
//结果就是lhs和rhs没有得到空间然后就是用了,就会出现 segmentation fault,core dumped之类的错误。
AndQuery(shared_ptr<Query_base> l,shared_ptr<Query_base> r):BinaryQuery(l,r,"&"){}
QueryResult eval(const TextQuery &)const override;};inline
QueryResult AndQuery::eval(const TextQuery &t)const
{
auto l = lhs->eval(t);
auto r = rhs->eval(t);
auto file = l.get_file();
auto new_set = make_shared<set<line_no>>();
//set_intersection算法:intersection意思是 交集 ,所以set_intersection是求交集
set_intersection(l.begin(),l.end(),r.begin(),r.end(),inserter(*new_set,new_set->begin()));return QueryResult(file,new_set,rep());
}class OrQuery:public BinaryQuery
{public:OrQuery(shared_ptr<Query_base> l,shared_ptr<Query_base> r):BinaryQuery(l,r,"|"){}QueryResult eval(const TextQuery &)const;string rep()const;
};
inline
QueryResult OrQuery::eval(const TextQuery &t)const
{
auto r1 = lhs->eval(t);
auto r2 = rhs->eval(t);
auto file = r1.get_file();
auto new_set = make_shared<set<line_no>>(r1.begin(),r1.end());
new_set->insert(r2.begin(),r2.end());
return QueryResult(file,new_set,rep());
}string OrQuery::rep()const
{return "(" + lhs->rep() +" | "+ rhs->rep() + ")";
}class NotQuery:public Query_base
{public:
NotQuery(shared_ptr<Query_base> s):query(s){}
QueryResult eval(const TextQuery &)const;
string rep()const;private:
shared_ptr<Query_base> query;
};inline
string NotQuery::rep()const
{return "~(" +  query->rep() + ")";
}inline
QueryResult NotQuery::eval(const TextQuery &t)const
{
auto result = query->eval(t);
auto file = result.get_file();
auto old_set = make_shared<set<line_no>>(result.begin(),result.end());
auto beg = old_set->begin();
auto end = old_set->end();
auto new_set = make_shared<set<line_no>>();
//下面是求一个子集的补集,并且全集和补集是按照同样的方式排序时候的一个算法,我自己称它为:
//“求补集算法”,很好用的一个算法
for(int i = 0;i != file->size(); ++i){if(beg != end && i != *beg){new_set->insert(i);}else{if(beg != end)++ beg;     }}
return QueryResult(file,new_set,rep());
}inline
shared_ptr<Query_base>operator&(shared_ptr<Query_base> l,shared_ptr<Query_base> r)
{return shared_ptr<Query_base>(new AndQuery(l,r));
}inline
shared_ptr<Query_base>operator|(shared_ptr<Query_base> l,shared_ptr<Query_base> r)
{return shared_ptr<Query_base>(new OrQuery(l,r));
}inline
shared_ptr<Query_base>    operator~(shared_ptr<Query_base> l)
{return shared_ptr<Query_base>(new NotQuery(l));
}inline
ostream & print(ostream &os,shared_ptr<Query_base> p)
{return os << p->rep();
}#endif

运行结果是:

(~(you) | are) occurs 1 times:
(line 1) you are  my good friend , and you are so good too .
(~(you) | are)

成功~~~

回顾:

如果运行后发现如下结果:

segmentation fault,core dumped.

那么对于本题的程序,很大程度上可能是指针没有智能没有分配空间就使用了,然后就会发生这种问题。

然后,本程序有以下几个地方需要去检查:

程序中已经做提示了

15.38  答:

(a)不合法,& 运算符返回的类型是 Query 类型,不能转换为BinaryQuery类型。而且BinaryQuery是抽象基类,不能定义自己类型对象。

(b)不合法,& 运算符返回的类型是 Query 类型,不能转换为BinaryQuery类型。

(c)不合法,道理同(b)

提醒:要解决这个问题,必须收先自己想想,如果类有动态内存,而类的成员也有动态内存的时候,析构函数应该怎么写?

//textquery.cc
#include <string>
#include <iostream>
#include <fstream>
#include <vector>
#include <map>
#include <set>
#include "textquery.h"
#include <memory>
#include <sstream>
using namespace std;
TextQuery::TextQuery(ifstream &in):file(new vector<string>())
{
string line;
//cnt 表示读入文件的行号
int cnt = 0;
while(getline(in,line)){//首先把行插入到文件file中file->push_back(line);istringstream in_s(line);string word;while(in_s >> word){//在wm中插入单词的行号//注意这里ret是引用类型auto &ret = wm[word];if(!ret){//这里无论有没有括号都是空集合ret.reset(new set<line_no>());  }ret->insert(cnt);}  ++ cnt; //行号递增    }
}QueryResult TextQuery::query(const string &s)const
{
//不能用下标操作查询单词s是否存在,因为下标操作在单词不存在的
//情况下会往map中添加单词,正确的做法用find
auto  ret = wm.find(s); //find返回的是迭代器
if(ret == wm.end()){//这里必须新建一个set<line_no>对象,内容为空,不然//对此时返回的QueryResult对象中的有关wm对象的一切操作均会报错,因为没有分配内存,是不能使用的。//智能指针使用前必须要里面有内容,也就是分配了内存auto nodata = make_shared<set<line_no>>();return QueryResult(file,nodata,s);}//注意迭代器其实就是一个指针,用->访问迭代器所指的pair
elsereturn QueryResult(file,ret->second,s);
}

query.cc
//query.h中的函数都定义为内联函数了,这里空了
#include <string>
#include <iostream>
#include <memory>
#include <vector>
#include <map>
#include <set>
#include "query.h"
using namespace std;
//main.cc
#include <string>
#include <iostream>
#include "query.h"
#include "textquery.h"
#include <fstream>
#include <set>
#include <memory>
using namespace std;int main()
{
ifstream in("data.txt");
TextQuery t(in);
Query q = Query("my") & Query("good") | Query("you");
cout << q.rep() << endl;
QueryResult ret = q.eval(t);
print(cout,ret);return 0;
}
//textquery.h
#ifndef  TEXTQUERY_H
#define  TEXTQUERY_H#include <string>
#include <iostream>
#include <fstream>
#include <vector>
#include <map>
#include <set>
#include <memory>
using namespace std;
class QueryResult;
class TextQuery{public:
using line_no = vector<string>::size_type;TextQuery() = default;
TextQuery(ifstream &in);
QueryResult query(const string &s)const;private:map<string,shared_ptr<set<line_no>>> wm;
shared_ptr<vector<string>> file;
};class QueryResult
{
friend ostream& print(ostream &,const QueryResult &);public:
using line_no = TextQuery::line_no;
QueryResult() = default;
QueryResult(shared_ptr<vector<string>> f,shared_ptr<set<line_no>> l,const string &w):file(f),lines(l),query_word(w){}
shared_ptr<vector<string>> get_file()const{return file;}
set<line_no>::iterator begin()const{return  lines->begin();}
set<line_no>::iteratorend()const{return lines->end();}
size_t size()const{return lines->size();}private:
shared_ptr<set<line_no>> lines;
shared_ptr<vector<string>> file;
string query_word;};
inline
ostream& print(ostream & os,const QueryResult &q)
{
os << q.query_word << " occurs "<<q.lines->size() << " times:" << endl;
auto file = q.get_file();
for(auto i : *(q.lines)){os << "(line " << i+1 << ") " << (*file)[i] << endl;}return os;
}#endif
//query.h/************************************************ 1.constructor may not be cv-qualified(构造函数不能是 const的),道理很简单,构造函数会改变对象的值* 2.constructor may not be ref-qualified(构造函数不可以加& 或者 &&,赋值运算符却可以)*    //道理也简单,构造函数是定义对象时候用,定义的对象当然不分左值和右值,也就是在定义对象之前*    //对象还不存在,也就是说定义对象之前根本没东西,当然就不能分左值和右值了。*    //其它函数是建立在有对象的基础上使用的,当然这个对象就可以是左值或者右值了。*    //一句话:“只有成员函数或者赋值运算符才可以是& 或者 &&的”,构造函数不在此,构造函数不在此列列* 3.构造函数不能分左值和右值版本,赋值运算符在本程序中不要分左值和右值版本,只要给参数类型分*    左值版本和右值版本就可以了,也就是说遇到左值/右值类型的参数(具体赋值过程中是右操作数),调用对应版本即可,左操作数不需要定义左右版本* 4.构造函数和赋值运算符都不可以定义成const的函数,因为它们都需要改变对象本身* 5.拷贝赋值运算符:首先需要把右侧值存起来,存的时候就存必要的成员还是整体全部*              其次需要释放左侧资源,调用左侧类型的的析构函数就可以了吧,还是要把左侧的基类部分              和非基类部分分开释放。*           最后给左侧赋值。**              暂时结论:析构的时候就析构左侧部分就可以。析构函数会自动递归调用基类的析构函数*             释放基类部分,但是赋值或者拷贝的时候必须要首先拷贝基类部分或者给基类部分赋值。*             ** 6.析构函数的安排* 7.在类的对象的析构中,必须手动释放类的资源,对于普通的内置类型的值,不用做任何事情。* 8.类值版本的赋值运算符定义的时候,左侧对象的析构能否直接调用左侧对象的析构函数?还是必须只手动析构动态内存成员就可以,但是如果普通成员是类类型,也包含动态内存成员又该怎么办呢?此时是调用左侧对象的析构函数呢?还是只释放动态内存成员* 9.赋值运算符能不能这样工作,直接调用拷贝构造函数,新建个对象把右侧对象放进来,然后再释放左侧对象(能不能直接调用左侧对象的西沟函数),然后再把左侧值赋予右侧的值。* 10.可能有点眉目了,如果在赋值运算符中左侧对象调用对象本身析构函数,可能会导致,赋值之前左侧对象就失效了...为了防止这类事情发生,只能是逐个成员地去释放即可.* 11.所有的纯虚函数必须都要有定义,因为运行时候才能知道调用哪个版本?如果不给虚函数定义,那么申明一个纯虚函数。否则直接继承父类的虚函数。* ***********************************************///友元,构造函数,拷贝构造成员,(虚函数覆盖)其它成员函数,析构函数,数据成员
#ifndef QUERY_H
#define QUERY_H#include <string>
#include <iostream>
#include <memory>
#include <vector>
#include <map>
#include <set>
#include "textquery.h"
#include <algorithm>using namespace std;class Query_base;class WordQuery;
class Binary_query;class NotQuery;
class AndQuery;
class OrQuery;
class Query;/*类值版本的*/
class Query_base
{
friend class Query;
protected:using line_no = TextQuery::line_no;//共有5个虚函数(成员函数):eval,rep,2 clones,~Query_base
virtual QueryResult eval(const TextQuery &)const = 0;
virtual string rep()const = 0;
virtual Query_base *clone() && = 0;
virtual Query_base *clone()const & = 0;protected:
virtual ~Query_base()=default;    //析构函数可不可以protected?};class WordQuery :public Query_base{friend class Query;private:WordQuery(const string &s):query_word(s){}protected:WordQuery(const WordQuery &);  //constructors may not be ref-qualified
WordQuery(WordQuery && );          //constructors may not be ref-qualified
WordQuery & operator=(const WordQuery &);
WordQuery & operator=(WordQuery && );//覆盖虚函数
QueryResult eval(const TextQuery &t) const override {return t.query(query_word);}
string rep()const override {return query_word;}
WordQuery * clone()&& override;
WordQuery * clone()const& override;~WordQuery();private:
string query_word;
};//拷贝控制成员
inline
WordQuery::WordQuery(const WordQuery &w):query_word(w.query_word){}
inline
WordQuery::WordQuery(WordQuery && w):query_word(std::move(w.query_word)){}  inline
WordQuery & WordQuery::operator=(const WordQuery &w)
{Query_base::operator=(w);//只是个形式,为了说明语法形式,实际不起作用query_word = w.query_word;return *this;
}
inline
WordQuery & WordQuery::operator=(WordQuery && w)
{Query_base::operator=(std::move(w));    //可不可以这样使用?光窃取基类部分,不会破坏基类部分外的部分吗?query_word = std::move(w.query_word);return *this;
}inline
WordQuery * WordQuery::clone()const&
{return new WordQuery(*this);//克隆就是把自己复制一次
}
inline
WordQuery * WordQuery::clone()&&
{return new WordQuery(std::move(*this)); //复制自己,调用自己的拷贝构造函数
}//WordQuery类型析构的时候,只要析构在内存中自己本身即可。
inline
WordQuery::~WordQuery(){}class Query
{public:
friend Query operator&(const Query &,const Query &);
friend Query operator|(const Query &,const Query &);
friend Query operator~(const Query &);//构造函数public:
Query(const string &s); //constructors may not be cv-qualified (构造函数用限定符是不合法的)
//限定符就是const和volatile//拷贝控制成员
//note:constructors may not be ref-qualified(构造函数不能加引用限定符)
Query(const Query &);
Query(Query &&);
Query & operator=(const Query &);
Query & operator=(Query &&);QueryResult eval(const TextQuery &)const;
string rep()const;
//clone函数/*开始Query类自己定义了下面两个函数,后来发现是多余的,Query的clone函数只是他自己用与克隆自己的query部分,完全可以直接调用自己的query的clone就可以。此时如果clone是各个类的私有成员函数,只需要Query是各个类的友元即可
Query_base* clone()const &;
Query_base* clone()&&;
*///析构函数
~Query();private:
Query_base * query;
//请确定这里用不用重新分配内存
Query(Query_base * q):query(q){}   //主要是三个函数使用,不能是explicit的
};//构造函数
inline
Query::Query(const string &s):query(new WordQuery(s)){}//拷贝控制成员
inline
Query::Query(const Query &s):query(s.query->clone()){} //拷贝构造函数inline
Query::Query(Query &&s):query(s.query->clone()){s.query == nullptr;}  //移动构造函数函数,调用右值版本的cloneinline
Query & Query::operator=(const Query &s)
{//必须能正确处理自赋值的情况auto new_data = s.query->clone();  //新开辟了内存,现在new_data中有了s中的内容//开始自己是这样写的,后来发现不妥this->~Query(); ,改成下面的delete query;query = new_data;return *this;
}
inline
Query & Query::operator=(Query && s)
{auto new_data = s.query->clone();  //为了正确处理自赋值情况,先把右侧值暂时存起来//这么写会破坏整个对下你给this->~Query();delete query;query = new_data;           //最后给左侧对象赋值return *this;}
inline
Query::~Query(){//利用派生类对象的指针调用基类的虚函数,就会发生动态绑定delete query;    //虚调用:利用基类指针调用虚析构函数}inline
QueryResult Query::eval(const TextQuery &t)const
{return query->eval(t);
}
inline
string Query::rep()const
{return query->rep();
}class BinaryQuery:public Query_base{friend class Query;protected:BinaryQuery(const Query &l,const Query &r,const string & o):lhs(l),rhs(r),opSym(o){}BinaryQuery(const BinaryQuery &);BinaryQuery(BinaryQuery &&);  //constructors may not be ref-BinaryQuery&operator=(const BinaryQuery &);BinaryQuery&operator=(BinaryQuery &&);string rep()const;~BinaryQuery();Query lhs,rhs;private:string opSym;};inline
BinaryQuery::BinaryQuery(const BinaryQuery &b):lhs(b.lhs),rhs(b.rhs),opSym(b.opSym){}
inline
BinaryQuery::BinaryQuery(BinaryQuery &&b):lhs(std::move(b.lhs)),rhs(std::move(b.rhs)),opSym(std::move(b.opSym)){}inline
BinaryQuery& BinaryQuery::operator=(const BinaryQuery &b)
{
//这里不用提前释放lhs和rhs,因为lhs和rhs有自己的拷贝赋值运算符。赋值时候会调用它们的拷贝赋值运算符
//先释放左边对象,然后再赋值
Query_base::operator=(b);    //调用直接基类的拷贝赋值运算符,给直接基类部分赋值
lhs = b.lhs;   //会调用lhs和rhs的拷贝赋值运算符,进行赋值
rhs = b.rhs;
opSym = b.opSym;return *this;
}
inline
BinaryQuery& BinaryQuery::operator=(BinaryQuery &&b)
{
//基类部分该怎么表示Query_base::operator=(std::move(b)); lhs = std::move(b.lhs);rhs = std::move(b.rhs);opSym = std::move(b.opSym);return *this;
}inline
BinaryQuery::~BinaryQuery(){} //没有本身动态内存,它也没有动态分配的成员指针inline
string  BinaryQuery::rep()const
{return "(" + lhs.rep() + opSym + rhs.rep() + ")";
}class AndQuery:public BinaryQuery{friend class Query;friend Query operator&(const Query &,const Query&);public:AndQuery(const AndQuery &);AndQuery(AndQuery &&);AndQuery& operator=(const AndQuery &);AndQuery& operator=(AndQuery&&);private:AndQuery(const Query &l,const Query &r):BinaryQuery(l,r,"&"){}QueryResult eval(const TextQuery &t)const override;AndQuery* clone()const & override;AndQuery* clone()&& override;protected:~AndQuery(){ }; //如果少了这句,会怎么样?};inline
AndQuery::AndQuery(const AndQuery &a):BinaryQuery(a) {}inline
AndQuery::AndQuery(AndQuery &&a):BinaryQuery(std::move(a)){}inline
AndQuery& AndQuery::operator=(const AndQuery &a)
{//赋值和构造函数过程必须给直接基类赋值或者构造,每个构造函数必须负责直接基类部分的构造BinaryQuery::operator=(a);//调用基类的拷贝赋值运算符给基类部分赋值return *this;
}
inline
AndQuery& AndQuery::operator=(AndQuery && a)
{BinaryQuery::operator=(std::move(a));  //调用基类部分的移动赋值运算符移动基类部分//AndQUery 没有自己的数据成员,这里为空return *this;
}inline
QueryResult AndQuery::eval(const TextQuery &t)const
{
auto left = lhs.eval(t),right = rhs.eval(t);
auto new_file = left.get_file();
auto new_set = make_shared<set<line_no>>();
set_intersection(left.begin(),left.end(),right.begin(),right.end(),inserter(*new_set,new_set->begin()));return QueryResult(new_file,new_set,rep());
}inline
AndQuery * AndQuery::clone()const &
{return new AndQuery(*this); //复制自己,调用自己的拷贝构造函数
}inline
AndQuery * AndQuery::clone()&&
{return new AndQuery(std::move(*this));  //复制自己,调用自己的拷贝构造函数
}class OrQuery:public BinaryQuery{friend class Query;
friend Query operator|(const Query &,const Query &);OrQuery(const Query &l,const Query &r):BinaryQuery(l,r,"|"){}
QueryResult eval(const TextQuery &)const;
OrQuery * clone() const &;
OrQuery * clone()&&;public:OrQuery(const OrQuery &);   //constructors may not be cv-qualified(不能加限定符const)
OrQuery(OrQuery &&);
OrQuery& operator=(const OrQuery &);
OrQuery& operator=(OrQuery &&);protected:
~OrQuery(){}  //不希望普通用户使用这个函数};//OrQuery没有自己的数据成员,只要拷贝直接基类部分即可
inline
OrQuery::OrQuery(const OrQuery &o):BinaryQuery(o) {}
inline
OrQuery::OrQuery(OrQuery && o):BinaryQuery(std::move(o)) {}
inline
OrQuery& OrQuery::operator=(const OrQuery &o )
{BinaryQuery::operator=(o);return *this;
}
inline
OrQuery& OrQuery::operator=(OrQuery && o)
{BinaryQuery::operator=(std::move(o));return *this;
}inline
QueryResult OrQuery::eval(const TextQuery &t)const
{
auto left = rhs.eval(t),right = rhs.eval(t);
auto l = left.get_file();
auto r = right.get_file();
auto new_set = make_shared<set<line_no>>(); //这里不加圆括号会报错的
set_intersection(left.begin(),left.end(),right.begin(),right.end(),inserter(*new_set,new_set->begin()));return QueryResult(l,new_set,rep());
}
inline
OrQuery * OrQuery::clone()const&
{return new OrQuery(*this);
}
inline
OrQuery * OrQuery::clone()&&
{return new OrQuery(std::move(*this));
}class NotQuery:public Query_base{friend class Query;friend Query operator~(const Query &);private:
Query query;  //唯一的一个数据成员
NotQuery(const Query &q):query(q){}NotQuery(const NotQuery &);
NotQuery(NotQuery &&);
//copy constructor and copy assignment operator may not be const
//拷贝构造函数和拷贝赋值运算符都不能是const的,因为要改变对象的成员
NotQuery& operator=(const NotQuery &);
NotQuery& operator=(NotQuery &&);protected:
//虚函数在这个类中是什么角色?取决于直接基类和间接基类的定义
//虚函数如果没有定义自己版本,会继承最近的那个基类的版本
//string rep()const;直接基类已经定义了一个可用的版本,这里会直接继承
//rep()会先继承最亲的那个基类的代码
QueryResult eval(const TextQuery &)const override;
string rep()const override{return "~(" + query.rep() + ")";}
NotQuery * clone()const & override;
NotQuery * clone()&& override;
~NotQuery();};
inline
//必须慎重
NotQuery::NotQuery(const NotQuery &n):query(n.query){}
inline
NotQuery::NotQuery(NotQuery &&n):query(std::move(n.query)){}inline
NotQuery& NotQuery::operator=(const NotQuery &n)
{Query_base::operator=(n);query = n.query;return *this;
}
inline
NotQuery& NotQuery::operator=(NotQuery && n)
{Query_base::operator=(std::move(n));query = std::move(n.query);return *this;
}inline
QueryResult NotQuery::eval(const TextQuery &t)const
{
auto ret = query.eval(t);
auto file = ret.get_file();
auto new_set = make_shared<set<line_no>>();
auto n = file->size();   //file是一个vector的shared_ptr对象
auto iter = ret.begin();
for(int i = 0; i != n; ++i){if(i != *iter || iter == ret.end())new_set->insert(i);else{if(iter != ret.end())++ iter;}}
return QueryResult(file,new_set,rep());
}inline
NotQuery * NotQuery::clone()const &
{return new NotQuery(*this);
}inline
NotQuery * NotQuery::clone()&&
{return new NotQuery(std::move(*this));
}inline
NotQuery::~NotQuery(){}//没有自己的动态成员inline
Query operator&(const Query &l,const Query &r)
{return new AndQuery(l,r);
}
inline
Query operator|(const Query &l,const Query &r)
{return new OrQuery(l,r);
}inline
Query operator~(const Query& q)
{return new NotQuery(q);
}
#endif

文件data.txt是外部读取单词的,自己随意编写的,内容如下:

i am a good boy and you are a good boy too
if you want to improve your c++ programming language you should write codes everyday
best wishes

在g++中执行代码

g++ main.cc query.cc textquery.cc -o 123

执行文件 123

./123

结果如下:

((my&good)|you)
((my&good)|you) occurs 3 times:
(line 1) i am a good boy and you are a good boy too
(line 2) if you want to improve your c++ programming language
(line 4) you should write codes everyday

未完待续。。。

补充一点知识:读者自己看吧,这点知识我自己作为初学者体会的,只可意会,不可言传。

#include <string>
#include <iostream>
using namespace std;
class A{public:A() = default;virtual void print()const{cout << "A::print()" << endl;}int a = 111;
int b = 11;};
class B : public A
{public:B()  = default;void print()const override{cout << "B::print()" << endl;}void f()const{cout << "B::f()" << endl;}private:int c= 222;int d = 22;};int main()
{
B b;
A * pa = &b;
pa->print();
A & ra = *pa;
ra.print();
A * pb = &ra;
pb->print();
pb->f();return 0;
}

运行结果:

1.cc: In function ‘int main()’:
1.cc:48:5: error: ‘class A’ has no member named ‘f’48 | pb->f();|     ^

用我自己的语言来说,如果一旦把一个派生类对象放在了基类的引用或者指针上,那么这个对象再也不能通过后续的(直接赋值或者把指针解引用赋值,不能用new或者make_shared重新分配内存)操作赋值给派生类对象了。而且,无论这个对象接下来通过多少次的解引用或者直接赋值,只要都是经过指针或者引用传递,那么它终究还是一个动态类型是派生类行的对象,而且静态类型始终都是A类型。

要注意下面2个结论:

1.除了动态绑定之外,那么其它部分都是按照静态类型来解析的,比如说“不存在基类向派生类的类型转换。”

2.如果用new或者make_shared分配内存。那么无论这个对象的静态类型和动态类型是否一致,那么始终结果都是按照静态类型来分配的。也就是除了动态绑定的事实,其余都是按照以前静态类型来操作的。也试试说,调用new(或者make_shared)分配新的内存,静态类型和动态类型不一致的对象被破坏。new分配的结果的对象中,动态类型和静态类型是一致的(结果都是静态对象类型)。

3.再进一步,只要一直是用引用、指针、解引用+指针(把一个静态类型和动态类型不一致的类型先解引用再绑定到基类的指针上),那么“动态绑定”的事实就可以一直持续下去。(千万不要中途去new或者make_shared)

4.关于更多相关内容,参见如下百度百科链接

dynamic_cast_百度百科

把上面这个程序作如下改动,可以发现一个更有趣的事实:

#include <string>
#include <iostream>
#include <memory>
using namespace std;
class A{public:A() = default;virtual void print()const{cout << "A::print()" << endl;}int a = 111;
int b = 11;};
class B : public A
{public:B()  = default;void print()const override{cout << "B::print()" << endl;}void f()const{cout << "B::f()" << endl;}private:int c= 222;int d = 22;};int main()
{
B b;
A * pa = &b;
pa->print();
A & ra = *pa;
ra.print();
A * pb = &ra;
pb->print();
//pb->f(); //这句代码是错误的,发生动态绑定的时候,只能调用虚函数,不能调用普通成员。
A * pc = new A(*pa); //调用new分配内存,动态类型对象被破坏。new分配的结果对象pc中,动态类型和静态类型是一致的。
//下面一句错误更厉害,g++编译器会直接报如下错误:
//no matching function for call to 'B::B(&A)',也就是B类中不存在参数类型是A&的构造函数
A * pd = new B(*pa);
pc -> print();
shared_ptr<A> pd = make_shared<A>(*pa);
pd->print();return 0;
}

g++编译结果如下:

1.cc: In function ‘int main()’:
1.cc:51:19: error: no matching function for call to ‘B::B(A&)’51 | A * pd = new B(*pa);|                   ^
1.cc:21:3: note: candidate: ‘constexpr B::B()’21 |   B()  = default;|   ^
1.cc:21:3: note:   candidate expects 0 arguments, 1 provided
1.cc:18:7: note: candidate: ‘constexpr B::B(const B&)’18 | class B : public A|       ^
1.cc:18:7: note:   no known conversion for argument 1 from ‘A’ to ‘const B&’
1.cc:18:7: note: candidate: ‘constexpr B::B(B&&)’
1.cc:18:7: note:   no known conversion for argument 1 from ‘A’ to ‘B&&’
1.cc:53:15: error: conflicting declaration ‘std::shared_ptr<A> pd’53 | shared_ptr<A> pd = make_shared<A>(*pa);|               ^~
1.cc:51:5: note: previous declaration as ‘A* pd’51 | A * pd = new B(*pa);|     ^~

关于dynamic的一个例子,如下:

B * pc = dynamic_cast<B*>(pb);
pc -> print();
pc->f();B &rb = dynamic_cast<B&>(ra);
rb.print();
rb.f();

c++primer 5th第15章基础、课后习题自己解析、心得体会等相关推荐

  1. C++ Primer - 5th Edition - 书中源代码 - 课后习题答案

    C++ Primer - 5th Edition - 书中源代码 - 课后习题答案 C++ Primer - 5th Edition - 书中源代码 - 课后习题答案 1. C++ Primer, 5 ...

  2. 机械祭天法力无边:C++primer学习(第一章及课后习题)

    练习1.1: 查阅你使用的编译器的文档,确定它所使用的文件命名约定,编译并运行第2页的main程序. 命令为: C:\Users\dell>cl /EHsc SMA.cpp 我的电脑运行结果如下 ...

  3. 《C++ Primer》第15章 15.4节习题答案

    <C++ Primer>第15章 面向对象程序设计 15.4节 抽象基类 习题答案 练习15.15:定义你自己的Disc_quote和Bulk_quote. [出题思路]本题练习实现不同折 ...

  4. 《C++ Primer》第15章 15.2节习题答案

    <C++ Primer>第15章 面向对象程序设计 本章介绍了面向对象程序设计的两个重要概念:继承和动态绑定,包括: □●继承.基类.派生类的基本概念. □●虚函数和虚基类. □●继承中的 ...

  5. 计算机科学基础第二版答案,浙江大学计算机科学基础课后习题参考答案new-PXY.doc...

    浙江大学计算机科学基础课后习题参考答案new-PXY 第一章课后习题参考答案 一.填空题 处理.处理 黑盒.程序 输入设备.运算器.存储器.控制器.输出设备 运算器.控制器.中央处理器 存储器.数据 ...

  6. 计算机网路基础课后习题答案 主编刘建友

    计算机网路基础课后习题答案 第一章 计算机网络概述 一.填空题 二.单项选择题 第二章 物理层 一.填空题 二.单项选择题 三.简答题 第三章 数据链路层 一.填空题 二.单项选择题 三.简答题 第四 ...

  7. Python编辑基础课后习题(持续更新)

    学习Python编程基础及应用时第2章~第4章部分课后习题的代码整理出来,每题尽量用的本章节内容. 文章目录 第二章 变量及简单的数据类型 第三章 语法初步 第四章 列表 第二章 变量及简单的数据类型 ...

  8. 大学物理学(第5版)下(第14章)课后习题答案

    大学物理学(第5版)下 第9章 静电场 课后习题答案    第10章 稳恒磁场   课后习题答案      第11章 变化的电磁场   课后习题答案  第12章 光的干涉   课后习题答案  第13章 ...

  9. 华师计算机应用基础知识,华师计算机应用基础课后习题答案.doc

    华师计算机应用基础课后习题答案 华师计算机应用基础课后习题答案 华师作业答案 2011年<计算机基础>期末考试复习题 第一章 计算机基础知识 选择题: 1. 目前使用最广泛.发展最快的计算 ...

最新文章

  1. IDC报告:谁是桌面虚拟化的王者
  2. python语法速成方法_30分钟学完Python基础语法
  3. 机器学习中的lazy method与eager method的比较
  4. FixedSizeList的使用
  5. 稳扎稳打Silverlight(8) - 2.0图形之基类System.Windows.Shapes.Shape
  6. 如何正确使用工业级交换机?
  7. 查看已有设置_腾讯企点呼叫中心如何查看企点电话产生的数据报表?
  8. python编程入门指南-Python入门学习指南
  9. java date只保留年月日_入门之JAVA爬虫
  10. 豆瓣9.3的高分! 牛逼的Git !!!
  11. Django 项目试炼blog(5) -- 个人站点的搭建
  12. keyshot卡住了还能保存吗_Sketchup建模和渲染能取代3dsMax吗?
  13. N54L文件服务器,N54L安装群晖需要修改的硬件设置
  14. python数据分析基础阮敬源码_python数据分析基础 阮敬pdf|保靖制作项目投资实施细则...
  15. 当当网上书店购物车——JS源码
  16. 如何在Linux下安装和启动Dragonfly
  17. 魔兽 怎么查服务器在线人数,网易魔兽世界人口普查查看
  18. 都市白领要学会的规则
  19. 真正的Java学习从入门到精通
  20. 苹果App Store 应用商店页面的优化技巧

热门文章

  1. 网络字节序与主机字节序的转换[转]
  2. 美国纽约的一个摄像头!刷新即现奇迹!
  3. 打印三角形、已三角形方式 输出数组中的内容
  4. 程序人生:无他,唯心向尔
  5. HarmonyOS之深入解析图像的位图操作和属性解码
  6. iOS开发之解析XML数据
  7. 2019第十届蓝桥杯C/C++ B组省赛 —— 第二题:年号字串
  8. Common Subsequence
  9. 【Qt】创建线程程序示例
  10. 【STM32】FreeRTOS任务挂起和恢复API