大并发服务器架构(陈硕muduo库源码解析)——基础篇
我从P8开始看的。
面向对象的编程和基于对象的编程思想
muduo库不使用抽象类和虚函数作为接口,只暴露具体类,这就意味着muduo库不使用面向对象的编程思想,而使用基于对象的编程思想,以下说明两者的区别:
面向对象的编程思想
Thread.h:
#ifndef _THREAD_H_
#define _THREAD_H_
#include <pthread.h>
class Thread
{
public:
Thread();
// 由于要用到多态,所以这里析构函数设为虚函数。
// 设为虚函数以后,使用父类指针访问的子类对象结束后,才会调用子类的析构函数,不然只会调用父类的析构函数。
virtual ~Thread();
void Start();
void Join();
void SetAutoDelete(bool autoDelete);
private:
// 加了静态,就没有隐含的 this 指针了
static void *ThreadRoutine(void *arg);
// 纯虚函数
virtual void Run() = 0;
pthread_t threadId_;
bool autoDelete_;
};
#endif // _THREAD_H_
C++中虚析构函数:设为虚析构函数以后,使用父类指针访问的子类对象结束后,才会调用子类的析构函数,不然只会调用父类的析构函数。
Thread.cpp:
#include "Thread.h"
#include <iostream>
using namespace std;
Thread::Thread() : autoDelete_(false)
{
cout<<"Thread ..."<<endl;
}
Thread::~Thread()
{
cout<<"~Thread ..."<<endl;
}
void Thread::Start()
{
// C++中类的普通成员函数不能作为 pthread_create的线程函数。
// 所以ThreadRoutine可以设为静态成员函数,但不能是普通成员函数,所以ThreadRoutine也不能直接设为Run
pthread_create(&threadId_, NULL, ThreadRoutine, this);
}
void Thread::Join()
{
pthread_join(threadId_, NULL);
}
void* Thread::ThreadRoutine(void* arg)
{
// static_cast 是强制类型转换为 Thread*
Thread* thread = static_cast<Thread*>(arg); // static_cast<Thread*>(arg)得到的肯定是一个Thread的子类对象
// 而thread是父类指针,故这里使用到了多态,所以Run要设为virtual
thread->Run(); // ThreadRoutine中不能直接调用Run,因为 ThreadRoutine是静态成员函数,而Run是普通成员函数
if (thread->autoDelete_) // 本函数执行完毕,即线程执行完毕,就将对象delete掉
delete thread;
return NULL;
}
void Thread::SetAutoDelete(bool autoDelete)
{
autoDelete_ = autoDelete;
}
C++中类的普通成员函数不能作为 pthread_create的线程函数,这篇文章说隐含传入的this指针与线程函数参数(void*)不能匹配。但是我觉得它说的有问题,因为this和(void*)明显是匹配的。我觉得“C++中类的普通成员函数不能作为 pthread_create的线程函数”的主要原因是:普通成员函数由于要隐含传入this,所以它在实现上可能与pthread_create要求的函数有所区别,所以不能传入。
Thread_test.cpp:
#include "Thread.h"
#include <unistd.h>
#include <iostream>
using namespace std;
class TestThread : public Thread
{
public:
TestThread(int count) : count_(count)
{
cout<<"TestThread ..."<<endl;
}
~TestThread()
{
cout<<"~TestThread ..."<<endl;
}
private:
void Run()
{
while (count_--)
{
cout<<"this is a test ..."<<endl;
sleep(1);
}
}
int count_;
};
int main(void)
{
/*
TestThread t(5);
t.Start();
t.Join();
*/
TestThread* t2 = new TestThread(5);
t2->SetAutoDelete(true);
t2->Start();
t2->Join();
return 0;
}
基于对象的编程思想
首先介绍以下boost bind/function,它替代了 stl中的mem_fun,ptr_fun,bind1st,bin2nd 等函数,具体如下:
boos::bind
:调整函数的部分形参为默认值和形参的位置boost::function
:构建函数指针
举例如下:
#include <iostream>
#include <boost/function.hpp>
#include <boost/bind.hpp>
using namespace std;
class Foo
{
public:
void memberFunc(double d, int i, int j)
{
cout << d << endl;//打印0.5
cout << i << endl;//打印100
cout << j << endl;//打印10
}
};
int main()
{
Foo foo;
boost::function<void (int)> fp = boost::bind(&Foo::memberFunc, &foo, 0.5, _1, 10);
fp(100);
return 0;
}
上述代码说明:
- 函数Foo::memberFunc的形参有四个,因为成员函数Foo::memberFunc隐含了本对象的指针作为形参。
_1
:将函数fp的第一个参数,放在_1所在的位置。【参考:boost::bind的理解与使用】- 从boost::bind的理解与使用中我们知道boost::bind返回的是是一个重载了()操作符的类的对象。
接下来我们修改了面向对象的编程思想那一小节中线程类的实现,实现代码见:链接
代码说明:
- 本代码中传递了不同形式的函数给线程类的执行函数。当
boost::function
定义的函数指针的参数和我们定义的函数A的形参不匹配的时候,就无法将A传递给函数指针,此时就可以使用boos::bind
来让两者的形参匹配上。 - 本代码中实现线程类与面向对象的编程思想中实现线程类的不同在于:
在面向对象的编程思想中,首先构造一个抽象的线程类,然后通过继承的方式定义线程类中具体的的执行函数Run。
在本代码中,不使用抽象类和虚函数作为接口,只暴露具体类。通过向线程类的对象传递函数指针的方式,来指定线程类中的执行函数Run。也就是说,面向对象的编程思想要实现不同的Run函数,需要构造不同的子类。本代码中要实现不同的Run函数,只需要向线程类的对象传递不同的函数指针就行。
下面我们举一个网络库实现的例子来说明面向对象的编程思想和基于对象的编程思想:
面向对象的编程思想实现网络库:首先定义一个抽象类TcpServer,TcpServer写了当哪个事件(如读/写事件)发生的时候就调用哪个回调函数的逻辑代码,但是TcpServer中将回调函数设置成虚函数,从而让子类自己进行实现回调函数。
基于对象的编程思想实现网络库:首先定义一个具体类TcpServer,TcpServer写了当哪个事件(如读/写事件)发生的时候就调用哪个回调函数的逻辑代码,TcpServer还包含了很多个函数指针,通过向这些函数指针传递函数来具体实现回调函数。如下面实现了一个EchoServer服务器:
class EchoServer
public:
EchoServer()
{
server.SetConnectionCallback (boost::bind(OnConnection));
server.SetMlessage (boost::bind(OnMlessage));
server.SetClose (boost::bind(OnClose));
}
void OnConnection(){ ... }
void OnMlessage();
void OnClose() ;
TcpServer server;
}
面向对象的编程思想:用一个EchoServer继承TcpServer(抽象类),实现三个接口OnConnection Onllessage, 0nClose.
基于对象的编程思想:用一个EchoServer包含一个TcpServer (具体类)对象,在构造函数中用boost::bind来注册三个成员函数OnConnection, OnMlessage, OnClose。为什么叫基于对象的编程思想呢?因为使用了具体类TcpServer和EchoServer。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?