C++Primer第五版 第十五章习题答案(21~30)

来源:互联网 发布:软件开发代码 编辑:程序博客网 时间:2024/06/05 12:48

21:发挥自己的想象力吧!

#ifndef FLOWER_H#define FLOWER_H#include <string>using namespace std;class Flower//花店花朵{public:Flower();//默认构造函数Flower(const string& s,double sale_pice):name(s),Price(sale_pice){}//构造函数virtual int net_price() const = 0;//纯虚函数,本身无意义protected:double Price;//价格private:string name;};class Rose:public Flower//普通玫瑰{public:Rose();//Rose(const string &SR,double n,size_t m):Flower(SR,n),quantity(m){}int net_price() const{return quantity*Price;}protected:size_t quantity;//售量};class Discount_Rose:public Rose//打折玫瑰{public:Discount_Rose();//Discount_Rose(const string &SR,double n,size_t m,double disc):Rose(SR,n,m),discount(disc){}int net_price() const{return quantity*Price*(1-discount);}protected:double discount;//折扣};#endif FLOWER_H


22:注意几个地方:继承方式、虚函数、保护成员,见21题


23:知识点1:当类之间存在继承关系时,派生类的作用域嵌套在其基类的作用域之内,如果一个名字在派生类作用域中找不到定义,则编译器会继续在外层基类中寻找

知识点2:派生类可以重用定义在其直接基类或者间接基类中的名字,此时定义在内层作用域的名字将隐藏外层作用于中的名字,但是我们可以使用作用域运算符来访问使用隐藏的基类成员

知识点3:除了覆盖虚函数,派生类最好不要重用其他基类中的名字

知识点4:函数的调用及解析过程:

1:确定其静态类型,必然是一个类类型

2:在其静态类型的类中查找该成员,找不到,则向外层的基类移动,再没有,编译器报错

3:找到了该成员,进行常规的类型检查,编译器再根据其是否是虚函数产生不同的代码

若是虚函数且为引用或者指针类型的调用,则需要进行动态绑定,编译器产生的运行代码在运行时将决定到底运行该虚函数的哪个版本,依据其动态类型

若不是虚函数或是没有指针引用调用,则产生常规调用

知识点5:内层作用域中的函数不会重载外层作用域中的函数,所以派生类成员若有名字相同,即使其形参列表不一致,基类成员也会被隐藏掉—名字查找优先于类型检查

#include <vector>#include <iostream>#include <algorithm>// #include "Quote.h"// #include "Disc_quote.h"// #include "Base.h"using namespace std;struct base{void Foo14();};struct Derived:base{void Foo14(int);//会隐藏掉基类中的相应成员};int main(int argc, char**argv){Derived d;base b;b.Foo14();d.Foo14(10);d.Foo14();//错误d.base::Foo14();return 0;}

知识点6:由知识点5,基类和派生类的虚函数参数列表必须相同,并且可以通过基类的作用域运算符调用基类的虚函数,若派生类的与基类虚函同名的成员参数列表与虚函数不同,那么派生类中的成员并没有覆盖基类的相应虚函数,因为形参列表不一致,派生类将拥有两个同名成员。


由知识点6:若要覆盖,则需要形参列表一致

int fcn();

只有虚函数调用将存在,fcn(int)类型的调用都将错误


24:知识点1:虚析构函数:可以动态分配继承体系中的对象,如果我们需要删除一个指向派生类对象的基类指针,就需要虚析构函数,这样可以确保delete基类指针时将运行正确的虚构函数版本(动态绑定虚析构函数)

知识点2:基类需要一个虚析构函数产生的影响:一个类定义了析构函数,即使它通过=default的形式生成合成的版本,编译器也不会为这个类合成移动操作


答案:基类需要虚析构函数,需要动态销毁对象


25:知识点1:如果基类中的默认构造函数、拷贝构造函数或是析构函数被删除或是不可访问,则其派生类中的相应成员是被删除的

知识点2:大多数基类都会有一个虚析构函数,因此基类通常不会含有合成的移动操作,派生类中也如此,如果我们确实需要移动的操作,我们需要自行首先在基类中进行定义


如果删除了Quote基类的默认构造函数,那么它所有派生类中都不能使用默认构造函数进行初始化对象,如果需要默认初始化,则会报错


26:知识点1:派生类的构造函数不仅要初始化自己的成员,还负责初始化派生类对象的基类部分,而派生类对象的基类部分是自动销毁的,派生类的析构函数只负责销毁自身派生类的成员,但是拷贝和移动操作,都会包含基类的部分

知识点2:定义派生类的拷贝和移动构造函数,需要在其初始值列表中显式的调用其基类的拷贝或移动构造函数,否则的话,派生类对象的基类部分会被默认初始化(派生类中定义拷贝和移动赋值运算符也是一样,需要在函数体中调用基类的相应成员)

知识点3:对象的销毁顺序正好和其创建的顺序相反,派生类的部分先被销毁,基类部分后被销毁

知识点4:如果构造函数或者析构函数调用了某个虚函数,那么我们应该执行与构造函数和析构函数所属类类型相对应的虚函数版本

只写一个:Quote.h

#include <string>#include <iostream>using namespace std;class Quote{friend double print_total(ostream &,const Quote&,size_t);//定义为友元函数public:/*Quote() = default;//C++11新特性*/Quote();//默认构造函数Quote(const string &book,double sales_price):BookNo(book),price(sales_price){};//构造函数Quote (const Quote& quote1):BookNo(quote1.BookNo),price(quote1.price)//拷贝构造函数{cout<<"拷贝构造函数"<<endl;}Quote& operator=(const Quote& quote2)//拷贝赋值运算符{if (*this != quote2)//防止自赋值的情况{BookNo = quote2.BookNo;price = quote2.price;}cout<<"拷贝赋值运算符"<<endl;return *this;}Quote (const Quote&& quote3):BookNo(move(quote3.BookNo)),price(move(quote3.price))//移动构造函数{cout<<"移动构造函数"<<endl;}Quote& operator=(const Quote&& quote4) noexcept//移动赋值运算符{if (*this != quote4){BookNo = move(quote4.BookNo);price = move(quote4.price);}cout<<"拷贝赋值运算符"<<endl;return *this;}string isbn() const{return BookNo;};//保存每本书籍的编号virtual double net_price(size_t n) const//定义为虚函数,让派生类自己定价{return n*price;}virtual void debug(){cout<<"This is Quote Class"<<endl;cout<<"ISBN: "<<BookNo<<endl;cout<<"Price: "<<price<<endl;}private:string BookNo;//书籍的ISBN编号protected:double price;//普通状态下不打折的价格,C++11不支持非静态变量的类内初始化};


27:知识点1:C++11新标准,派生类可以直接重用基类的构造函数,一个类只能”继承“它的直接基类的构造函数,并且不能继承默认、拷贝、移动构造函数,若派生类中没有这些构造函数,编译器会自动产生合成版本

知识点2:在派生类中使用using声明语句

class B{public:B(const string &s);//构造函数};class A:public B{public:using B::B;//继承了基类的构造函数};

知识点3:通常情况下,using只是令某个名字在当前作用域可见,但是当作用于构造函数时,using声明语句将会使编译器产生代码,派生类继承基类的构造函数,其派生的部分成员将会默认初始化

知识点4:不管using出现在哪,基类的私有构造函数在派生类中还是一个私有类型的,其访问级别不会被using改变

知识点5:当一个基类的构造函数有默认实参时,这些实参不会被继承,派生类会获得多个继承的构造函数,每个构造函数都将省略掉一个含有默认实参的形参

bulk_quote.h

class bulk_quote : public Quote{public:bulk_quote();//默认构造函数bulk_quote(string& book, double p, size_t n, double disc):Quote(book,p),min_qty(n),discount(disc){};//派生类构造函数bulk_quote(const bulk_quote& rhs):Quote(rhs){}//继承拷贝构造函数bulk_quote& operator=(const bulk_quote& rhs){Quote::operator=(rhs);//继承拷贝赋值运算符return *this;}bulk_quote(const bulk_quote&& rhs):Quote(move(rhs)){}//继承移动构造函数bulk_quote& operator=(const bulk_quote&& rhs){Quote::operator=(move(rhs));//继承拷贝赋值运算符return *this;}//virtual double net_price(size_t) const;//重新声明,与下面的方式作用相同double net_price(size_t cnt) const override//允许派生类显示的注明它将使用哪个成员函数改写基类的虚函数{if (cnt >= min_qty){return cnt*(1-discount)*price;} else{return cnt*price;}}void debug(){cout<<"This is bulk_quote Class"<<endl;cout<<"DISCOUNT: "<<discount<<endl;cout<<"Min_qty: "<<min_qty<<endl;cout<<"Price: "<<price<<endl;}//除了自定义的版本,还可以访问其基类的相应成员private:double discount;//折扣额,C++11新标准size_t min_qty ;//适用折扣的最低购买量};


28:知识点1:使用容器存放继承体系中的对象时,必须采用简介存储的方式,因为不允许保存类型不相同的元素,如若我们保存了一个quote对象,再传入一个bulk_quote,那么其派生类的部分将会被忽略掉

知识点2:可以使用指向类类型对象的指针存放入容器,最好使用智能指针,管理动态类型


这题把我给气的,难受死~~~~

记住27题要求写继承自基类的构造函数,千万别忘了,不然其移动或者拷贝函数继承的是基类的,其派生类的部分会默认初始化,答案就是一样的了!!!记住将你写的继承的构造函数注释掉!!!

#include <vector>#include <iostream>#include <algorithm>#include "Quote.h"using namespace std;int main(int argc, char**argv){vector<Quote> vec1;bulk_quote b1(string("C++ Primer I"),128,10,0.5);bulk_quote b2(string("C++ Primer II"),118,8,0.6);bulk_quote b3(string("C++ Primer III"),108,12,0.4);bulk_quote b4(string("C++ Primer IV"),138,4,0.3);vec1.push_back(b1);vec1.push_back(b2);vec1.push_back(b3);vec1.push_back(b4);double Sum_of_net_price1 = 0;for(int i = 0; i < vec1.size(); ++i){Sum_of_net_price1 += vec1[i].net_price(20);cout<<Sum_of_net_price1<<endl;}cout<<"The total net price is:"<<Sum_of_net_price1<<endl;vector<shared_ptr<Quote>> vec2;vec2.push_back(make_shared<bulk_quote>(b1));vec2.push_back(make_shared<bulk_quote>(b2));vec2.push_back(make_shared<bulk_quote>(b3));vec2.push_back(make_shared<bulk_quote>(b4));double Sum_of_net_price2 = 0;for(int i = 0; i < vec2.size(); ++i){Sum_of_net_price2 += vec2[i]->net_price(20);cout<<Sum_of_net_price2<<endl;}cout<<"The total net price is:"<<Sum_of_net_price2<<endl;return 0;}


Quote.h

#ifndef QUOTE_H#define QUOTE_H#include <string>#include <iostream>using namespace std;class Quote{friend double print_total(ostream &,const Quote&,size_t);//定义为友元函数public:/*Quote() = default;//C++11新特性*/Quote();//默认构造函数Quote(const string &book,double sales_price):BookNo(book),price(sales_price){};//构造函数Quote (const Quote& quote1):BookNo(quote1.BookNo),price(quote1.price)//拷贝构造函数{cout<<"拷贝构造函数"<<endl;}// Quote& operator=(const Quote& quote2)//拷贝赋值运算符// {// if (*this != quote2)//防止自赋值的情况// {// BookNo = quote2.BookNo;// price = quote2.price;// }// cout<<"拷贝赋值运算符"<<endl;// return *this;// }Quote (const Quote&& quote3):BookNo(move(quote3.BookNo)),price(move(quote3.price))//移动构造函数{cout<<"移动构造函数"<<endl;}// Quote& operator=(const Quote&& quote4) noexcept//拷贝赋值运算符// {// if (*this != quote4)// {// BookNo = move(quote4.BookNo);// price = move(quote4.price);// }// cout<<"拷贝赋值运算符"<<endl;// return *this;// }string isbn() const{return BookNo;};//保存每本书籍的编号virtual double net_price(size_t n) const//定义为虚函数,让派生类自己定价{cout<<"Quote"<<endl;return n*price;}virtual void debug(){cout<<"This is Quote Class"<<endl;cout<<"ISBN: "<<BookNo<<endl;cout<<"Price: "<<price<<endl;}private:string BookNo;//书籍的ISBN编号protected:double price;//普通状态下不打折的价格,C++11不支持非静态变量的类内初始化};double print_total(ostream &os,const Quote&item,size_t n){//动态绑定的实例double ret = item.net_price(n);os<<"ISBN: "<<item.isbn()<<endl;os<<"sold: "<<n<<" total price: "<<ret<<endl;return ret;}class bulk_quote : public Quote{public:bulk_quote();//默认构造函数bulk_quote(string& book, double p, size_t ny, double disc):Quote(book,p),min_qty(ny),discount(disc){};//派生类构造函数// bulk_quote(const bulk_quote& rhs):Quote(rhs){}//继承拷贝构造函数// bulk_quote& operator=(const bulk_quote& rhs)// {// Quote::operator=(rhs);//继承拷贝赋值运算符// return *this;// }// bulk_quote(const bulk_quote&& rhs):Quote(move(rhs)){}//继承移动构造函数// bulk_quote& operator=(const bulk_quote&& rhs)// {// Quote::operator=(move(rhs));//继承拷贝赋值运算符// return *this;// }这就是作怪的继承构造函数啦!//virtual double net_price(size_t) const;//重新声明,与下面的方式作用相同double net_price(size_t cnt) const/* override*///允许派生类显示的注明它将使用哪个成员函数改写基类的虚函数{cout<<"bulk_uote"<<endl;if (cnt >= min_qty){cout<<cnt*(1-discount)*price<<endl;return cnt*(1-discount)*price;} else{return 0;}}void debug(){cout<<"This is bulk_quote Class"<<endl;cout<<"DISCOUNT: "<<discount<<endl;cout<<"Min_qty: "<<min_qty<<endl;cout<<"Price: "<<price<<endl;}//除了自定义的版本,还可以访问其基类的相应成员private:double discount;//折扣额,C++11新标准size_t min_qty ;//适用折扣的最低购买量};class number_quote : public Quote{public:number_quote();//默认构造函数number_quote(string& book, double p, size_t n, double disc):Quote(book,p),number1(n),discount1(disc){};//派生类构造函数//virtual double net_price(size_t) const;//重新声明,与下面的方式作用相同double net_price(size_t cnt) const override//允许派生类显示的注明它将使用哪个成员函数改写基类的虚函数{if (cnt >= number1){return number1*(1-discount1)*price+(cnt-number1)*price;} else{return cnt*(1-discount1)*price;}}void debug(){cout<<"This is number_quote Class"<<endl;cout<<"DISCOUNT: "<<discount1<<endl;cout<<"Max_qty: "<<number1<<endl;cout<<"Price: "<<price<<endl;//protected成员也可以包含}//除了自定义的版本,还可以访问其基类的相应成员private:double discount1;//折扣额,C++11新标准size_t number1 ;//给定限量};#endif QUOTE_H



30:

Basket.h

class Basket  {      static bool compare(const std::shared_ptr<Quote> rhs, const std::shared_ptr<Quote> lhs) {          return rhs->isbn() < lhs->isbn();      }  //自定义compare    std::multiset<std::shared_ptr<Quote>, decltype(compare)*> items{ compare }; //头文件setpublic:      void add_item(const std::shared_ptr<Quote> &sale) {          items.insert(sale);      }      void add_item(const Quote &q)      {          items.insert(std::shared_ptr<Quote>(q.clone()));      }      void add_item(Quote &&q)      {          items.insert(std::shared_ptr<Quote>(std::move(q).clone()));      }      double total_receipt(std::ostream &os)const;  };  


Basket.cpp

double Basket::total_receipt(std::ostream &os)const  {      double sum = 0.0;                                            for (auto iter = items.cbegin(); iter != items.cend(); iter = items.upper_bound(*iter))      {          sum += print_total(os, **iter, items.count(*iter));     }      os << "Total Sale: " << sum << std::endl;      return sum;  } //和书上的一样

还需要给Quote类加上clone虚函数,太长了就不贴了,书上写的很完整



1 0
原创粉丝点击