C++ 全栈知识体系C++ 全栈知识体系
✿导航
  • 基础
  • 函数
  • 知识点
  • IO框架
  • 新版本特性
  • 数据库原理
  • SQL语言
  • SQL - MySQL
  • NoSQL - Redis
  • NoSQL - ElasticSearch
  • 算法基础
  • 常见算法
  • 领域算法
  • 分布式算法
  • 数据结构与算法
  • 计算机网络
  • 操作系统
  • 计算机组成
  • 开发
  • 测试
  • 架构基础
  • 分布式系统
  • 微服务
  • 中间件
  • 概念
  • 理论
  • 架构设计原则
  • 设计模式
  • 协议
  • 技术选型
  • 编码规范
  • 流水线构建 - CI/CD
  • 知识点 - Linux
  • 网站 - Nginx
  • 容器化 - Docker
  • 容器编排 - Kubernetes
  • 服务网格 - Service Mesh Istio
  • 常用快捷键 - Shortcut
  • 工具使用 - Tools
  • 开源项目
  • 学习项目
  • 个人项目
  • 项目开发
  • 项目Idea
  • 并发
  • 部署
  • 分布式
  • 知识
  • 问题
  • 编程语言与技术
  • 系统与架构
  • 软件开发实践
  • 数据处理与应用设计
  • 个人
  • 产品
  • 团队
  • 知识体系
  • Vue
关于
✿导航
  • 基础
  • 函数
  • 知识点
  • IO框架
  • 新版本特性
  • 数据库原理
  • SQL语言
  • SQL - MySQL
  • NoSQL - Redis
  • NoSQL - ElasticSearch
  • 算法基础
  • 常见算法
  • 领域算法
  • 分布式算法
  • 数据结构与算法
  • 计算机网络
  • 操作系统
  • 计算机组成
  • 开发
  • 测试
  • 架构基础
  • 分布式系统
  • 微服务
  • 中间件
  • 概念
  • 理论
  • 架构设计原则
  • 设计模式
  • 协议
  • 技术选型
  • 编码规范
  • 流水线构建 - CI/CD
  • 知识点 - Linux
  • 网站 - Nginx
  • 容器化 - Docker
  • 容器编排 - Kubernetes
  • 服务网格 - Service Mesh Istio
  • 常用快捷键 - Shortcut
  • 工具使用 - Tools
  • 开源项目
  • 学习项目
  • 个人项目
  • 项目开发
  • 项目Idea
  • 并发
  • 部署
  • 分布式
  • 知识
  • 问题
  • 编程语言与技术
  • 系统与架构
  • 软件开发实践
  • 数据处理与应用设计
  • 个人
  • 产品
  • 团队
  • 知识体系
  • Vue
关于
  • 编程语言与技术

    • Effective C++: 改善程序与设计的55个具体做法

      • 第2章 - 构造/析构/赋值运算(一)
      • 第2章 - 构造/析构/赋值运算(二)
      • 第2章 - 构造/析构/赋值运算(三)
      • 第3章 - 资源管理
      • 第4章 - 设计与声明(一)
      • 第4章 - 设计与声明(二)
      • 第5章 - 实现(一)
      • 第5章 - 实现(二)
      • 第6章 - 继承与面向对象设计
      • 第7章 - 模板与泛型编程
    • 深度探索C++对象模型

      • 第1章 - 关于对象
      • 第2章 - 构造函数语意学
      • 第3章 - Data 语意学
    • STL源码剖析

      • 第1章 - STL概论和版本简介
      • 第2章 - 空间配置器
      • 第3章 - 迭代器(iterators)概念与traits编程技法(一)
      • 第3章 - 迭代器(iterators)概念与traits编程技法(二)
      • 第4章 - 序列式容器 vector
      • 第4章 - 序列式容器 list
      • 第4章 - 序列式容器 deque
      • 第4章 - 序列式容器 stack和queue
      • 第4章 - 序列式容器 heap
      • 第4章 - 序列式容器 priority_queue
      • 第4章 - 序列式容器 slist
      • 第5章 - 关联式容器 RB-tree
      • 第5章 - 关联式容器 set和map
      • 第5章 - 关联式容器 hashtable
      • 第6章 - 算法
      • 第6章 - 算法之set
      • 第7章 - 仿函数
      • 第8章 - 配接器
  • 系统与架构

    • 深入理解计算机系统

      • 第1章 - 计算机系统漫游
      • 第2章 - 信息的表示和处理
      • 第3章 - 程序的机器级表示
      • 第5章 - 优化程序性能
      • 第6章 - 存储器层次结构
      • 第7章 - 链接
      • 第8章 - 异常控制流
      • 第9章 - 虚拟内存
      • 第10章 - 系统级I/O
      • 第11章 - 网络编程
      • 第12章 - 并发编程
    • 大型网站技术架构——核心原理与案例分析

      • 第1章 - 大型网站架构演化
      • 第2章 - 大型网站架构模式
      • 第3章 - 大型网站核心架构要素
      • 第4章 - 瞬时响应:网站的高性能架构
      • 第5章 - 万无一失:网站的高可用架构
      • 第6章 - 永无止境:网站的伸缩性架构
      • 第7章 - 随需应变:网站的可扩展架构
      • 第8章 - 固若金汤:网站的安全架构
    • 从零开始学架构

      • 架构基础
      • 架构设计原则
      • 高性能架构
      • 高可用架构
    • 程序员的自我修养————链接、装载与库

      • 第1章 - 简介
      • 第2章 - 静态链路
      • 第3章 - 目标文件里有什么
      • 第4章 - 静态链接
      • 第7章 - 动态链接
      • 第8章 - 共享库版本
      • 第10章 - 内存
      • 第11章 - 运行库
      • 第12章 - 系统调用与API
      • 第13章 - 运行库实现
  • 软件开发实践

    • 重构改善既有代码的设计

      • 第1章 - 重构,第一个示例
      • 第2章 - 重构的原则
      • 第3章 - 代码的坏味道
      • 第5章 - 重构列表
      • 第6章 - 重新组织函数
      • 第7章 - 在对象之间搬移特性
      • 第8章 - 重新组织数据
      • 第9章 - 简化条件表达式
      • 第10章 - 简化函数调用
      • 第11章 - 处理概括关系
      • 第12章 - 设计之大型重构
    • 代码大全2

      • 第1章 - 欢迎进入软件构建的世界
      • 第2章 - 用隐喻来更充分地理解软件开发
      • 第3章 - 三思而后行: 前期准备
      • 第4章 - 关键的构建决策
      • 第5章 - 软件构建中的设计
    • Linux多线程服务端编程——使用muduo C++ 网络库

      • Buffer类的设计
      • 设计与实现
      • 定时器与TimerQueue
      • Protobuf网络传输和Protobuf编解码器与消息分发器
      • EventLoop类剖析
      • EventLoopThread和EventLoopThreadPool剖析
      • TCP网络库和核心类
      • Connector剖析
      • TcpClient剖析
      • 学习总结
      • timing wheel
      • 消息广播服务
      • 线程安全的对象生命期管理
  • 数据处理与应用设计

    • 数据密集型应用系统设计

      • 第1章 - 可靠、可扩展与可维护的应用系统
      • 第2章 - 数据模型与查询语言
      • 第3章 - 数据存储与检索
      • 第4章 - 数据编码与演化
      • 第5章 - 数据复制
      • 第6章 - 数据分区
      • 第7章 - 事务

第6章 继承与面向对象设计

    确认你的public继承塑模出is-a关系

    C++面向对象编程,最重要的一个规则:public inheritance(公开继承)意味“is-a”(是一种)的关系。即能够施行于base class 对象身上的每件事情,也可以施行于derived class对象身上,因为derived class对象也都是一个base class对象。但是界限很难鉴别,举例如下:

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

    人的概念比学生更一般化,学生是人的一种特殊形式。

    class Bird{...};
    class Penguin:public Bird {...};
    

    鸟可以飞,企鹅是鸟,但是不能飞。这种情况下,应该在编译期拒绝企鹅飞行的设计,而不是在运行期才能侦测它们。

    同理,正方形和矩形也可以是:

    class Square:public Rectangle
    

    正方形一定时矩形,但是矩形的一些函数操作便不适用于正方形。

    明智而审慎地使用private继承

    如果classes之间的继承关系是private,编译器不会自动将一个derived class对象转换为一个base class对象。

    class Person{
    };
    class Student : private Person {};
    
    void eat(const Person &p){};   // 任何人都会吃
    void study(const Student &s){}; // 只有学生会学习
    
    int main(){
        Person p;
        Student s;
    
        eat(p);  // 人会吃
        eat(s); // error!!! 难道学生不是人???
    
        study(p); // error!!!不是所有的人都会学习
        study(s); // 只有学生会学习
    }
    
    

    会提示:

    [root@iZuf61kbf845xt6tz10abgZ home]# g++ -o main  main.cpp
    main.cpp: In function ‘int main()’:
    main.cpp:14:10: error: ‘Person’ is an inaccessible base of ‘Student’
       14 |     eat(s); // error!!! 难道学生不是人???
          |          ^
    main.cpp:16:11: error: invalid initialization of reference of type ‘const Student&’ from expression of type ‘Person’
       16 |     study(p); // error!!!不是所有的人都会学习
    

    注意事项:

    1. private继承意味implemented-in-terms-of(根据某物实现出)。它通常比复合(composition)的级别低。但是当derived class需要访问protected base class的成员,或需要重新定义继承而来的virtual函数时,这么设计时合理的。
    2. 和复合(composition)不同,private继承可以造成empty base 最优化。

    通过复合塑模出has-a或“根据某物实现出”

    STL中set可以用list实现出来,但是set和list并不是public继承关系,因为set中元素是唯一的,但是list可以拥有多个重复元素节点。

    template<class T>
    class Set
    {
    public:
        bool member(const T& item) const;
        bool insert(const T& item) ;
        bool remove(const T& item) ;
    private:
        std::list<T> rep;
    }
    

    避免遮掩继承而来的名称

    class Base
    {
        public:
            Base(){};
            ~Base(){};
    		virtual void mFun1() = 0;
    		virtual void mFun1(int i){};
    		virtual void mFun2(){};
    		void mFun3(){};
    		void mFun3(double d){};
        private:
            int x;
    };
    
    class Derived:public Base
    {
        public:
    		//using Base::mFun1;
    		//using Base::mFun3;
            Derived(){};
            ~Derived(){};
    		virtual void mFun1(){};
    		void mFun3(){};
    		void mFun4(){};
        private:
    };
    
    int main()
    {
       Derived m_Derived;
        int x=10;
        m_Derived.mFun1();
        m_Derived.mFun1(x);//错误
        m_Derived.mFun2();
        m_Derived.mFun3();
        m_Derived.mFun3(x);//错误
        m_Derived.mFun4(); 
    }
    

    提示:

    [root@iZuf61kbf845xt6tz10abgZ home]# g++ -o main  main.cpp
    main.cpp: In function ‘int main()’:
    main.cpp:34:22: error: no matching function for call to ‘Derived::mFun1(int&)’
       34 |     m_Derived.mFun1(x);//错误
          |                      ^
    main.cpp:23:16: note: candidate: ‘virtual void Derived::mFun1()’
       23 |   virtual void mFun1();
          |                ^~~~~
    main.cpp:23:16: note:   candidate expects 0 arguments, 1 provided
    main.cpp:37:22: error: no matching function for call to ‘Derived::mFun3(int&)’
       37 |     m_Derived.mFun3(x);//错误
          |                      ^
    main.cpp:24:8: note: candidate: ‘void Derived::mFun3()’
    

    需要使用

    using Base::mFun1;
    using Base::mFun3;
    
    or
    
    m_Derived.Base::mFun1(x);
    m_Derived.Base::mFun3(x);
    

    上述才能正常调用

    如果继承base class并加上重载函数,而你又希望重新定义或覆写其中一部分,那么你必须为那些原本会被遮掩的每个名称引入一个using声明式,否则某些你希望继承的名称会被遮掩。

    注意事项:

    1. derived classes 内的名称会遮掩base classes内的名称。在publid继承下从来没有人希望如此。
    2. 为了让被遮掩的名称再见天日,可使用using声明式或转交函数。

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

    **non-virtual函数是静态绑定的,virtual函数是动态绑定的。**public继承表示is-a的关系,non-virtual函数会为class建立起一个不变性,凌驾其特异性。

    class B
    {
    public:
        void mFun();
    }
    
    class D:public B
    {
    public:
        void mFun();
    }
    
    D x;
    B* pB =&x;
    D* pD =&x;
    pB->mFun();//调用B::mFun()
    pD->mFun();//调用D::mFun()
    

    pB和pD指向相同的对象x,调用结果却不一致。是因为non-virtual是静态绑定,通过pB调用的non-virtual函数永远是B所定义的版本。

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

    继承一个带有缺省参数值的virtual函数是不正确的,因为virtual函数系动态绑定,而缺省参数值却是静态绑定。

    明智而审慎地使用多重继承

    C++标准程序库内含一个多重继承体系,basic_ios,basic_istream,basic_ostream和basic_iostream。basic_istream和basic_ostream  virtual public继承于basic_ios,basic_iostream  public继承于basic_istream和basic_ostream 。virtual public继承不会拥有两份basic_ios内容。

    注意事项:

    1. 多重继承比单一继承复杂,它可能导致新的歧义性,以及对virtual继承的需要。(例如继承的两个类中有相同的函数,可以用class::mFun指明调用的是哪个函数)。
    2. virtual 继承会增加大小、速度、初始化(及赋值)复杂度等等成本。如果virtual base classes不带任何数据,将是最具使用价值的情况。
    3. 多重继承的确有正当用途。其中一个情节涉及“public继承某个Interface class”和“private继承某个协助实现的class”两相结合。 ​
    Last Updated:
    Contributors: klc407073648
    Prev
    第5章 - 实现(二)
    Next
    第7章 - 模板与泛型编程