1.确定你的public继承塑造出is-a关系

public继承意味着is-a.适用于base classes身上的每一件事情一定也适用于derived classes身上,因为每一个derived classes对象也都是一个base class对象,反过来不成立。

2.避免隐藏继承而来的名称

c++名称查找不考虑类型,只考虑名称。

class Base
{
private:int x;
public:virtual void mf1() = 0;virtual void mf1(int){}virtual void mf2(){}void mf3(){}void mf3(double){}
};
class Derived :public Base
{
public:virtual void mf1(){}void mf3(){}void mf4(){}
};
/*
base class内所有名为mf1和mf3的函数都被derived class内的mf1和mf3函数遮掩掉
函数隐藏只与函数名称有关,与函数的参数类型、是否virtual无关,注意此处是静态绑定若通过指针或引用来调用虚函数,此时是动态绑定,不会发生函数隐藏
*/
int main()
{Derived d;int x = 1;d.mf1();//ok//d.mf1(x);//falsed.mf2();//okd.mf3();//ok//d.mf3(x);//false
system("pause");return 0;
}

class Base
{
private:int x;
public:virtual void mf1() = 0;virtual void mf1(int){}virtual void mf2(){}void mf3(){}void mf3(double){}
};class Derived :public Base
{
public:using Base::mf1;using Base::mf3;virtual void mf1(){}void mf3(){}void mf4(){}
};/*
如果你继承base class并且base class中有重载函数,而你又希望在派生类中重新定义其中一部分,那么你必须为那些
原本会被隐藏的每个名称引入一个using声明式,否则某些你希望继承的名称会被隐藏。using声明式会令继承而来的基类中某给定名称之所有同名函数在derived class中都可见
*/
int main()
{Derived d;int x = 1;d.mf1();//okd.mf1(x);//okd.mf2();//okd.mf3();//okd.mf3(x);//ok
system("pause");return 0;
}

3.区分接口继承和实现继承

  • 声明一个pure virtual函数的目的是为了让derived classes只继承函数接口

通常纯虚函数没有定义,但是可以为纯虚函数提供定义,调用它的唯一途径是调用时明确指出其class名称

class Shape
{
public:virtual void draw() const = 0{}virtual void error(const string& msg);int objectID() const;
};class Rectangle:public Shape
{
public:virtual void draw() const{}
};class Ellipse:public Shape
{
public:virtual void draw() const {}
};int main()
{//Shape *ps = new Shape;//error 抽象类不能生成实例Shape *ps1 = new Rectangle;ps1->draw();Shape *ps2 = new Ellipse;ps2->draw();ps1->Shape::draw();ps2->Shape::draw();system("pause");return 0;
}

  • 声明impure virtual函数的目的,是让derived class继承该函数的接口和缺省实现

如果有的派生类只想继承接口但忘记重新定义该虚函数,这样一来就会使用基类缺省实现,出错:将接口和缺省实现分开,派生类如果想使用缺省实现,需要去显式调用

方案一:将接口定义为纯虚函数,缺省实现定义为non-virtual函数,派生类若想使用缺省实现,需要在继承而来的纯虚函数中调用该non-virtual函数,派生类若只想继承接口,此时不会忘记重新定义该虚函数了,因为不重新定义的话就是抽象类,无法实例化

方案二:纯虚函数必须在derived class中重新定义,但基类中也可以为纯虚函数提供定义,该定义作为缺省实现。派生类若想使用缺省实现,需要在在继承而来的纯虚函数中通过基类名来显式调用上述函数定义

  • 声明non-virtual函数的目的是为了令derived class继承函数的接口及一份强制性实现

4.考虑virtual函数以外的其他选择

  • NVI手法:令用户通过public non-virtual成员函数间接调用private virtual函数
  • 由Function Pointer实现strategy模式

运用函数指针(作为类的成员变量)替换virtual函数,优点是每个对象可各自拥有自己的对应函数及可在运行期改变该函数,缺点是可能会降低类的封装性

  • 由tr1::function完成strategy模式
  • 古典的strategy模式

5.绝不重新定义继承而来的non-virtual函数(设计理念)

6.绝不重新定义继承而来的缺省参数值

虚函数是动态绑定的,而缺省参数值是静态绑定的。

class Shape
{
public:enum ShapeColor{Red,Green,Blue};virtual void draw(ShapeColor color = Red) const = 0;
};class Circle :public Shape
{
public:/*当用户以对象调用此函数,一定要指定参数值。因为静态绑定下这个函数并不从其基类继承缺省参数值。但若以指针或引用调用此函数,可以不指定参数值,因为动态绑定下这个函数会从其基类继承缺省参数值*/virtual void draw(ShapeColor color) const;
};

7.通过复合composition塑造出has-a或"根据某物实现出"

复合:某种类型的对象内含别的类型的对象,复合意味着has-a或is-implemented-in-terms-of

8.明智地使用private继承

  • 如果class之间的继承关系是private,编译器不会自动将一个derived class对象转换为一个base class对象(将派生类实参传递给基类形参,编译出错)
  • 由private base class继承而来的所有成员,在derived class中都会变成private属性,纵使它们在base class中原本是protected或public属性,即派生类对象不能调用基类方法

private继承意味着implemented-in-terms-of,private继承纯粹只是一种实现技术。

9.明智地使用多重继承

未完待续

转载于:https://www.cnblogs.com/ljygoodgoodstudydaydayup/p/5819125.html

【effective c++】继承与面向对象设计相关推荐

  1. C++进阶_Effective_C++第三版(六) 继承与面向对象设计 Inheritance and Object-Oriented Design

    继承与面向对象设计 Inheritance and Object-Oriented Design 面向对象编程已经风靡编程界,关于继承.派生.virtual函数等等需要深入了解. 32.确定你的pub ...

  2. 《Effective C++》第三版 第六章 继承与面向对象设计 32~35条例

    文章目录 条款32:确定你的 `public` 继承塑膜出 is-a 关系 故事引入规则 案例说明 小结上代码 公有继承用法 企鹅不会飞 企鹅会飞,但那是错的! 总结 请记住 条款33:避免遮掩继承而 ...

  3. Effective C++ --6 继承与面向对象设计

    上一篇Effective C++ --5 实现 32.确定你的public继承塑模出is-a关系 (1)public 继承意味着is-a.适用于base class身上的每一件事一定也适用于deriv ...

  4. Effective C++: 06继承与面向对象设计

    32:确定你的public继承塑模出is-a关系 以C++进行面向对象编程,最重要的一个规则是:public继承表示的是"is-a"(是一种)的关系. 如果令class D以pub ...

  5. (6)继承与面向对象设计- Effective C++改善程序与设计的55个具体做法(Effective C++: 55 Specific Ways to Improve Your Programs)

    文章目录 32. 确定你的public继承塑模出is-a关系(Make sure public inheritance models "is-a") 33. 避免遮挡继承而来的名称 ...

  6. 《Effective C++ 3th》——继承与面向对象设计

    文章目录 Is A 确定你的public继承塑模出is-a关系 避免遮掩继承而来的名称 区分接口继承和实现继承 考虑virtual函数以外的其他选择 绝不重新定义继承而来的non-virtual函数 ...

  7. uml图中的各种箭头_设计模式学习笔记(二):UML与面向对象设计原则

    1 UML 1.1 UML UML(Unified Modeling Language)是统一建模语言,1997年11月UML1.1版本提交给OMG并正式通过,成为建模语言的个那个也标准.2003年6 ...

  8. C++面向对象设计原则详解

    概述 C++面向对象设计原则主要包括以下几点: 依赖倒置原则 开放封闭原则 单一职责原则 里氏替换原则 接口隔离原则 封装变化点原则 面向接口编程原则 优先使用对象组合,而不是类继承 接下来详细的分析 ...

  9. 7.11 其他面向对象设计原则2:能用组合的地方,不要用继承

    其他面向对象设计原则2  能用组合的地方,不要用继承 Favor object composition over class inheritance 6.1 代码重用的两种方式  能用组合的地方不 ...

最新文章

  1. WIP模块常用表结构
  2. php 设置时区_为什么没有 Asia/Beijing 时区?
  3. KVM虚拟机的优化历程---按需优化
  4. IT十八掌作业_java基础第二天_进制转换原理和补码存储方式
  5. python 实现文本自动翻译功能
  6. select into from和insert into select
  7. 从底层重学 Java 之两大浮点类型 GitChat连接
  8. TensorFlow2.0(二)--Keras构建神经网络分类模型
  9. 动态ip软件win7_IPXE+ISCSI Target安装WIN7
  10. 局域网内如何设置多个ip地址分配
  11. Vue在HTML模板中插入JS数据
  12. Vulkan入门(一)-环境配置.md
  13. WebX框架解析及使用教程
  14. 从零开始搭建EasyDarwin环境——Windows系统开发环境Golang的搭建
  15. ckplayer html播放本地,vue中使用ckplayer播放器
  16. Android .9图
  17. HTML-简单表格制作
  18. 报时功能_厦门宝藏 | 海关大钟悠扬的鸣曲报时,承载着老厦门人无数的记忆!...
  19. 保研面试/考研复试线性代数问题整理
  20. 可以在linux下运行的u盘制作工具,启动U盘创建工具(LiLi USB Creator)

热门文章

  1. TensorFlow:实战Google深度学习框架(六)图像数据处理
  2. java比ios慢_Android为什么比iOS慢
  3. python 接口测试 url_Python 接口测试之接口请求方法封装
  4. python大概学多久_自学Python要学多久可以学会?
  5. linux mkdir绝对路径,linux学习(六)绝对路径、相对路径、cd、mkdir、rmdir、rm(示例代码)...
  6. python网站设计理念_简单介绍下python Django框架的历史,设计理念及优势_Django讲解2...
  7. scrapy如何指定生成python3的项目_新手学习scrapy的坑(都是泪)
  8. 神雕侠侣2服务器维护,《神雕侠侣2》手游10月22日停服维护公告
  9. Java生鲜电商平台-用户管理的架构与实战
  10. SpringBoot操作MongoDB实现增删改查