回顾面向对象的编程风格封装线程类,一般这么做:写一个Thread base class,含有(纯)虚函数Thread::run(),然后应用程序派生一个derived class,覆写run()。程序里的每一种线程对应一个Thread的派生类。
面向对象的编程风格主要还是围绕面向对象的三大特点“封装、继承、多态
”来展开的——通过封装隐藏实现细节,只暴露需要对外提供的属性或方法;通过继承的特性来提高程序的可复用性;定义抽象基类(纯虚函数),让其派生类继承并实现它的接口方法。
“面向对象”和“基于对象”都实现了“封装”的概念,但是面向对象实现了“继承和多态”,而“基于对象”是使用对象,无法利用现有的对象模板产生新的对象类型继而产生新的对象,没有实现“继承和多态”。
二、基于对象的编程风格
基于对象的编程风格要借助boost bind/function
库来实现,用它来代替虚函数作为类的(回调)接口。它的出现替代了STL中的mem_fun
、ptr_fun
、bind1st
、bin2nd
等函数适配器,**其主要作用是使一种类型的函数接口适配成另一种类型的函数接口,但boost bind/function的通用性更好。**❗❗❗
且boost bind/function现在已经成为C++11的标准了
boost bind/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;
cout << i << endl;
cout << j << endl;
}
};
int main()
{
Foo foo;
boost::function<void (int)> fp = boost::bind(&Foo::memberFunc, &foo, 0.5, _1, 10);//把一个成员函数适配成新的函数类型
//boost::function<void(int)>:这个类型可以看成是:“返回值void, 接收一个int类型的参数” 这样的一个接口,
//原来的函数memberFun有三个参数,准确说是4个参数,在函数调用的时候隐含一个this指针,而我们适配成的只有1个参数,
//调用适配函数 fp(100), 实际上调用的函数原来的memberFunc函数,但原函数有四个参数嘛
//这个100实际放到的是谁的位置,传递的是哪一个参数呢,取决于 右边 bind()
//看bind里参数依次,&Foo::memberFunc,&foo 指向成员函数的指针, 绑定到this的对象, 其他几个参数
//0.5, _1, 10分别就i是那三个形参,其中第二个_1是占位符, 表示boost::function里面能接收的参数?↓
//表示我们不打算向成员函数绑定第2个参数,这里使用 _1 来占位,同理下边还会用到 _2 _3 这样的占位符.
fp(100); //调用foo::memberFunc(0.5, 100, 10),打印0.5,100,10
boost::function<void (int, int)> fp2 = boost::bind(&Foo::memberFunc, boost::ref(foo), 0.5, _1, _2);
fp2(100, 200); //调用foo::memberFunc(0.5, 100, 200),打印0.5,100,200
return 0;
}
boost::function<返回类型(形参们)> 适配函数名fp = boost::bind(指向成员函数的指针,绑定到this的对象,参数们)
调用适配器:fp(参数)
这个参数是传给哪个形参的,就看bind中占位符
了解了之后就看看基于对象用boost bind/function是怎么个编程风格👇
使用基于对象的编程风格来封装一个线程类
面向对象中封装了一个Thread的抽象类, 基于对象使用的是具体类:
接口大致上还是一样的:启动线程Start(), 等待Join(),线程的执行体Run(), 线程的入口函数ThreadRoutine()去调用线程的执行体
🌂多一个ThreadFunc函数, 定义了一个boost::function ThreadFunc(所以是适配函数咯),这个适配函数正是类图中线程构造函数中传递的线程函数, 也就是说线程实际执行的函数是这个ThreadFun函数,可想而知这个函数要在Run方法中调用
面向对象中,实现一个具体的线程类是覆盖Run方法, 在Run方法中提供这个线程类的具体实现,但这边基于对象的具体实现是通过TreadFunx传递进来的,只需要通过boost::bind转换成boost::function
代码:
文件名:Thread.h
#ifndef _THREAD_H_
#define _THREAD_H_
#include <pthread.h>
#include <boost/function.hpp>
class Thread
{
public:
typedef boost::function<void ()> ThreadFunc; //⭐一种新的类型 1⭐
explicit Thread(const ThreadFunc& func); // 单参数用explicit阻止隐式的转换构造,只能显示调用 3 那多了这个函数的话就去cpp文件中实现一下
//传进去的是一个(未定的)新类型,⭐这跟重要!!!为后面做铺垫
void Start();//创建线程 pthread_create()出处
void Join();
void SetAutoDelete(bool autoDelete);
private:
static void* ThreadRoutine(void* arg);
void Run();//上一个是虚函数,这个就不用了
ThreadFunc func_;//用run方法调用func_方法, 这个方法在线程的构造函数中要传递进来 2
pthread_t threadId_;
bool autoDelete_;
};
#endif // _THREAD_H_
文件名:Thread.cpp
#include "Thread.h"
#include <iostream>
using namespace std;
//变化的就是构造函数的参数和run函数
// 构造函数
Thread::Thread(const ThreadFunc& func) : func_(func), autoDelete_(false)
{
cout<<"Thread..."<<endl;
}
Thread::~Thread()
{
cout<<"~Thread..."<<endl;
}
// 创建线程
void Thread::Start()
{
pthread_create(&threadId_, NULL, ThreadRoutine, this);
}
// 等待线程结束
void Thread::Join()
{
pthread_join(threadId_, NULL);
}
// 线程入口函数,这些都不用变
void* Thread::ThreadRoutine(void* arg)
{//入口函数的参数是是第一个(隐式)参数this,就是对象本身的地址,
Thread* thread = static_cast<Thread*>(arg);//因此,基类指针指向了派生类对象
thread->Run();//调用派生类实现的Run
if (thread->autoDelete_)//自动销毁
delete thread;
return NULL;
}
// 设置自动销毁标识
void Thread::SetAutoDelete(bool autoDelete)
{
autoDelete_ = autoDelete;
}
//不是纯虚函数要自己实现, Run方法就是直接调用func_(),
void Thread::Run()
{
func_();
}
测试代码:
文件名:Thread_test.cpp
#include "Thread.h"
#include <boost/bind.hpp>
#include <unistd.h>
#include <iostream>
using namespace std;
class Foo
{//不用继承,也不用覆盖run方法
public:
Foo(int count) : count_(count)
{
}
void MemberFun()
{
while (count_--)
{
cout<<"this is a test ..."<<endl;
sleep(1);
}
}
void MemberFun2(int x)
{
while (count_--)
{
cout<<"x="<<x<<" this is a test2 ..."<<endl;
sleep(1);
}
}
int count_;
}; //why this class
void ThreadFunc()//提供一个线程函数, 开始定义的typedef boost::function<void ()> ThreadFunc
{
cout<<"ThreadFunc ..."<<endl;
}
void ThreadFunc2(int count)
{
while (count--)
{
cout<<"ThreadFunc2 ..."<<endl;
sleep(1);
}
}
int main()
{
// 调用普通函数
Thread t1(ThreadFunc);//创建 一个线程对象,把ThreadFunc传递进来
/*
t1.start();
t1.join();
*/
//typedef boost::function<void ()> ThreadFunc
Thread t2(boost::bind(ThreadFunc2, 3));//实际执行ThreadFunc2这个函数
// 调用成员函数
Foo foo(3);//count_ = 3
Thread t3(boost::bind(&Foo::MemberFun, &foo));//分别是指向成员函数的指针, 绑定到this的对象,无形参
Foo foo2(3);
Thread t4(boost::bind(&Foo::MemberFun2, &foo2, 1000));//一个形参
t1.Start();
t2.Start();
t3.Start();
t4.Start();
t1.Join();
t2.Join();
t3.Join();
t4.Join();
//这是三个线程交替执行的
return 0;
}
用例子总结:
实现C编程风格下,注册三个全局函数到网络库,网络库通过函数指针来回调:
面向对象风格: 用一个EchoServer 继承 TcpServer这个抽象类,再去实现各自的三个接口函数
基于对象风格: 用一个EchoServer 包含 一个TcpServer这个具体类对象, 在构造函数中用boost::bind
来注册三个成员函数