读书笔记_Effective_C++_条款三十四:区分接口继承和实现继承
这个条款书上内容说的篇幅比较多,但其实思想并不复杂。只要能理解三句话即可,第一句话是:纯虚函数只继承接口;第二句话是:虚函数既继承接口,也提供了一份默认实现;第三句话是:普通函数既继承接口,也强制继承实现。这里假定讨论的成员函数都是public的。
这里回顾一下这三类函数,如下:
class BaseClass
{
public:
void virtual PureVirtualFunction() = ; // 纯虚函数
void virtual ImpureVirtualFunction(); // 虚函数
void CommonFunciton(); // 普通函数
};
纯虚函数有一个“等于0”的声明,具体实现一般放在派生中(但基类也可以有具体实现),所在的类(称之为虚基类)是不能定义对象的,派生类中仍然也可以不实现这个纯虚函数,交由派生类的派生类实现,总之直到有一个派生类将之实现,才可以由这个派生类定义出它的对象。
虚函数则必须有实现,否则会报链接错误。虚函数可以在基类和多个派生类中提供不同的版本,利用多态性质,在程序运行时动态决定执行哪一个版本的虚函数(机制是编译器生成的虚表)。virtual关键字在基类中必须显式指明,在派生类中不必指明,即使不写,也会被编译器认可为virtual函数,virtual函数存在的类可以定义实例对象。
普通函数则是将接口与实现都继承下来了,如果在派生类中重定义普通函数,将会出现名称的遮盖(见条款33),事实上,也是极不推荐在派生类中覆盖基类的普通函数的,如果真的要这样做,请一定要考虑是否该把基类的这个函数声明为虚函数或者纯虚函数。
下面是三类成员函数的应用:
class BaseClass
{
public:
void virtual PureVirtualFunction() = ; // 纯虚函数
void virtual ImpureVirtualFunction(); // 虚函数
void CommonFunciton(); // 普通函数
};
void BaseClass::PureVirtualFunction()
{
cout << "Base PureVirtualFunction" << endl;
}
void BaseClass::ImpureVirtualFunction()
{
cout << "Base ImpureVirtualFunciton" << endl;
} class DerivedClass1: public BaseClass
{
void PureVirtualFunction()
{
cout << "DerivedClass1 PureVirturalFunction Called" << endl;
}
}; class DerivedClass2: public BaseClass
{
void PureVirtualFunction()
{
cout << "DerivedClass2 PureVirturalFunction Called" << endl;
}
}; int main()
{
BaseClass *b1 = new DerivedClass1();
BaseClass *b2 = new DerivedClass2();
b1->PureVirtualFunction(); // 调用的是DerivedClass1版本的PureVirtualFunction
b2->PureVirtualFunction(); // 调用的是DerivedClass2版本析PureVirtualFunction
b1->BaseClass::PureVirtualFunction(); // 当然也可以调用BaseClass版本的PureVirtualFucntion
return ;
}
书上提倡用纯虚函数去替代虚函数,因为虚函数提供了一个默认的实现,如果派生类的想要的行为与这个虚函数不一致,而又恰好忘记去覆盖虚函数,就会出现问题。但纯虚函数不会,因为它从语法上限定派生类必须要去实现它,否则将无法定义派生类的对象。
同时,因为纯虚函数也是可以有默认实现的(但是它从语法上强调派生类必须重定义之,否则不能定义对象),所以完全可以替换虚函数。
普通函数所代表的意义是不变性凌驾与特异性,所以它绝不该在派生类中被重新定义。
在设计类成员函数时,一般既不要将所有函数都声明为non-virtual(普通函数),这会使得没有余裕空间进行特化工作;也一般不要将所有函数都声明为virtual(虚函数或纯虚函数),因为一般会有一些成员函数是基类就可以决定下来的,而被所有派生类所共用的。这个设计法则并不绝对,要视实际情况来定。
最后总结一下:
1. 接口继承和实现继承不同。在public继承之下,derived class总是继承base class的接口;
2. pure virtual函数只具体指定接口继承;
3. impure virtual函数具体指定接口继承和缺省实现继承;
4. non-virutal函数具体指定接口继承以及强制性实现继承。
读书笔记_Effective_C++_条款三十四:区分接口继承和实现继承的更多相关文章
- 读书笔记_Effective_C++_条款三十二:确定你的public继承继承塑模出is-a关系
这一条款是说的是公有继承的逻辑,如果使用继承,而且继承是公有继承的话,一定要确保子类是一种父类(is-a关系).这种逻辑可能与生活中的常理不相符,比如企鹅是生蛋的,所有企鹅是鸟类的一种,直观来看,我们 ...
- 读书笔记_Effective_C++_条款三十:了解inline的里里外外
学过基本程序课的同学都知道,inline是内联的关键字,它可以建议编译器将函数的每一个调用都用函数本体替换.这是一种以空间换时间的做法.把每一次调用都用本体替换,无疑会使代码膨胀,但可以节省函数调用的 ...
- 读书笔记_Effective_C++_条款三十九:明智而审慎地使用private继承
private继承的意义在于“be implemented in turns of”,这个与上一条款中说的复合模型的第二层含义是相同的,这也意味着通常我们可以在这两种设计方法之间转换,但书上还是更提倡 ...
- 读书笔记_Effective_C++_条款三十六:绝不重新定义继承而来的non-virtual函数
这个条款的内容很简单,见下面的示例: class BaseClass { public: void NonVirtualFunction() { cout << "BaseCla ...
- 读书笔记_Effective_C++_条款二十四: 若所有参数皆需类型转换,请为此采用non-member函数
class A { private: int a; public: A(int x) :a(x){} A operator*(const A& x) { return A(a*x.a); } ...
- 读书笔记_Effective_C++_条款三十八:通过复合塑模出has-a或者is-implemented-in-terms-of
如果说public是一种is-a的关系的话,那么复合就是has-a的关系.直观来说,复合就是在一个类中采用其他类的对象作为自身的成员变量,可以举个例子,像下面这样: class Person { pr ...
- 读书笔记_Effective_C++_条款三十五:考虑virtual函数以外的其他选择
举书上的例子,考虑一个virtual函数的应用实例: class GameCharacter { private: int BaseHealth; public: virtual int GetHea ...
- 读书笔记_Effective_C++_条款二十九:为“异常安全”而努力是值得的
还是举书上的例子: void PrettyMenu::changeBackground(std::istream& imgSrc) { lock(&mutex); delete bgI ...
- 读书笔记_Effective_C++_条款三十七:绝不重新定义继承而来的缺省参数值
先看下面的例子: enum MyColor { RED, GREEN, BLUE, }; class Shape { public: ; }; class Rectangle: public Shap ...
随机推荐
- 七、springboot整合Spring-data-jpa
1.Spring Data JPA是什么 由Spring提供的一个用于简化JPA开发的框架.可以在几乎不用写实现的情况下,实现对数据的访问和操作.除了CRUD外,还包括如分页.排序等一些常用的功能 1 ...
- asp.net mvc 本地化 默认的错误提示
System.ComponentModel.DataAnnotations 给我们提供了一些特性来直接对model的属性进行验证和约束, 同时也提供了 ErrorMessageResourceName ...
- Isolate randomforest检测异常点的非监督方法
由于异常数据的两个特征(少且不同: few and different) 异常数据只占很少量; 异常数据特征值和正常数据差别很大. iTree的构成过程如下: l 随机选择一个属性Attr: l ...
- ASP .NET Core 2.0 MVC 发布到 IIS 上以后 无法下载apk等格式的文件
ASP .NET Core MVC 发布到 IIS 上以后 无法下载apk等格式的文件 使用.NET Core MVC创建了一个站点,其他文件可以下载,但是后来又需求,就把手机端的apk合适的文件上 ...
- redis入门——redis常用命令
http://blog.csdn.net/wclxyn/article/details/8449082 https://jingyan.baidu.com/article/90bc8fc87ce8e2 ...
- elasticsearch5.5
1.不能以root用户运行 groupadd es #增加es组 useradd es -g es -p pwd #增加es用户并附加到es组 chown -R e ...
- 如何解决谷歌Chrome浏览器空白页的问题
如何解决谷歌Chrome浏览器空白页的问题 谷歌Chrome浏览器突然不打开任何网页,无论是任何站点(如http://www.baidu.com), 还是Chrome浏览器的设置页面(chrome ...
- bzoj 3676 后缀自动机+马拉车+树上倍增
思路:用马拉车把一个串中的回文串个数降到O(n)级别,然后每个串在后缀自动机上倍增找个数. #include<bits/stdc++.h> #define LL long long #de ...
- jenkins升级
1丶查看war包所在的目录 #rpm -ql jenkins /etc/init.d/jenkins //jenkins的启动文件 /etc/logrotate.d/jenkins //日志切割 /e ...
- 使用Merge存储引擎实现MySQL分表
一.使用场景 Merge表有点类似于视图.使用Merge存储引擎实现MySQL分表,这种方法比较适合那些没有事先考虑分表,随着数据的增多,已经出现了数据查询慢的情况. 这个时候如果要把已有的大数据量表 ...