https://blog.csdn.net/misayaaaaa/article/details/61193442
1:知识点1:面向对象程序设计的三个基本概念:数据抽象、继承和动态绑定(核心概念)
数据抽象:将类的接口与实现分离
继承:我们可以定义与其他类相似但完全不相同的新类
动态绑定:在使用这些彼此相似的类时,在一定程度上忽略他们的区别,统一使用它们的对象
知识点2:类的继承构成一种层次关系,在层次根部的为基类,其他类则直接或者间接的从基类中继承而来,称为派生类
基类负责定义在层次关系中所有类共有的数据成员,而派生类定义各自特有的成员
知识点3:对于某些函数,基类希望它的派生类各自定义适合其自身的版本,基类会将该函数声明为虚函数!!!而派生类必须在其内部对所有重新定义的虚函数进行声明,若不加virtual关键字,可以在其后加上override显式声明改写基类的虚函数
知识点4:派生类必须通过使用类派生列表明确指出它是从哪个基类中继承而来的:冒号+(访问限定符)基类列表
知识点5:动态绑定,函数的运行版本由实参决定也被称为运行时绑定:在使用基类的引用或者指针调用一个虚函数时将会发生动态绑定!!!调用的函数若是虚函数,则调指针所指的对象的类型的函数,否则调指针类型的类的函数。当我们使用指针或者引用调用虚函数时,将调用动态绑定
知识点6:基类通常应该定义一个虚析构函数,即使该函数不执行任何操作也是如此!
知识点7:关键词virtual只能出现在类内的声明函数语句之前,而不能用于类外部的函数定义,在派生类中相应的函数将隐式的是虚函数(不加virtual的情况)
知识点8:非虚函数,其解析过程将发生在编译时而非运行时
知识点9:protected受保护的成员,基类希望它的派生类有权访问该成员,同时禁止其他用户访问,而private即使是其派生类也不能访问
虚成员就是虚函数,详解见知识点
2:见知识点9
3: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;//保存每本书籍的编号
- virtual double net_price(size_t) const;//定义为虚函数,让派生类自己定价
- 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:
- virtual double net_price(size_t) const;//重新声明
- double net_price(size_t) const override;//允许派生类显示的注明它将使用哪个成员函数改写基类的虚函数
- };
- #endif QUOTE_H
4:知识点1:派生类必须通过类派生列表指出它是从那个基类继承而来的
知识点2:派生类必须将其继承来的成员函数中需要覆盖的那些重新声明!!!
知识点3:大多数类只继承自一个类,被称作“单继承”,还有其他的类型在P710
知识点4:派生类拥有的成员包括自己定义的成员和继承自基类的相关成员,但是这两部分并不一定是连续存储的
知识点5:我们可以把派生类的对象当成基类的对象来使用,也可以将基类的指针或引用绑定到派生类对象中的基类部分上
- #include <vector>
- #include <iostream>
- #include <algorithm>
- #include"Quote.h"
- int main()
- {
- Quote item;//基类对象
- bulk_quote bulk;//派生类对象
- Quote *p = &item;//P为一个指针,指向一个Quote类型对象
- p = &bulk;//p指向的是bulk的Quote部分,也就是派生类的基类部分
- Quote &r = bulk;//r绑定到bulk的基类部分
- }
知识点6:继承的关键:派生类中有基类的对应组成部分
知识点7:派生类需要使用基类的构造函数来初始化它的基类部分—每个类控制自己的初始化过程,派生类的构造函数同样是通过构造函数初始化列表将实参传递给基类的构造函数,再由基类的构造函数,完成其基类部分的初始化
- bulk_quote(string& book, double p, size_t n, double disc):Quote(book,p),min_qty(n),discount(disc){};//派生类构造函数
知识点8:首先初始化基类的部分,然后按照声明的顺序依次初始化派生类的成员
知识点9:派生类的作用域嵌套在基类的作用域之内,所以对于一个派生类的成员,其使用派生类成员和积累成员的方式相同—最好使用各自的接口
知识点10:若基类中定义了静态成员static,则在整个继承体系中只存在该成员的唯一定义,并且只有一份实例
知识点11:派生类的声明只能包含类名,不能包含它的派生列表:一条声明语句的目的是令程序知晓某个名字的存在以及该名字表示一个怎样的实体,派生列表以及其定义的相关细节必须与类的主体一起出现
知识点12:若要将一个类当作基类来使用,那么这个类就必须是已经定义过的,仅仅声明是不可以的,因为派生类需要知道其基类到底是什么,所以类不能派生其本身,最终的派生类将包含所有间接类的相关成员
知识点13:C++11新标准,防止类被继承可以在其类名后加final,表示其不能作为基类
(a)错误,类不能继承自本身,见知识点11
(b)正确
(c)错误,类的声明中不能包含它的派生列表(感谢评论区的小伙伴指出错误!我知识点总结时漏了这一点!~)
5:
- 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) const override;//允许派生类显示的注明它将使用哪个成员函数改写基类的虚函数
- //除了自定义的版本,还可以访问其基类的相应成员
- private:
- double discount =0.0;//适用折扣的最低购买量
- size_t min_qty = 0;//折扣额,C++11新标准
- };
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;
- }
- 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;
- }
- }
- //除了自定义的版本,还可以访问其基类的相应成员
- private:
- double discount;//适用折扣的最低购买量
- size_t min_qty ;//折扣额,C++11新标准
- };
- #endif QUOTE_H
test.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);
- print_total(cout, b1, 10);
- print_total(cout, b2, 10);
- system("pause");
- return 0;
- }
7:其实就是修改一下net_price函数
- 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;
- }
- }
- //除了自定义的版本,还可以访问其基类的相应成员
- private:
- double discount1;//折扣额,C++11新标准
- size_t number1 ;//给定限量
- };
8:知识点1:通常情况下,如果我们想使用指针或者引用绑定一个对象,则指针或者引用的类型需要和对象的类型一致或者可进行const转换,但是存在继承关系的类是一个重要的例外:我们可以将基类的指针或者引用绑定到派生类的对象上:这就意味着,当我们使用基类的指针或者引用时,我们并不知道该指针或引用所绑定的对象的真实类型,该对象可能是基类的对象,也可能是派生类的对象
知识点2:当我们使用一个变量或者表达式时,我们需要将其静态类型和动态类型相互区分开,表达式的静态类型是在编译时已知的,是变量声明时的类型或者表达式生成的类型,其动态类型是变量或者表达式表示内存的对象的类型,知道运行时才可知,即如item对象,静态类型为Quote&,动态类型则依赖于item所绑定的实参,直到函数运行时才可知
知识点3:如果表达式既不是指针也不是引用,则其动态类型和静态类型会一直绑定在一起
答案:见知识点2
9:见知识点3,基类的引用或者指针,其静态类型和动态类型分离
10:知识点1:之所以存在派生类到基类的类型转换是因为派生类之中含有基类的部分,但是基类中并不含有派生类中的成员,所以一个基类对象既可能是以独立的形式存在,也可能是派生类对象的一部分,所以不存在从基类到派生类之间的自动类型转换(可以将派生类转化为基类)
知识点2:派生类到基类的类型转换只针对与引用或者指针的类型,其本类型是不支持的,即对象之间不存在类型转换
知识点3:当我们用一个派生类的对象给一个基类对象初始化或者赋值时,只有其基类的部分被拷贝、移动或者赋值,它的派生类部分将会被忽略掉
答案:ifstream 从 istream中派生而来,是一个对象,所以可以正常使用
关键点:存在继承关系的类型之间的转换规则
1:从派生类像基类类型转换只有对指针或者引用类型有效
2:基类到派生类不存在隐式类型转换
3:派生类到基类的类型转换也可能会由于访问限制而变得不可行