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

  • public继承意味着is-a。适用于base class身上的每一个函数也一定适用于derived class。

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

几层作用域:

  • global作用域

    • namespace作用域

      • Base class作用域

        • Drive class作用域

          • 成员函数

            • 控制块作用域

当前作用域会遮掩上一层作用域的名称(重载的函数也会直接遮掩)

class Base{
public:
void f1();
} class Drive : public Base {
public:
void f1(int); //会遮掩f1(),子类并没有继承f1()
} Drive d;
d.f1(); //错误
d.f1(3); //正确

可以通过using声明式(public继承)或者inline转交(public和private继承)解决这一问题

class Base{
public:
void f1();
} //using 声明式
class Drive:public Base {
public:
using Base::f1;
void f1(int);
} //inline转交
class Drive : private Base {
public:
void f1(){
Base::f1();
}
void f1(int);
}

条款34:区分接口继承和实现继承

  • 纯虚函数(只提供接口)

    • 纯虚函数造成了抽象类,抽象类不可以构造实体(对象)
    • Drived class中必须给出纯虚函数的实现
    • 纯虚函数可以给出实现(类外)
  • 虚函数(提供接口和缺省实现)

    • 用于实现多态
  • 非虚函数(提供了强制实现)

    • 可以看成此类独有,且最好不要在Drived class重新定义非虚函数

条款35:考虑virtual函数以外的其他选择

virtual的替代方案

  • non-virtual interface(NVI)手法,以public non-virtual成员函数包裹较低访问性(privat或protected)的virtual函数
#include <iostream>
#include <string>
#include <memory> using namespace std; class base {
public:
void show() {
doshow();
} private:
virtual void doshow() const {
cout << "base" << endl;
}
}; class drive : public base {
public: private:
virtual void doshow() const {
cout << "drive" << endl;
} }; int main() {
shared_ptr<base> d = shared_ptr<base>(new drive);
d->show();
}
  • 以函数指针变量替换virtual函数
#include <iostream>
#include <string>
#include <memory> using namespace std; class base {
typedef void (*showFuc) (const base&);
public:
explicit base(showFuc _s) : s(_s){}
void doshow() const {
cout << "base" << endl;
}
int show() const {
s(*this);
} private:
showFuc s;
}; void showFuc1(const base& b) {
cout << "function 1: ";
b.doshow();
} void showFuc2(const base& b) {
cout << "function 2: ";
b.doshow();
} int main() {
shared_ptr<base> b1 = shared_ptr<base>(new base(showFuc1));
shared_ptr<base> b2 = shared_ptr<base>(new base(showFuc2));
b1->show();
b2->show();
}
  • 利用std::function成员变量替换virtual函数(可将函数写在类中)
#include <iostream>
#include <string>
#include <memory> using namespace std; class base1 {
public:
explicit base1(function<void(const base1&)> _f) : f(_f){} void doshow() const {
cout << "base1" << endl;
} void show() const {
f(*this);
} private:
function<void(const base1&)> f;
}; class base2 {
public:
void baseFuc(const base1& b) {
b.doshow();
}
}; int main() {
shared_ptr<base2> b2;
shared_ptr<base1> b1 = shared_ptr<base1>(new base1(bind(&base2::baseFuc, b2, placeholders::_1)));
b1->show();
}
  • 古典的Strategy设计模式(将virtual函数替换为另一个继承体系内的virtual函数)
#include <iostream>
#include <string>
#include <memory> using namespace std; class base2; class base1 {
public:
base1(const shared_ptr<base2>& _b) : b(_b) {} void doshow() const;
void show() const ;
private:
shared_ptr<base2> b;
}; class base2 {
public:
virtual void baseFuc(const base1& b) {
b.doshow();
}
}; void base1::doshow() const {
cout << "base1" << endl;
} void base1::show() const {
b->baseFuc(*this);
} int main() {
shared_ptr<base2> b2 = shared_ptr<base2>(new base2());
shared_ptr<base1> b1 = shared_ptr<base1>(new base1(b2));
b1->show();
}

条款36:绝不重新定义继承而来的non-virtual函数

条款37:绝不要重新定义继承而来的缺省参数值

  • 缺省参数值是静态绑定
  • 虚函数是动态绑定
class Base{
public:
virtual void print(int a = 1) {cout <<"Base "<< a <<endl;};
int a;
}; class Drive : public Base{
public:
void print(int a = 2){cout << "Drive " << a <<endl;}
}; int main(){
Base *b = new Drive;
b->print(); // vptr[0](1)
} //Drive 1

条款38:通过复合塑模树has-a 或“根据某物实现出”

  • 复合(composition)

    • has-a(应用域对象间复合)

      • 应用域(程序中塑造的某些事物,如人、汽车、视频画面等等)
    • is-implemented-in-terms-of(实现域对象间复合)
      • 实现域(实现细节的人工制品,如缓冲区、互斥器、查找树等等)

条款39:明智而审慎的使用private继承

  • private继承(不继承接口,但继承实现)

    • 编译器不会自动将一个derived class对象转换为base class对象
    • base class的所有成员在derived class中都变成private属性
    • private继承可以造成empty class最优化(这对致力于"对象尺寸最小化"的程序开发者而言,可能很重要)
class Base{
public:
void fun(){}
} //8个字节
class Object{
private:
int a;
Base b;
}; //4个字节
class Object : private Base{
private:
int a;
}

条款40:明智而审慎的使用多重继承

  • 多重继承可能导致歧义(菱形继承)
class A { ... };
class B: public A { ... };
class C: public A { ... };
class D: public B, public C { ... };
//D中会包含两份A的成员变量
  • 采用virtual继承解决歧义

    • virtual继承会增加大小、速度、初始化及赋值等等成本
    • 如果base class不带有任何数据,virtual继承比较有实用价值
class A { ... };
class B: virtual public A { ... };
class C: virtual public A { ... };
class D: public B, public C { ... };
//D中只包含一份A的成员变量
  • 有一些情况可采用 "public继承抽象class(继承接口)" 和 "private继承继承协助实现class(继承实现)" 两相结合的方法

《Effective C++》继承与面对对象设计:条款32-条款40的更多相关文章

  1. C语言面对对象设计模式汇编

    面向对象发展到今天,已经出现了许许多多优秀的实践.方法和技术.很多的技术都能够有效的提高软件质量.IBM上的<面向对象软件开发和过程>系列文章对面对对象设计从如下层面进行了详细的介绍:代码 ...

  2. Effective C++ —— 继承与面向对象设计(六)

    条款32 : 确定你的public继承塑模出is-a关系 以C++进行面向对象编程,最重要的一个规则是:public inheritance(公开继承)意味“is-a”(是一种)的关系.请务必牢记.当 ...

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

    条款32:确定你的public继承塑模出is-a关系 以public继承的类,其父类的所有的性质都应该使用与子类,任何需要父类的地方都应该能用子类来代替,任何子类类型的对象也同时是父类的: class ...

  4. 16、python面对对象之类和继承

    前言:本文主要介绍python面对对象中的类和继承,包括类方法.静态方法.只读属性.继承等. 一.类方法 1.类方法定义 使用装饰器@classmethod装饰,且第一个参数必须是当前类对象,该参数名 ...

  5. 《Effective C++》第4章 设计与声明(1)-读书笔记

    章节回顾: <Effective C++>第1章 让自己习惯C++-读书笔记 <Effective C++>第2章 构造/析构/赋值运算(1)-读书笔记 <Effecti ...

  6. java基础--面对对象

    面对对象--概述 什么是对象? +---->对象可以泛指一切现实中存着的事物 +---->类是对象的抽象集合 什么是面对对象? +--->万物皆对象,面对对象实际就是人与万物接触== ...

  7. python面对对象

    面对过程VS面对对象 面向过程的程序设计的核心是过程,过程就是解决问题的步骤,面向过程的设计就是考虑周全什么时候处理什么东西 优点:极大的降低了写程序的复杂度,只需要顺着要执行的步骤,堆叠代码即可. ...

  8. 《Effective C++》第4章 设计与声明(2)-读书笔记

    章节回顾: <Effective C++>第1章 让自己习惯C++-读书笔记 <Effective C++>第2章 构造/析构/赋值运算(1)-读书笔记 <Effecti ...

  9. Python面对对象相关知识总结

    很有一段时间没使用python了,前两天研究微信公众号使用了下python的django服务,感觉好多知识都遗忘了,毕竟之前没有深入的实践,长期不使用就忘得快.本博的主要目的就是对Python中我认为 ...

随机推荐

  1. python3中time模块与datetime模块的简单用法

    __author__ = "JentZhang" import time # Timestamp 时间戳 print("Timestamp 时间戳:") pri ...

  2. Java 由浅入深GUI编程实战练习(三)

    一,項目介紹 1.可以查看年,月,日等功能.能获取今天的日期,并且能够通过下拉年,月的列表. 2.当程序运行时,显示的时间是系统当前时间. 3.可以手动输入时间,确定后系统跳转到制定的时间. 4.提供 ...

  3. vue项目中vscode格式化配置和eslint配置冲突

    问题描述 使用vscode开发vue项目的时候,从远端拉下一个新的项目后,安装完依赖后跑起项目时,发现直接报了一堆语法错误:包括换行.空格.单双引号.分号等各种格式问题 因为我的 vscode 安装使 ...

  4. opencv3.2.0图像处理之高斯滤波GaussianBlur API函数

    /*高斯滤波:GaussianBlur函数 函数原型: void GaussianBlur( InputArray src, OutputArray dst, Size ksize, double s ...

  5. Android为TV端助力linux命令

    从命令行push到系统目录,用户组是root,而代码里面的是个人用户组,所以要把你push进去的东西改成跟你APK一样的用户组,并且chmod -R 777 文件名修改文件的权限 修改用户组chown ...

  6. 2019Java查漏补缺(三)

    1.为什么这个public的类的类名必须和文件名相同    是为了方便虚拟机在相应的路径中找到相应的类所对应的字节码文件    2.java8 的一些新特性:     3: 数据库隔离级别 隔离级别 ...

  7. 电脑一键U盘启动快捷键

    下面是我特意列出的品牌电脑.笔记本电脑.组装电脑一键U盘启动快捷键对应列表,仅供大家查阅参考! [品牌-笔记本电脑] 笔记本品牌  启动按键 联想笔记本  F12 宏基笔记本  F12 华硕笔记本   ...

  8. AlwaysOn配置时在连接步骤时报错(35250)

    1.错误描述 1XX.XXX.XXX.241(主节点) 1XX.XXX.XXX.242(从节点) 添加节点需要在主节点上执行的,错误代码:35250 报错截图 2.网上相关介绍都是怀疑端口5022的问 ...

  9. AngularJS学习之旅—AngularJS Table(十一)

    1.AngularJS 表格 1. ng-repeat 指令可以完美的显示表格 AngularJS 实例 <!DOCTYPE html> <html> <head> ...

  10. mysql免安装版初次使用

    在自己电脑上安装一个mysql数据库并启动,碰到一些问题,总结一下 1.下载免安装版mysql数据库,百度下载了了5.7.25版本 2.在bin文件夹下找到my-defaults.ini文件,我这没有 ...