《C++ Primer》读书笔记第十五章-2-虚函数、继承中的类作用域
2017-12-24 22:32
375 查看
笔记会持续更新,有错误的地方欢迎指正,谢谢!
在函数体位置写上=0就可以将一个虚函数说明为纯虚函数,只能出现在类内部的虚函数声明语句处:
含有纯虚函数的类是抽象基类
struct默认公有继承,class默认私有继承;
派生类的访问说明符不影响派生类成员及友元直接访问基类成员,只会控制 派生类的派生类 对基类成员的访问权限。
某个类对其继承而来的成员的访问权限受到两个因素影响:
1.在基类中该成员的访问说明符
2.在派生类的派生列表中的访问说明符
另外,派生访问说明符对子孙也有影响:
所以,在类外,某代派生类对最老的那个基类的某个成员访问权限等于一路继承过来最低的权限。比如原来是protected,通过private继承后,那它就是private了。
总结:
友元关系不能传递、不能继承;
每个类负责控制各自成员的访问权限。
当存在继承关系时,派生类的作用域嵌套在其基类的作用域内。
若一个名字在派生类的作用域内无法正确解析,则编译器将继续在外层的基类作用域中寻找该名字的定义。
我们看一段之前的书店折扣类的代码:
我们来看看isbn是怎么解析的:
因为我们是通过Bulk_quote的对象来调用isbn的,所以首先在
加入基类和派生类的虚函数接受的实参不同,那我们就没办法通过基类的引用或指针来调用派生类的虚函数了。
能说出上面6个结果,说明你的虚函数、动态绑定、重载、作用域都OK了,加油!
虚函数
与普通函数一样,有个需要注意的地方是:既然默认实参可以在基类和派生类中都有,那我们最好让它们一致。实参值由本次调用的静态类型决定。抽象基类
纯虚函数
纯虚函数可清晰地告诉用户当前这个纯虚函数(net_price)是没实际意义的。在函数体位置写上=0就可以将一个虚函数说明为纯虚函数,只能出现在类内部的虚函数声明语句处:
//书店多种购书优惠政策问题 class Disc_quote : public Quote { public: Disc_quote() = default; //Quote(book, price):父类的变量由父类的构造函数初始化 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; //虚函数后面加=0是纯虚函数 protected: size_t quantity = 0; //折扣适用的购买量 double discount = 0; //表示折扣的小数值 };
含有纯虚函数的类是抽象基类
抽象基类:
含有或未经覆盖直接继承纯虚函数的类,负责定义接口给后续类覆盖。不能直接创建抽象基类的对象,但可定义抽象基类的派生类的对象,前提是这些派生类覆盖了纯虚函数net_price函数:
//假设Bulk_quote继承Disc_quote并已覆盖了net_price函数 Bulk_quote bulk; //正确:Bulk_quote中没有纯虚函数
Disc_quote的派生类必须给出继承的纯虚函数的定义(net_price),否则仍是抽象基类。
派生类构造函数只初始化它的直接基类
我们重新实现Bulk_quote,让它继承
Disc_quote而不是直接继承Quote,于是,在构造函数中只需调用它的直接基类的构造函数就好了,至于再往上的类,自有它儿子负责:
class Bulk_quote : public Disc_quote { public: Bulk_quote() = default; Bulk_quote(const string& book, double price, size_t qty, double disc) : //为Disc_quote的基类Disc_quote里的变量初始化 Disc_quote(book, price, qty, disc) {} double net_price(size_t) const override; };
访问控制与继承
由上,每个类分别控制自己成员的初始化过程,与之类似,每个类还分别控制着其成员对于派生类来说是否可访问。受保护成员:
类使用protected声明那些希望与派生类共享但不想被其他公共访问使用的成员。访问权限:
受基类中成员和派生列表中访问说明符共同影响;struct默认公有继承,class默认私有继承;
派生类的访问说明符不影响派生类成员及友元直接访问基类成员,只会控制 派生类的派生类 对基类成员的访问权限。
某个类对其继承而来的成员的访问权限受到两个因素影响:
1.在基类中该成员的访问说明符
private: char priv_mem;
2.在派生类的派生列表中的访问说明符
struct Pub_Derv : public Base{//省略派生类的代码}; //公有继承
另外,派生访问说明符对子孙也有影响:
struct DfP : public Pub_Derv { int use_base(){return prot_mem;} //正确:在派生类的派生列表中的访问说明符一路public。 }; struct DfPr : public Priv_Derv { int use_base(){return prot_mem;} //错误:DfPr的爷爷是private,连累了它。 };
所以,在类外,某代派生类对最老的那个基类的某个成员访问权限等于一路继承过来最低的权限。比如原来是protected,通过private继承后,那它就是private了。
友元与继承:朋友不能继承或传递
基类的友元在访问派生类成员如其他正常类一样访问,无优待,就像你爸爸的朋友访问你,你对待他像对待陌生人即可。类似的,派生类的友元也不能随意访问基类的成员,你爸爸对待你的朋友也像对待陌生人一样即可。class Base { //添加友元类,其他跟之前一样 firend class Pal; //友元类 }; class Pal { public: int f(Base b) {return b.prot_mem;} //正确:Pal是Base的友元 int f2(Sneaky s) {return s.j;} //错误:Pal不是Sneaky的友元(没有继承朋友关系) int f3(Sneaky s){return s.prot_mem;} //正确:访问基类的成员, //能不能被访问,基类说了算,由于基类Base和Pal是朋友,所以能被访问。 };
总结:
友元关系不能传递、不能继承;
每个类负责控制各自成员的访问权限。
继承中的类作用域
概述:
每个类定义自己的作用域,在这个作用域内我们定义类的成员。当存在继承关系时,派生类的作用域嵌套在其基类的作用域内。
若一个名字在派生类的作用域内无法正确解析,则编译器将继续在外层的基类作用域中寻找该名字的定义。
我们看一段之前的书店折扣类的代码:
Bulk_quote bulk; cout << bulk.isbn();
我们来看看isbn是怎么解析的:
因为我们是通过Bulk_quote的对象来调用isbn的,所以首先在
Bulk_quote中查找,没找到,又因为
Bulk_quote是
Disc_quote的派生类,所以接下来在
Disc_quote中找,没找到,接下来在
Quote中找,找到了。
名字冲突与继承:局部覆盖整体
隐藏:对于同名变量和同名函数,派生类作用域嵌套在基类作用域内,所以派生类中定义的成员隐藏同名的基类成员。虚函数与作用域
要求基类与派生类中的虚函数要有相同的形参列表的原因:加入基类和派生类的虚函数接受的实参不同,那我们就没办法通过基类的引用或指针来调用派生类的虚函数了。
class Base { public: int fcn(int) //与类中下面的虚函数同名,必须要形参不同才能重载 { cout << "Base不虚" << endl; } virtual int fcn() { cout << "Base虚" << endl; } }; class D1 : public Base { public: int fcn(int) //并不是虚函数,因为参数和基类不同 { cout << "D1不虚" << endl; } int fcn() //这才是虚函数,虽然没写override,更好的习惯是写上,这里为了迷惑你们就不写了 { cout << "D1虚" << endl; } }; int main() { Base bobj; D1 d1obj; Base *bp1 = &bobj; Base *bp2 = &d1obj; D1 *bp3 = &d1obj; //通过基类的指针来调用派生类的虚函数 bp2->fcn(); //D1虚 bp1->fcn(); //Base虚 bp3->fcn(); //D1虚 bp1->fcn(1); //Base不虚 bp2->fcn(1); //Base不虚 bp3->fcn(1); //D1不虚 return 0; }
能说出上面6个结果,说明你的虚函数、动态绑定、重载、作用域都OK了,加油!
相关文章推荐
- 《C++ Primer》读书笔记第十五章-3-构造函数和拷贝控制、容器与继承
- 读书笔记《C++ Primer》第五版——第十五章 面向对象程序设计
- 《C++ Primer》读书笔记——第十五章_面向对象程序设计_2
- C++ primer 这本书上有这么两句话“派生类虚函数调用基类版本时,必须显式使用作用域操作符。如果派生类函数忽略了这样做,则函数调用会在运行时确定并且将是一个自身调用,从而导致无穷递归。”
- C++ Primer 读书笔记 - 第十五章
- C++ Primer(第五版) 第十五章 友元和继承
- C++ primer(十三)--类继承、构造函数成员初始化、虚函数、抽象基类
- 读书笔记 effective c++ Item 36 永远不要重新定义继承而来的非虚函数
- 《C++ Primer》读书笔记第十五章-1-OOP概述、定义基类和派生类
- 32、C++ Primer 4th 笔记,多重继承与虚函数
- 继承-虚函数-作用域
- C++ primer(十三)--类继承、构造函数成员初始化、虚函数、抽象基类
- 《C++ Primer》读书笔记——第十五章_面向对象程序设计
- c++基础10:继承和派生 虚函数的作用 多态性概念 纯虚函数和抽象类的概念
- C++虚函数和虚继承浅析
- [置顶] 从零开始学C++之虚继承和虚函数对C++对象内存模型造成的影响(类/对象的大小)
- C++继承:同名隐藏、覆盖,虚函数
- 读书笔记 effective c++ Item 39 明智而谨慎的使用private继承
- C++ 继承,虚函数与多态性专题
- 《C++ Primer》读书笔记第三章-4-数组 And 多维数组