11:知识点1:当我们使用引用或者指针调用一个虚成员函数时才会执行动态绑定,因为我们知道在程序运行时才知道到底调用了哪个版本的虚函数,所以所有虚函数都必须有定义
知识点2:引用和指针的静态类型与动态类型不同的这一事实是C++语言支持多态性的根本所在(必须是虚函数)
知识点3:一个派生类的函数成员如果覆盖了基类的继承而来的虚函数,则它的形参类型必须与被它覆盖的基类函数完全一致,返回类型也必须相匹配,但当类的虚函数的返回类型是类本身的指针或者引用时,返回类型可以不同
知识点4:派生类如果定义了一个与基类虚函数同名函数,但参数列表不相同的话,仍然是合法行为,编译器会认为该函数与基类虚函数是相互独立的,但这往往是把形参列表弄错了的错误,编译器发现不了,所以C++11有一个好东西,在其后加上override表示其要对基类的函数进行覆盖,若未覆盖,编译器报错,我们可以发现自己的错误
知识点5:虚函数可以有默认实参,若函数调用了默认实参,则实参值由静态类型决定,所以基类和派生类中定义的默认实参最好一致
知识点6:某些情况下,我们不希望进行动态绑定,我们可使用作用域运算符强行指定其执行哪个版本,进行回避虚函数—一般情况下是成员函数中的代码才需要以防止自己调用自身造成无限循环
Quote.h
#ifndef QUOTE_H #define QUOTE_H #include <string> 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){}; 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不支持非静态变量的类内初始化 }; 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 n, double disc):Quote(book,p),min_qty(n),discount(disc){};//派生类构造函数 //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 ;//适用折扣的最低购买量 }; 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_Htest.cpp
#include <vector> #include <iostream> #include <algorithm> #include"Quote.h" int main(int argc, char**argv) { Quote b1("龙族I", 128); bulk_quote b2(string("龙族II"), 128.0, 10, 0.7); number_quote b3(string("龙族III"), 128.0, 10, 0.7); print_total(cout, b1, 10); print_total(cout, b2, 10); cout<<endl; b1.debug(); b2.debug(); b3.debug(); system("pause"); return 0; }12:当然有需要啊,两个不一样的,override是表示对其基类函数的覆盖,final是表示此函数不能再被其他的函数所覆盖
13:在知识点6中,我们提到了成员函数的函数体中避免虚函数的问题,因为这会导致函数的自我调用,导致无限循环,此处派生类的print()函数中使用的print()函数会自动被解析为对自身的调用。所以应加上作用域限定符:
base::print(os);14:
(a):调用基类版本的print()函数
(b):调用派生类版本的print()函数
(c):调用基类的name()函数
(d):调用派生类中基类部分的name()函数
(e):调用基类版本
(f):调用派生类版本
15:Disc_quote.h,注意其成员是protected
#ifndef DISC_QUOTE_H #define DISC_QUOTE_H #include "Quote.h" using namespace std; class Disc_quote:public Quote { public: Disc_quote();//默认构造函数 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;//临界数量 double _discount;//折扣 }; class Bulk_quote : public Disc_quote { public: Bulk_quote();//默认构造函数 Bulk_quote(const string&book, double price, size_t qty,double disc):Disc_quote(book,price,qty,disc){}//构造函数,直接调用基类的构造函数 double net_price(size_t n) const { if (n > quantity) { return n*(1-_discount)*price; } else { return n*price; } } }; #endif DISC_QUOTE_H16:只需要修改一下net_price()函数
class limit_quote : public Disc_quote { public: limit_quote();//默认构造函数 limit_quote(const string&book, double price, size_t qty,double disc):Disc_quote(book,price,qty,disc){}//构造函数,直接调用基类的构造函数 double net_price(size_t n) const { if (n > quantity) { return quantity*(1-_discount)*price+(n-quantity)*price; } else { return quantity*(1-_discount)*price; } } };17:不能实例化抽象类
18:知识点1:protected成员,类的对象用户不可访问,派生类和友元可访问
知识点2:派生类的成员只能通过派生类对象来访问基类的受保护成员!!!也就是说派生类不能访问基类对象的受保护成员
class A { protected: int i; }; class B:public A { public: friend void foo(B &); friend void foo(A &); int j; }; void foo(B &b){b.i = 0;}; void foo(A &a){a.i = 0;};//派生类的成员或者友元不能通过其基类的对象来访问受保护成员知识点3:派生类的成员和友元只能访问派生类对象中的基类的受保护部分,对于普通的基类对象的受保护成员不具有特殊的访问权限
知识点4:共有继承,继承自基类的成员在派生类中遵循原有的访问说明符,私有继承,继承而来的基类成员在派生类中变为private,变为私有,派生类的对象都不可以访问其本身的私有成员,但私有继承不影响派生类本身的访问权限,它同样不可以使用基类的私有成员。
知识点5:友元关系不能继承,每个类负责自己控制自己成员的访问权限
知识点6:当一个类将另一个类作为友元函数时,这种友元关系只对作出声明的类有效,对于原来的类来说,其友元的基类或派生类不具备特殊访问能力
知识点7:通过在类的内部使用using声明改变派生类继承的某个名字的访问权限,using语句中名字的访问权限由该using出现之前的访问说明符决定
知识点8:struct默认公有继承,private默认私有继承,但是我们最好将其显式的表明
知识点9:1: 假定B继承自A,无论B以什么方式继承A,B的成员函数和友元都能使用派生类到基类的转换
2: B继承A的方式是公有或者保护,则B的派生类的成员或友元可以使用B到A的转换,若未私有则不行
3: B继承A的方式是公有的,用户的代码才能使用B到A的(派生类到基类)的转换,保护或者私有方式则不行
答案:见知识点9
19:见知识点9的第1、2条
20:Base.h
#ifndef BASE_H #define BASE_H class Base { public: void pub_mem(); protected: int pro_mem(); private: char pri_mem(); }; struct Pub_derv:public Base { int f(){return pro_mem();} //错误,私有成员不可访问 char g(){return pri_mem();} }; struct Pri_derv:private Base { int f1(){return pro_mem();} //错误,私有成员也不可访问 char g1(){return pri_mem();} }; struct Pro_derv:protected Base { int f2(){return pro_mem();} //错误,私有成员也不可访问 char g3(){return pri_mem();} }; struct Derivated_from_Public:public Pub_derv { int use(){ return pro_mem();} }; struct Derivated_from_Protected:public Pro_derv { int use(){ return pro_mem();} }; struct Derivated_from_Private:public Pri_derv { //错误,不可访问 int use(){ return pro_mem();} }; #endif BASE_Hmain.cpp
#include <vector> #include <iostream> #include <algorithm> #include "Quote.h" #include "Disc_quote.h" #include "Base.h" using namespace std; int main(int argc, char**argv) { Pub_derv d1; Pri_derv d2; Pro_derv d3; Derivated_from_Public dd1; Derivated_from_Protected dd2; Derivated_from_Private dd3; d1.pub_mem(); /* d2.pub_mem();//错误,不可访问,该对象成员都为私有*/ Base *p = &d1; p = &d2;//不允许对不可访问的基类进行转换 p = &d3;//不允许对不可访问的基类进行转换 p = &dd1; p = &dd2;//不允许对不可访问的基类进行转换 p = &dd3;//不允许对不可访问的基类进行转换 return 0; }