条款32:确定你的 public 继承塑模出 is-a 关系

public inheritance 意味着 is-a 关系
class Derived 以 public 形式继承 class Base, 则每一个 Derived对象同时也是一个 Base对象,反之不成立
可以施行于Base class 对象身上的每件事情,也同样可以施行于Derived class 对象

class Person{...};
class Student : public Person{...};

每一个Student都是Person,反之,每个Person不一定是Student
正因为具有 is-a 关系,指向Base类型的引用可以引用Base类型对象,也可以引用Derived类型对象(多态基础)

条款33:通过组合塑模出 has-a 关系
class Address{...};
class PhoneNumber{...};
class Person{
public:
...
private:
std::string name;
Address address;
PhoneNumber phoneNumber;
};

string对象、Address对象、PhoneNumber对象组合塑模出Person对象
Person class 示范 has-a 关系:每一个Person都 has-a string name、has-a Address、 has-a PhoneNumber

条款34:避免遮掩继承而来的名称

内层作用域的名称会遮蔽外围作用域的名称

int x;            //global变量
void func() {
double x; //local变量
}

class Base {
private:
int x;
public:
virtual void mf1() = ;
virtual void mf2();
void mf3();
}; class Derived : public Base {
public:
virtual void mf1();
void mf4();
};

如果 Derived 中mf4实现如下:

void Derived::mf4() {
mf2();
...
}

名称查找规则(作用域由小到大): Derived mf4 ==> class Derived ==> class Base ==> namespace ==> global

class Base {
private:
int x;
public:
virtual void mf1() = ;
virtual void mf1(int);
virtual void mf2();
void mf3();
void mf3(double);
}; class Derived :public Base {
public:
virtual void mf1();
void mf3();
void mf4();
};

class Base 中 mf1() 和 mf3() 被 Derived 中 mf1() 和 mf3() 遮蔽

Derived d;
int x;
d.mf1(); //没问题,调用 Derived::mf1
d.mf1(x); //错误!因为 Derived::mf1 遮蔽了 Base::mf1
d.mf2(); //没问题,调用 Base::mf2
d.mf3(); //没问题,调用 Derived::mf3
d.mf3(x); //错误!因为 Derived::mf3 遮蔽了 Base::mf3

如果继承 class Base 并加上重载函数,又希望重新定义其中一部分,那么必须为那些原本会被遮蔽的每个名称都引入一个 using 声明,强制声明作用域

class Base {
private:
int x;
public:
virtual void mf1() = ;
virtual void mf1(int);
virtual void mf2();
virtual void mf3();
virtual void mf3(double);
}; class Derived : public Base{
public:
using Base::mf1;
using Base::mf2;
virtual void mf1();
void mf3();
void mf4();
};

Derived d;
int x;
d.mf1(); //没问题,调用 Derived::mf1
d.mf1(x); //没问题,调用 Base::mf1
d.mf2(); //没问题,调用 Base::mf2
d.mf3(); //没问题,调用 Derived::mf3
d.mf3(x); //没问题,调用 Base::mf3

为了让被 base class 遮蔽的名称重见天日,可使用 using 声明式

条款35:区分接口继承和实现继承
class Shape {
public:
virtual void draw() const = ;
virtual void error(const std::string& msg);
int objectID() const;
};
class Rectangle : public Shape {...};
class Elllipse : public Shape {...};

成员函数的接口总是被继承,因为 public 继承意味着 is-a,所以对base class 为真的任何事情一定也对其 derived class 为真

.pure virtual member function:
pure virtual 函数有两个最突出的特性:
a.必须被任何"继承了它们"的 class 重新声明
b.它们在 抽象基类里面通常没有定义

声明一个 pure virtual 函数是为了让 derived class 只继承 函数接口

Shape* ps = new Shape;        //错误!抽象基类不能实例化
Shape* ps1 = new Rectangle; //基类指针指向派生类对象
ps1->draw(); //多态:调用 Rectangle::draw Shape* ps2 = new Elllipse; //基类指针指向派生类对象
ps2->draw(); //多态:调用 Elllipse::draw ps1->Shape::draw(); //调用 Shape::draw
ps2->Shape::draw(); //调用 Shape::draw
.简朴的 non-pure virtual member function:
声明简朴的 non-pure virtual 函数是为了让 derived class 继承 函数接口 和 函数缺省实现

a.函数为 virtual 函数,表明 继承base class 的函数接口
b.函数为 non-pure 函数,表明 base class 可以为该函数提供一个缺省实现,并且让 derived class 继承该函数缺省实现
Shape::error 声明式(non-pure virtual function)是告诉 derived class 的设计者"你必须提供一个 error 函数,但如果你不想自己写一个,可以使用

Shape class 提供的缺省版本"
基类中的所有 non-pure virtual 函数都必须提供一个 缺省实现(可以为空函数体),否则编译错误

. non-virtual member function:
声明 non-virtual 函数是为了让 derived class 继承 函数接口 和 函数强制性实现(注意区别于 函数缺省实现)

实际上 non-virtual 函数表现出 不变性(所有 derived class 都不能重定义该函数,并且都共享 base class 的同一份强制性函数实现)

总结:
设计一个 class 的API函数原型时,必须谨慎选择:
()pure virtual member function 继承base class函数接口(derived class 必须重定义该函数)
()non-pure virtual member function 继承base class函数接口和函数缺省实现(derived class 没有重定义该函数时,会执行继承而来的缺省实现)
()non-virtual member function 继承base class函数接口和函数强制性实现(derived class 不能重定义该函数,都共享继承而来的强制性实现)
条款36:考虑 virtual 函数以外的其他选择

设计某个方法时,可以考虑 virtual 函数以外的选择方案,这里主要采用 std::function 结合 std::bind 替换 virtual 函数

条款37:绝不重定义继承而来的 non-virtual 函数
条款38:绝不重定义继承而来的 缺省参数值
class Base {
public:
virtual void func() const = ;
}; class Derived : public Base {
public:
virtual void func() const;
};
Base* pBase = new Base;
Base* pDerived = new Derived;

(1)对象的静态类型:对象在程序中被声明时所采用的类型
pBase声明为 Base*,所以 pBase 静态类型为 Base*(不论真正指向的对象类型)
pDerived声明为 Base*,所以 pDerived 静态类型为 Base*(不论真正指向的对象类型)

(2)对象的动态类型:目前真正所指对象的类型
pBase真正指向的对象类型为 Base*,所以 pBase 动态类型为 Base*
pDerived真正指向的对象类型为 Derived*,所以 pDerived 动态类型为 Derived*

virtual 函数系动态绑定而来,最终调用哪一份函数实现代码,取决于发出调用的那个对象的动态类型

virtual 函数是动态绑定, 缺省参数值是静态绑定,重定义缺省参数值会导致致命的语义错误.

条款39:尽量慎用 private 继承(采用组合)

Base class 里的所有成员通过 private 继承之后, 在 Derived class 中都会变成 private 属性

条款40:禁用 多重继承 和 virtual 继承

采用组合的替代方案, 多重继承和 virtual 继承会导致语义歧义和非常大的复杂度

注:Google c++ 编程规范:所有继承都必须是 public,否则采用组合

EC++学习笔记(六) 继承和面向对象设计的更多相关文章

  1. Effective C++笔记:继承与面向对象设计

    关于OOP 博客地址:http://www.cnblogs.com/ronny 转载请注明出处! 1,继承可以是单一继承或多重继承,每一个继承连接可以是public.protected或private ...

  2. java之jvm学习笔记六-十二(实践写自己的安全管理器)(jar包的代码认证和签名) (实践对jar包的代码签名) (策略文件)(策略和保护域) (访问控制器) (访问控制器的栈校验机制) (jvm基本结构)

    java之jvm学习笔记六(实践写自己的安全管理器) 安全管理器SecurityManager里设计的内容实在是非常的庞大,它的核心方法就是checkPerssiom这个方法里又调用 AccessCo ...

  3. Typescript 学习笔记六:接口

    中文网:https://www.tslang.cn/ 官网:http://www.typescriptlang.org/ 目录: Typescript 学习笔记一:介绍.安装.编译 Typescrip ...

  4. c++学习笔记之继承篇

    title: c++学习笔记之继承篇 date: 2017-03-26 16:36:33 tags: [c++,继承,public,virtual,private,protected] categor ...

  5. EffectiveC++ 第6章 继承与面向对象设计

    我根据自己的理解,对原文的精华部分进行了提炼,并在一些难以理解的地方加上了自己的"可能比较准确"的「翻译」. Chapter 6 继承与面向对象设计 Inheritance and ...

  6. JavaSE学习笔记(3)---面向对象三大特性

    JavaSE学习笔记(3)---面向对象三大特性 面向对象的三大特征:继承.封装.多态 1.封装 面向对象编程语言是对客观世界的模拟,客观世界里成员变量都是隐藏在对象内部的,外界无法直接操作和修改.然 ...

  7. [java学习笔记]java语言核心----面向对象之this关键字

    一.this关键字 体现:当成员变量和函数的局部变量重名时,可以使用this关键字来区别:在构造函数中调用其它构造函数 原理:         代表的是当前对象.         this就是所在函数 ...

  8. Learning ROS for Robotics Programming Second Edition学习笔记(六) indigo xtion pro live

    中文译著已经出版,详情请参考:http://blog.csdn.net/ZhangRelay/article/category/6506865 Learning ROS for Robotics Pr ...

  9. python3.4学习笔记(六) 常用快捷键使用技巧,持续更新

    python3.4学习笔记(六) 常用快捷键使用技巧,持续更新 安装IDLE后鼠标右键点击*.py 文件,可以看到Edit with IDLE 选择这个可以直接打开编辑器.IDLE默认不能显示行号,使 ...

随机推荐

  1. JavaWeb项目实现图片验证码

    一.什么是图片验证码? 可以参考下面这张图: 我们在一些网站注册的时候,经常需要填写以上图片的信息. 这种图片验证方式是我们最常见的形式,它可以有效的防范恶意攻击者采用恶意工具,调用“动态验证码短信获 ...

  2. off-by-one&doublefree. 看雪10月ctf2017 TSRC 第四题赛后学习

    off-by-one 0x00 发现漏洞 1.off-by-one 在massage函数中,如图所示,可以修改的字节数比原内存大小多了一个字节 2.悬挂指针 可以看到,在free堆块的时候,没有清空指 ...

  3. 身份证号正则校验(js校验+JAVA校验)

    js校验身份证号[15位和18位] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 3 ...

  4. ios之UITextView

    我们计划创建UITextView,实现UITextViewDelegate协议方法,使用NSLog检查该方法何时被调用.我们还会接触到如何在TextView中限制字符的数量,以及如何使用return键 ...

  5. ABAQUS用户子程序一览表

    说明 ABAQUS用户子程序一览表 ABAQUSStandard subroutines Refence 说明 本系列文章本人基本没有原创贡献,都是在学习过程中找到的相关书籍和教程相关内容的汇总和梳理 ...

  6. linux设备驱动程序 - 待解决问题记录

    1.每个模式都有自己的内存映射,也即自己的地址空间?(P26) http://www.cnblogs.com/wuchanming/p/4360277.html (不知道是不是,没时间看)

  7. python爬虫基础03-requests库

    优雅到骨子里的Requests 本文地址:https://www.jianshu.com/p/678489e022c8 简介 上一篇文章介绍了Python的网络请求库urllib和urllib3的使用 ...

  8. MySQL 之视图、 触发器、事务、存储过程、内置函数、流程控制、索引

    本文内容: 视图 触发器 事务 存储过程 内置函数 流程控制 索引 ------------------------------------------------------------------ ...

  9. python--MySQL权限管理 数据备份还原

    一 权限管理 mysql最高管理者是root用户, 这个一般掌握在公司DBA手里, 当你想去对数据库进行一些操作的时候,需要DBA授权给你. 1. 对新用户增删改 1. 创建用户 # 要先use my ...

  10. python 列表(增删改查)

    列表 :(列表可以嵌套,列表的中的元素可以为任意) 列表的创建:1.   a = [1, 2, 3] 2.   a = list([1, 2, 3]) 1.查: 索引(下标),都是从0开始 切片 .c ...