C++深入刨析muduo中的抽象类Poller

目录

Poller是抽象类,Eventloop通过抽象类Poller,引用不同的派生类对象(PollPoller或EpollPoller),调用同名覆盖方法,就可以很方便地去扩展不同的I/O复用

Poller.h源码

#include <map>
#include <vector>
#include "muduo/base/Timestamp.h"
#include "muduo/net/EventLoop.h"
namespace muduo
{
namespace net
{
class Channel;
class Poller : noncopyable
{
 public:
  typedef std::vector<Channel*> ChannelList;
  Poller(EventLoop* loop);
  virtual ~Poller();
  virtual Timestamp poll(int timeoutMs, ChannelList* activeChannels) = 0;
  virtual void updateChannel(Channel* channel) = 0;
  virtual void removeChannel(Channel* channel) = 0;
  virtual bool hasChannel(Channel* channel) const;
  static Poller* newDefaultPoller(EventLoop* loop);
  void assertInLoopThread() const
  {
    ownerLoop_->assertInLoopThread();
  }
 protected:
  typedef std::map<int, Channel*> ChannelMap;
  ChannelMap channels_;
 private:
  EventLoop* ownerLoop_;
};
}  // namespace net
}  // namespace muduo

可以看到,Poller里有很多纯虚函数,是抽象类

为什么muduo库要抽象一层Poller呢?

因为在eventloop里面,在使用I/O复用的时候,并没有直接指定epoll,因为muduo库对外提供两种I/O复用方法poll和epoll,在eventloop里面,没有直接使用poll或者epoll,而是从抽象层面通过抽象类Poller,引用不同的派生类对象,调用同名覆盖方法,就可以很方便地去扩展不同的I/O复用

Poller抽象基类有两个成员变量:

 protected:
  typedef std::map<int, Channel*> ChannelMap;
  ChannelMap channels_;
 private:
  EventLoop* ownerLoop_;  // 表示Poller所属的事件循环EventLoop

Poller监听的就是Eventloop另外一个成员ChannelList保存的那些channel,所以Poller里面会有一个ChannelMap,key是sockfd,value是sockfd所属的Channel

protected的成员变量就是让派生类可以访问到,private的成员变量派生类不能访问到

Poller.h

#pragma once
#include "noncopyable.h"
#include "Timestamp.h"
#include <vector>
#include <unordered_map>
class Channel;  //只用到指针类型,如果需要用到实例就要包含相应头文件,类型声明是没用的
class EventLoop;
// muduo库中多路事件分发器的核心IO复用模块
class Poller : noncopyable{
    public:
        using ChannelList = std::vector<Channel*>;
        Poller(EventLoop *loop);
        virtual ~Poller();
        // 给所有IO复用保留统一的接口  epoll_wait
        virtual Timestamp poll(int timeoutMs, ChannelList* activeChannels) = 0;
        // 更新感兴趣的事件  epoll_ctl  EPOLL_CTL_ADD  EPOLL_CTL_MOD
        virtual void updateChannel(Channel* channel) = 0;
        // eventloop中删除channel  epoll_ctl  EPOLL_CTL_DEL
        virtual void removeChannel(Channel* channel) = 0;
        // 判断Poller里是否包含某个channel
        bool hasChannel(Channel* channel) const;
        // EventLoop可以通过newDefaultPoller获取一个Poller实例
        static Poller* newDefaultPoller(EventLoop* loop);
    protected:
        // key:sockfd,value:sockfd所属的Channel
        using ChannelMap = std::unordered_map<int, Channel*>;
        ChannelMap channels_;
    private:
        EventLoop* ownerLoop_;  // Poller所属的事件循环
};

Poller.cc

#include "Poller.h"
#include "Channel.h"
Poller::Poller(EventLoop *loop)
    : ownerLoop_(loop)
{}
Poller::~Poller() = default;
bool Poller::hasChannel(Channel* channel) const{
    auto iter = channels_.find(channel->fd());
    return iter != channels_.end() && iter->second == channel;  // 找到fd且channel相等
}

为什么不把newDefaultPoller的实现放在Poller.cc

如果真的把newDefaultPoller写在Poller.cc里面,从语法上来说,没有错误。但是这个函数是要生成一个具体的I/O复用对象,并返回一个基类的指针

所以基类就得include这两个包含了派生类声明的头文件,才能去生成一个具体的实例对象并返回回去,这样不合理。

在继承结构中,Poller是基类,只能派生类引用基类,而Poller.cc基类不能引用派生类,这就是好的OOP设计

muduo用一个单独的源文件DefaultPoller.cc实现newDefaultPoller

#include "muduo/net/Poller.h"
#include "muduo/net/poller/PollPoller.h"
#include "muduo/net/poller/EPollPoller.h"
#include <stdlib.h>
using namespace muduo::net;
Poller* Poller::newDefaultPoller(EventLoop* loop)
{
  if (::getenv("MUDUO_USE_POLL"))
  {
    return new PollPoller(loop);  // 环境变量中有MUDUO_USE_POLL,则返回PollPoller实例
  }
  else
  {
    return new EPollPoller(loop); // 默认返回EPollPoller实例
  }
}

重写DefaultPoller.cc

#include <stdlib.h>
#include "Poller.h"
#include "EPollPoller.h"
Poller* Poller::newDefaultPoller(EventLoop* loop){
    if(std::getenv("MUDUO_USE_POLL")){
        // new poll的实例
        return nullptr;
    }else{
        return new EPollPoller(loop);
    }
}

到此这篇关于C++深入刨析muduo中的抽象类Poller的文章就介绍到这了,更多相关C++ 抽象类Poller内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • C/C++中抽象类详解及其作用介绍

    目录 概述 抽象类 vs 具体类 案例 抽象类的作用 总结 概述 抽象类 (abstract class), 是一些不用来定义对象, 而只作为基类被继承的类. 由于抽象类常用作基类, 所以通常称为抽象基类 (abstract base class). 定义抽象类的唯一目的, 就是去建立派生类. 我们在抽象类基础上要定义出功能各异的派生类, 再用这些派生类去建立对象. 抽象类 vs 具体类 凡是包含纯虚函数的类都是抽象类. 纯虚函数不用实现, 故不能被调用, 抽象类无法建立对象. 抽象类的作用是作

  • C++深入刨析muduo中的抽象类Poller

    目录 Poller是抽象类,Eventloop通过抽象类Poller,引用不同的派生类对象(PollPoller或EpollPoller),调用同名覆盖方法,就可以很方便地去扩展不同的I/O复用 Poller.h源码 #include <map> #include <vector> #include "muduo/base/Timestamp.h" #include "muduo/net/EventLoop.h" namespace mudu

  • C++结构体中变长数组的使用问题分解刨析

    目录 1. 问题来源 2. 问题复现 2.1 初始程序 2.2 独立变长数组复现 2.3 变长数组置前复现 2.4 缓冲区溢出复现 3. 结构体变长数组使用要点 1. 问题来源 今天在结构体里面使用变长数组来封装消息体,运行程序时弹出如下错误: *** stack smashing detected ***: <unknown> terminatedAborted (core dumped) 问题已经解决,由于源程序不方便截取,现在通过一个实例来复现问题. 2. 问题复现 2.1 初始程序 #

  • Android Activity View加载与绘制流程深入刨析源码

    1.App的启动流程,从startActivity到Activity被创建. 这个流程主要是ActivityThread和ActivityManagerService之间通过binder进行通信来完成. ActivityThread可以拿到AMS 的BinderProxy.AMS可以拿到ActivityThread的BinderProxy ApplicationThread.这样双方就可以互相通讯了. 当ApplicationThread 接收到AMS的Binder调用后,会通过handler机

  • Mysql案例刨析事务隔离级别

    目录 1. 理论 SERIALIZABLE REPEATABLE READ READ COMMITTED READ UNCOMMITTED 2. SQL 实践 2.1 查看隔离级别 2.2 READ UNCOMMITTED 2.2.1 准备测试数据 2.2.2 脏读 2.2.3 不可重复读 2.2.4 幻象读 2.3 READ COMMITTED 2.4 REPEATABLE READ 2.5 SERIALIZABLE 3. 总结 很多小伙伴对 MySQL 的隔离级别一直心存疑惑,其实这个问题一

  • Java 逻辑结构与方法函数详解刨析

    ⭐前言⭐ 本文主要介绍JavaSE的逻辑结构和方法. 对一门编程语言逻辑结构和方法的理解是站在C语言之上的,建议配套C语言版本的分析一起食用 链接直达:

  • Java集合框架之List ArrayList LinkedList使用详解刨析

    目录 1. List 1.1 List 的常见方法 1.2 代码示例 2. ArrayList 2.1 介绍 2.2 ArrayList 的构造方法 2.3 ArrayList 底层数组的大小 3. LinkedList 3.1 介绍 3.2 LinkedList 的构造方法 4. 练习题 5. 扑克牌小游戏 1. List 1.1 List 的常见方法 方法 描述 boolean add(E e) 尾插 e void add(int index, E element) 将 e 插入到 inde

  • Java 关于时间复杂度和空间复杂度的深度刨析

    目录 1.算法效率 2.时间复杂度 2.1时间复杂度的概念 2.2大O的渐进表示法 2.3常见时间复杂度计算 2.3.1常用的时间复杂度量级 2.3.2常见示例举例 2.3.2示例答案及分析 3.空间复杂度 1.算法效率 算法效率分析分为两种:第一种是时间效率,第二种是空间效率.时间效率被称为时间复杂度,而空间效率被称作空间复杂度. 时间复杂度主要衡量的是一个算法的运行速度,而空间复杂度主要衡量一个算法所需要的额外空间 如今我们更关注的是时间复杂度,而对空间复杂度已不再关注. 2.时间复杂度 2

  • JavaScript深入刨析this的指向以及如何修改指向

    目录 this 方法中 对象中 隐藏的this 严格模式 可以改变this指向 this 老规矩先看代码: 方法中 function test(){ console.log(this); } 对象中 Person={ name:"张三", eat:function(){ console.log(this) } } 在方法中,this表示该方法所属的对象.因为第一个是window上的方法,所以打印了window,而eat方法是Person方法,所以打印除了对象Person. 所以可以看出

  • Java 十大排序算法之冒泡排序刨析

    目录 冒泡排序原理 冒泡排序API设计 冒泡排序的代码实现 冒泡排序的时间复杂度分析 冒泡排序原理 ①比较相邻的元素,如果前一个元素比后一个元素大,则交换这两个元素的位置 ②对每一对相邻的元素循环上面的步骤,最终最后面的元素就是最大值 冒泡排序API设计 类名 Bubble 构造方法 Bubble:创建Bubble对象 成员方法 1.public static void sort(Comparable[] a):对数组内元素进行排序 2.private static void greater(C

  • Java 十大排序算法之选择排序刨析

    目录 选择排序原理 选择排序API设计 选择排序代码实现 选择排序的时间复杂度 选择排序原理 ①假设第一个索引处的元素为最小值,和其他值进行比较,如果当前的索引处的元素大于其他某个索引处的值,则假定其他某个索引处的值为最小值,最后找到最小值所在的索引 ②交换第一个索引处和最小值所在的索引处的值 选择排序API设计 类名 Selection 构造方法 Selection():创建Selection对象 成员方法 1.public static void sort(Comparable[] a):对

随机推荐