首先复习一下C++面向对象和基于对象的区别
面向对象
面向对象的三大特点:封装,继承,多态缺一不可
封装:数据和处理数据的函数统一起来,封装在一个class中
继承:通过继承某个类派生出一个新类,被继承的类称作基类,派生出的类称作派生类。派生类是对基类的补充,二者之间满足一定的归属关心,如动物(基类),鸟(派生类)。继承可以是public/private/protected继承,也可以是虚继承(用于解决多重继承带来的重复问题),基类可以是抽象基类(不能被实例化),但是派生类需要重新实现基类定义的每个纯虚函数。
多态:在继承的基础上通过基类指针指向派生类的实例化对象,达到调用派生类虚函数的目的,多态又被叫做运行时多态,是在运行期根据基类指针实际指向的对象类型判断调用哪个函数的方式。
基于对象
无继承,无多态,只有封装
利用类封装好的接口实现对数据的操作
————————————————
如何禁止编译器自动生成拷贝构造函数/赋值运算符
继承boost::noncopyable
自定义空基类,基类中将两个函数放在private域,派生类private继承该基类
在自己的private域中声明两个函数,不予实现
c++11版本采用第2中,boost版本采用第1中,第三种效果不好,因为错误是在链接期发现,前两个是在编译期
————————————————
现如今大多C++程序都是基于对象的,面向对象只在整个程序中占一小部分比重。
muduo采用的也是基于对象的手法,但是对io多路复用的封装采用的是面向对象,即定义一个基类,派生出不同的派生类。muduo只派生了poll/epoll两个类封装,因为二者在实现上有相似性,可以共用基类。
基类Poller主要用于设计统一接口,两个派生类EPollPoller/PollPoller用于实现各自的操作,Poller定义如下
class Poller : noncopyable
{
public:
typedef std::vector<Channel*> ChannelList;
Poller(EventLoop* loop);
virtual ~Poller();
/// Polls the I/O events.
/// Must be called in the loop thread.
/*
* 监听函数,对于epoll是epoll_wait,对于poll是poll
* 返回epoll_wait/poll返回的时间
*/
virtual Timestamp poll(int timeoutMs, ChannelList* activeChannels) = 0;
/// Changes the interested I/O events.
/// Must be called in the loop thread.
/* 更新监听事件,增删改对fd的监听事件 */
virtual void updateChannel(Channel* channel) = 0;
/// Remove the channel, when it destructs.
/// Must be called in the loop thread.
/* 删除监听事件 */
virtual void removeChannel(Channel* channel) = 0;
virtual bool hasChannel(Channel* channel) const;
static Poller* newDefaultPoller(EventLoop* loop);
void assertInLoopThread() const
{
ownerLoop_->assertInLoopThread();
}
protected:
/*
* Channel,保存fd和需要监听的events,以及各种回调函数(可读/可写/错误/关闭等)
* 类似libevent的struct event
*/
typedef std::map<int, Channel*> ChannelMap;
/* 保存所有事件Channel,类似libevent中base的注册队列 */
ChannelMap channels_;
private:
/*
* EventLoop,事件驱动主循环,用于调用poll函数
* 类似libevent的struct event_base
*/
EventLoop* ownerLoop_;
};
————————————————
类中采用前向声明,即在定义Poller之前声明一下class Channel;,用处是避免让头文件#include <muduo/net/Channel.h>从而增加依赖性,因为头文件中并没有使用Channel,只是定义了这个类型的变量,所以只声明就好了,而在成员函数的实现中需要使用Channel的接口,这就需要让编译器知道Channel是怎么定义的,就需要在.cpp文件中#include <muduo/net/Channel.h>。另外,因为Channel是Poller的成员变量,当Poller析构时也会调用Channel的析构函数,这就需要让编译器知道Channel析构函数的定义,所以Poller的析构函数需要在.cpp中定义。
以上也是大多muduo类采用的方法,这种方法可以降低依赖关系,如果Channel文件改变,不需要重新编译Poller文件
————————————————
派生类EPollPoller的实现就是重新实现基类Poller声明的纯虚函数,简单的调用epoll的接口。在poll返回后也会将就绪的fd(muduo是由Channel管理,libevent是由struct event管理)添加到激活队列中
/*
* 对epoll函数的封装,继承自Poller
*/
class EPollPoller : public Poller
{
public:
EPollPoller(EventLoop* loop);
virtual ~EPollPoller();
/* epoll_wait */
virtual Timestamp poll(int timeoutMs, ChannelList* activeChannels);
/* ADD/MOD/DEL */
virtual void updateChannel(Channel* channel);
/* DEL */
virtual void removeChannel(Channel* channel);
private:
static const int kInitEventListSize = 16;
/* EPOLL_CTL_ADD/MOD/DEL转成字符串 */
static const char* operationToString(int op);
/* epoll_wait返回后将就绪的文件描述符添加到参数的激活队列中 */
void fillActiveChannels(int numEvents,
ChannelList* activeChannels) const;
/* 由updateChannel/removeChannel间接调用,执行epoll_ctl */
void update(int operation, Channel* channel);
typedef std::vector<struct epoll_event> EventList;
int epollfd_;
EventList events_;
};
————————————————
类的声明中的EventList记录着所有监听的epoll_event,.cpp中就是实现上述函数,进行增删改等,主要记录一些没接触过的知识