muduo2——编程风格:面向对象的编程和基于对象的编程(下)

回顾面向对象的编程风格封装线程类,一般这么做:写一个Thread base class,含有(纯)虚函数Thread::run(),然后应用程序派生一个derived class,覆写run()。程序里的每一种线程对应一个Thread的派生类。
面向对象的编程风格主要还是围绕面向对象的三大特点“封装、继承、多态”来展开的——通过封装隐藏实现细节,只暴露需要对外提供的属性或方法;通过继承的特性来提高程序的可复用性;定义抽象基类(纯虚函数),让其派生类继承并实现它的接口方法。

“面向对象”和“基于对象”都实现了“封装”的概念,但是面向对象实现了“继承和多态”,而“基于对象”是使用对象,无法利用现有的对象模板产生新的对象类型继而产生新的对象,没有实现“继承和多态”。

二、基于对象的编程风格

基于对象的编程风格要借助boost bind/function来实现,用它来代替虚函数作为类的(回调)接口。它的出现替代了STL中的mem_funptr_funbind1stbin2nd等函数适配器,**其主要作用是使一种类型的函数接口适配成另一种类型的函数接口,但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来注册三个成员函数

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值