自己写的解答,如有错误之处,烦请在评论区指正!
对于某些成员,基类希望它的派生类各自定义适合自身的版本(而不是不作改变直接继承),此时基类就将这些成员定义成虚成员。
对于 protected,派生类有权访问,但是其他用户无法访问
对于 private,派生类和其他用户都无法访问
#pragma once #include <iostream> using std::ostream; using std::endl; #include <string> using std::string; class Quote { public: Quote() = default; Quote(const string& book, double sales_price) : bookNo(book), price(sales_price) { } string isbn() const { return bookNo; } virtual double net_price(size_t n) const { return n * price; } virtual ~Quote() = default; private: string bookNo; protected: double price = 0.0; }; class Bulk_quote : public Quote { public: double net_price(size_t) const override; }; double printTotal(ostream& os, const Quote &item, size_t n) { double ret = item.net_price(n); os << "ISBN: " << item.isbn() << " # sold: " << n << " total due: " << ret << endl; return ret; }
a. 不正确,一个类不能派生自身
b. 正确
c. 不正确,在声明中不能写派生列表,这些细节都要放在类的主体中。
#pragma once #include <iostream> using std::ostream; using std::endl; #include <string> using std::string; class Quote { public: Quote() = default; Quote(const string& book, double sales_price) : bookNo(book), price(sales_price) { } string isbn() const { return bookNo; } virtual double net_price(size_t n) const { return n * price; } virtual ~Quote() = default; private: string bookNo; protected: double price = 0.0; }; class Bulk_quote : public Quote { public: Bulk_quote() = default; Bulk_quote(const string& book, double p, size_t qty, double disc) : Quote(book, p), min_qty(qty), discount(disc) { } double net_price(size_t) const override; private: size_t min_qty = 0; double discount = 0.0; }; double Bulk_quote::net_price(size_t cnt) const { if (cnt >= min_qty) return cnt * (1 - discount) * price; else return cnt * price; } double printTotal(ostream& os, const Quote &item, size_t n) { double ret = item.net_price(n); os << "ISBN: " << item.isbn() << " # sold: " << n << " total due: " << ret << endl; return ret; }
Quote.h 代码见上一题。
CppPrimerCh15.cpp:
#include <iostream> #include "Quote.h" using namespace std; int main() { Quote q("Harry Potter", 15.0); Bulk_quote bq("C++ Primer", 65.0, 3, 0.5); printTotal(cout, q, 5); printTotal(cout, bq, 5); return 0; }
运行结果:
ISBN: Harry Potter # sold: 5 total due: 75 ISBN: C++ Primer # sold: 5 total due: 162.5
class Limited_bulk_quote : public Quote { public: Limited_bulk_quote() = default; Limited_bulk_quote(const string& book, double p, size_t qty, double disc) : Quote(book, p), max_qty(qty), discount(disc) { } double net_price(size_t) const override; private: size_t max_qty = 0; double discount = 0.0; }; double Limited_bulk_quote::net_price(size_t cnt) const { if (cnt <= max_qty) return cnt * (1 - discount) * price; else return cnt * price; }
静态类型在编译时总是已知的,它是变量声明时的类型或表达式生成的类型
而动态类型则是变量或表达式表示的内存中的对象的类型,动态类型直到运行时才可知
#include <iostream> #include "Quote.h" using namespace std; int main() { Bulk_quote derived; Limited_bulk_quote anotherDerived; Quote* baseP = &derived; Quote& baseR = derived; Quote& baseR1 = anotherDerived; return 0; }
read 函数的第一个参数接受一个 iostream 的引用,实际用了 ifstream 类型来调用,这是派生类向基类的隐式转换(仅对指针或引用类型有效)。因为派生类中包括了基类的成员,所以 iostream 的引用可以指向派生类中基类的这部分。
#pragma once #include <iostream> using std::ostream; using std::cout; using std::endl; #include <string> using std::string; /*============================================= Quote =============================================*/ class Quote { public: Quote() = default; Quote(const string& book, double sales_price) : bookNo(book), price(sales_price) { } string isbn() const { return bookNo; } virtual double net_price(size_t n) const { return n * price; } virtual ~Quote() = default; virtual void debug() const { cout << "bookNo: " << bookNo << "\nprice: " << price << endl; } private: string bookNo; protected: double price = 0.0; }; double printTotal(ostream& os, const Quote &item, size_t n) { double ret = item.net_price(n); os << "ISBN: " << item.isbn() << " # sold: " << n << " total due: " << ret << endl; return ret; } /*============================================= Bulk_quote =============================================*/ class Bulk_quote : public Quote { public: Bulk_quote() = default; Bulk_quote(const string& book, double p, size_t qty, double disc) : Quote(book, p), min_qty(qty), discount(disc) { } double net_price(size_t) const override; virtual void debug() const override { cout << "min_qty: " << min_qty << "\ndiscount: " << discount << endl; } private: size_t min_qty = 0; double discount = 0.0; }; double Bulk_quote::net_price(size_t cnt) const { if (cnt >= min_qty) return cnt * (1 - discount) * price; else return cnt * price; }
CppPrimerCh15.cpp:
#include <iostream> #include "Quote.h" using namespace std; int main() { Quote base("Harry Potter", 15.0); Bulk_quote derived("C++ Primer", 65.0, 3, 0.5); base.debug(); putchar('\n'); derived.debug(); putchar('\n'); derived.Quote::debug(); return 0; }
运行结果:
bookNo: Harry Potter price: 15 min_qty: 3 discount: 0.5 bookNo: C++ Primer price: 65
有必要的时候可以这么做,这两个关键字不冲突:
有问题,derived 类中的 print 函数体中又调用了 print 函数,本意应该是调用基类中的 print,但是如果不显示地指出作用域,就会调用自身引发无限递归。另一个问题是最好在派生类重写的函数头后面加上 override 防止参数列表写错。
#include <iostream> #include "Quote.h" using namespace std; class Base { public: string name() { return basename; } virtual void print(ostream& os) { os << basename; } private: string basename; }; class Derived : public Base { public: void print(ostream &os) override { Base::print(os); os << " " << i; } private: int i; }; int main() { Derived d; d.print(cout); return 0; }
a. base::print()
b. derived::print()
c. base::name()
d. base::name()
e. base::print()
f. derived::print()
#pragma once #include <iostream> using std::ostream; using std::cout; using std::endl; #include <string> using std::string; /*============================================= Quote =============================================*/ class Quote { public: Quote() = default; Quote(const string& book, double sales_price) : bookNo(book), price(sales_price) { } string isbn() const { return bookNo; } virtual double net_price(size_t n) const { return n * price; } virtual ~Quote() = default; virtual void debug() const { cout << "bookNo: " << bookNo << "\nprice: " << price << endl; } private: string bookNo; protected: double price = 0.0; }; double printTotal(ostream& os, const Quote &item, size_t n) { double ret = item.net_price(n); os << "ISBN: " << item.isbn() << " # sold: " << n << " total due: " << ret << endl; return ret; } /*============================================= Disc_quote =============================================*/ 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; protected: size_t quantity = 0; double discount = 0.0; }; /*============================================= Bulk_quote =============================================*/ class Bulk_quote : public Disc_quote { public: Bulk_quote() = default; Bulk_quote(const string& book, double p, size_t qty, double disc) : Disc_quote(book, p, qty, disc) { } double net_price(size_t) const override; virtual void debug() const override { cout << "min_qty: " << min_qty << "\ndiscount: " << discount << endl; } private: size_t min_qty = 0; double discount = 0.0; }; double Bulk_quote::net_price(size_t cnt) const { if (cnt >= min_qty) return cnt * (1 - discount) * price; else return cnt * price; } /*============================================= Limited_bulk_quote =============================================*/ class Limited_bulk_quote : public Disc_quote { public: Limited_bulk_quote() = default; Limited_bulk_quote(const string& book, double p, size_t qty, double disc) : Disc_quote(book, p, qty, disc) { } double net_price(size_t) const override; private: size_t max_qty = 0; double discount = 0.0; }; double Limited_bulk_quote::net_price(size_t cnt) const { if (cnt <= max_qty) return cnt * (1 - discount) * price; else return cnt * price; }
见 15
不允许使用抽象类类型 “Disc_quote” 的对象
函数 double net_price(size_t) const
是纯虚拟函数
// 只有D公有地继承B时,用户代码才能使用D向B的转换 Base *p = &d1; // 正确 p = &d2; // 错误 p = &d3; // 错误 // 派生类的派生类也是同理 // 只有DD公有地继承D时,用户代码才能使用DD向D的转换,然后再进行D到B的转换 p = &dd1; // 正确 p = &dd2; // 错误 p = &dd3; // 错误
class Base { public: int pub_mem; protected: int prot_mem; private: int priv_mem; }; class Pub_Derv : public Base { void memfcn(Base& b) { b = *this; } }; class Prot_Derv : protected Base { void memfcn(Base& b) { b = *this; } }; class Priv_Derv : private Base { void memfcn(Base& b) { b = *this; } }; class Derived_from_Public : public Pub_Derv { void memfcn(Base& b) { b = *this; } }; class Derived_from_Protected : public Prot_Derv { void memfcn(Base& b) { b = *this; } }; class Derived_from_Private : public Priv_Derv { void memfcn(Base& b) { b = *this; } // 只有这个是非法的 };
对于 Pub_Derv、Prot_Derv、Priv_Derv 这三个类,不论 D 以什么方式继承 B,D 的成员函数和友元都能使用派生类向基类的转换,所以对这三个类来说成员函数都是合法的
对于 Derived_from_Public、Derived_from_Protected、Derived_from_Private 来说,如果 D 继承 B 的方式是 public 或者 protected,DD(D 的派生类)才能使用派生类向基类的转换,所以只有 Derived_from_Private 的成员函数非法
略
图形
2D 图形:图形
3D 图形:图形
矩形:2D 图形
圆形:2D 图形
……
长方体:3D 图形
球体:3D 图形
……
略
#include <iostream> #include "Quote.h" using namespace std; class Base { public: virtual int fcn() { cout << "Base::fcn()\n"; return 0; } }; class D1 : public Base { public: virtual int fcn() override { cout << "D1::fcn()\n"; return 0; } }; class D2 : public D1 { public: virtual int fcn() override { cout << "D2::fcn()\n"; return 0; } }; int main() { Base bobj; D1 d1obj; D2 d2obj; Base *bp1 = &bobj, *bp2 = &d1obj, *bp3 = &d2obj; bp1->fcn(); bp2->fcn(); bp3->fcn(); return 0; }
运行结果:
Base::fcn() D1::fcn() D2::fcn()
基类通常需要定义一个虚析构函数。虚析构函数可以确保编译器执行正确的析构函数版本(例如一个指针的静态类型与指向对象的动态类型不一致的时候)。
因为可以预见 Disc_quote 会有自己的派生类,如果去掉默认构造函数的话,因为我们为 Disc_quote 定义了一个接受 4 个参数的自定义构造函数,于是默认构造函数是删除的,Disc_quote 的派生类如 Bulk_quote 的默认构造函数也将会是删除的,因为 Bulk_quote 的 Disc_quote 基类部分无法被构造。
Quote.h:
#pragma once #include <iostream> using std::ostream; using std::cout; using std::endl; #include <string> using std::string; #include <utility> using std::pair; /*============================================= Quote =============================================*/ class Quote { public: //Quote() = default; Quote() : bookNo(""), price(0.0) { printf("Quote constructor\n"); } //Quote(const Quote& rhs) = default; Quote(const Quote& rhs) : bookNo(rhs.bookNo), price(rhs.price) { printf("Quote copy constructor\n"); } //Quote(Quote&& rhs) = default; Quote(Quote&& rhs) noexcept : bookNo(std::move(rhs.bookNo)), price(std::move(rhs.price)) { printf("Quote move constructor\n"); } //Quote& operator=(const Quote& rhs) = default; Quote& operator=(const Quote& rhs) { printf("Quote copy assignment\n"); bookNo = rhs.bookNo; price = rhs.price; return *this; } //Quote& operator=(Quote&& rhs) = default; Quote& operator=(Quote&& rhs) noexcept { printf("Quote move assignment\n"); bookNo = rhs.bookNo; price = rhs.price; return *this; } Quote(const string& book, double sales_price) : bookNo(book), price(sales_price) { } string isbn() const { return bookNo; } virtual double net_price(size_t n) const { return n * price; } //virtual ~Quote() = default; virtual ~Quote() { printf("Quote deconstructor\n"); } virtual void debug() const { cout << "bookNo: " << bookNo << "\nprice: " << price << endl; } private: string bookNo; protected: double price = 0.0; }; double printTotal(ostream& os, const Quote &item, size_t n) { double ret = item.net_price(n); os << "ISBN: " << item.isbn() << " # sold: " << n << " total due: " << ret << endl; return ret; } /*============================================= Disc_quote =============================================*/ 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) { } //virtual ~Disc_quote() = default; virtual ~Disc_quote() { printf("Disc_quote deconstructor\n"); } virtual double net_price(size_t) const = 0; pair<size_t, double> discount_policy() const { return { quantity, discount }; } protected: size_t quantity = 0; double discount = 0.0; }; /*============================================= Bulk_quote =============================================*/ class Bulk_quote : public Disc_quote { public: //Bulk_quote() = default; Bulk_quote() : Disc_quote(), min_qty(0), discount(0.0) { printf("Bulk_quote constructor\n"); } //Bulk_quote(const Bulk_quote& rhs) = default; Bulk_quote(const Bulk_quote& rhs) : Disc_quote(rhs), min_qty(rhs.min_qty), discount(rhs.discount) { printf("Bulk_quote copy constructor\n"); } //Bulk_quote(Bulk_quote&& rhs) = default; Bulk_quote(Bulk_quote&& rhs) noexcept : Disc_quote(std::move(rhs)), min_qty(std::move(rhs.min_qty)), discount(std::move(rhs.discount)) { printf("Bulk_quote move constructor\n"); } //Bulk_quote& operator=(const Bulk_quote& rhs) = default; Bulk_quote& operator=(const Bulk_quote& rhs) { printf("Bulk_quote copy assignment\n"); Disc_quote::operator=(rhs); min_qty = rhs.min_qty; discount = rhs.discount; return *this; } //Bulk_quote& operator=(Bulk_quote&& rhs) = default; Bulk_quote& operator=(Bulk_quote&& rhs) noexcept { printf("Bulk_quote move assignment\n"); Disc_quote::operator=(std::move(rhs)); min_qty = rhs.min_qty; discount = rhs.discount; return *this; } //virtual ~Bulk_quote() = default; virtual ~Bulk_quote() { printf("Bulk_quote deconstructor\n"); } Bulk_quote(const string& book, double p, size_t qty, double disc) : Disc_quote(book, p, qty, disc) { } double net_price(size_t) const override; virtual void debug() const override { cout << "min_qty: " << min_qty << "\ndiscount: " << discount << endl; } private: size_t min_qty = 0; double discount = 0.0; }; double Bulk_quote::net_price(size_t cnt) const { if (cnt >= min_qty) return cnt * (1 - discount) * price; else return cnt * price; }
CppPrimerCh15.cpp:
#include <iostream> #include "Quote.h" using namespace std; int main() { Quote b1; Quote b2(b1); Quote b3(std::move(b1)); b1 = b2; b1 = std::move(b2); Bulk_quote d1; Bulk_quote d2(d1); Bulk_quote d3(std::move(d1)); d1 = d2; d1 = std::move(d2); return 0; }
运行结果:
Quote move constructor Quote copy assignment Quote move assignment Quote constructor Bulk_quote constructor Quote copy constructor Bulk_quote copy constructor Quote copy constructor Bulk_quote move constructor Bulk_quote copy assignment Quote copy assignment Bulk_quote move assignment Quote copy assignment Bulk_quote deconstructor Disc_quote deconstructor Quote deconstructor Bulk_quote deconstructor Disc_quote deconstructor Quote deconstructor Bulk_quote deconstructor Disc_quote deconstructor Quote deconstructor Quote deconstructor Quote deconstructor Quote deconstructor
在类中加入
using Disc_quote::Disc_quote;