模板化虚拟函数的C++设计帮助

C++ design help for templated virtual function

本文关键字:C++ 帮助 函数 虚拟      更新时间:2023-10-16

我正在尝试基于一个通用类实现许多类,该类使用boost.threadpool抽象线程池。我有一些可以工作的东西(在osx 10.7.2上的Xcode中),但我真的不确定它的设计是否好,甚至是否安全(主要是因为我在网上读到了关于使用模板的虚拟成员函数的内容)。我正在寻找一些关于实现这样的东西的最佳方式的风格建议。我在这里边学习边学习,所以我知道很多这都是"糟糕的状态"。。。

我有一个名为"工作队列"的基类,如下所示:

template <typename T> 
class Workqueue{
    private:
        pool            *pThreadPool;
    public:
        Workqueue       (int);        
        void Start      (T);        
        void Schedule   (T);
        virtual bool Process(T) {return true;}
};
template <typename T> Workqueue<T>::Workqueue(int thread_count){
    pThreadPool = new pool(thread_count);
}
template <typename T> void Workqueue<T>::Start(T data){
    pThreadPool->schedule(boost::bind(&Workqueue::Process,this, data));  
    pThreadPool->wait();
}
template <typename T> void Workqueue<T>::Schedule(T data){
    pThreadPool->schedule(boost::bind(&Workqueue::Process,this, data));    
}

然后,我基于这个类定义了一个新的服务,如下所示:

struct Service1Data{
    string item_data;
};
class MyService : public Workqueue<Service1Data> {    
public:
    MyService       (int);
    bool Process    (Service1Data);        
};
MyService::MyService(int workers) : Workqueue<Service1Data>(workers) {}
bool MyService::Process(Service1Data service_data){
    cout << "in process (" << service_data.item_data << ")" << endl;     
    return true;
}

(为了保持简单,我删除了尽可能多的代码,以便在它不断提交新作品时永远运行)。我使用的服务是这样的:

    MyService *service1 = new MyService(5);
    Service1Data x;
    x.item_data = "testing";
    service1->Start(x);
    // will wait until no more work.
    delete service1;

所以我的具体问题是:

首先(请温柔一点…)这种形式不好吗?有更好的方法吗?(为什么?)

其次,考虑到虚拟/模板问题,这是否安全?我在某个地方读到,如果类本身是模板化的,应该是安全的,我想我理解vtable的基本问题,但真的不确定具体情况。

第三,基本工作队列类需要在'h'文件中具有成员定义和要链接的类定义。不知道为什么会这样——我想这是一个与虚拟/模板问题有关的链接器问题,所以让我很紧张。

所有的帮助都得到了感激。。

Chris

我认为,您不应该将数据处理队列处理

我的意思是,你的Workqueue中不应该有Process方法。Data可以自己处理,或者您的队列可以获得作为(模板?)参数的处理函数。

然后你就可以用虚拟函数解决所有的问题了。则CCD_ 4类应当同意CCD_ 5并且可以提供处理功能。

另外,我怀疑你是否真的需要Workqueue。您可以在YourService中使用pThreadPool

如果您需要一个用于服务的通用接口,则应该明确指定它&分别地你的继承链看起来不清楚。继承意味着。为什么YourServiceWorkqueue我不相信我认为YourService可以使用任何类型的队列。但是用法是聚合。

编辑:代码如下:

template<typename Processor>
class WorkQueue
{
public:
    WorkQueue(int count, Processor& processor):_processor(processor),_pool(count) {}
    template <typename Data>
    void schedule(const Data& data)
    {
        _pool->schedule(std::bind(&Processor::process,_processor, data));
    }
    template <typename Data>
    void run(const Data& data)
    {
        schedule(data);
        _pool->wait();
    }
private:
    Processor& _processor;
    pool _pool;
};
class Service
{
public:
    virtual void run() = 0;
    virtual ~Service() {}
};
struct ServiceParams
{
    int param;
};
class MyService: public Service
{
    friend class WorkQueue<MyService>;
public:
    MyService(const ServiceParams& params): _params(params), _queue(1, *this) {}
    void run() { return _queue.run(_params); }
private:
    ServiceParams _params;
    WorkQueue<MyService> _queue;
    void process(const ServiceParams& params) {std::cout <<"hello, worldn";}
};

编辑:我最初认为用法为:

ServiceData data;
Service* service = new MyService(data);
service->run();
delete service;

我可以明显指出的一些小事情:

  • 当您可以创建自动对象时,过度使用new和delete

例如,如果您的工作队列和池具有相同的生存期,则:

template <typename T>  class Workqueue
{      
    private:
      pool            threadPool;  
 // // etc
};
template< typename T >
Workqueue::Workqueue( int numThreads ) : threadPool( numThreads )
{
}

基类需要一个虚拟析构函数,目前Start可以调用Schedule,而不是两次实现同一行代码(使用boost::bind)。理想情况下,只接受int成员的构造函数将被声明为显式的。

您可能需要逻辑来等待线程完成。

我认为一个好的设计应该将排队和工作/任务分开。在您的设计中,两者紧密耦合。如果你想为每种类型的工作/任务创建单独的池,这种设计是很好的。

另一种方法是创建一个单独的包含process函数的Work类。然后您的MyService将扩展Work。CCD_ 16类将接受CCD_ 17,也就是说任何派生类也将接受。这种方法本质上更为通用。所以同一个工作队列可以接受不同类型的工作/任务。下面的代码说明将更清楚。

如果您想为不同类型的数据提供不同的池,也可以使用添加此方法。它在本质上更灵活。

template <typename T> 
class Work{
    T data; // contains the actual data to work on
    public:
        Work(T data) : data(data) {} // constructor to init data
        virtual bool Process(T) {return false;} // returns false to tell process failed
        T getData() { return data; } // get the data
};
class MyWork : public Work<Service1Data> {    
public:
    MyService (Service1Data data) : 
      Work(data) {}
    bool Process (Service1Data); // Implement your work specific process func
};
bool MyWork::Process(Service1Data service_data){
    cout << "in process (" << service_data.item_data << ")" << endl;     
    return true;
}
class Workqueue{
    private:
        pool            *pThreadPool;
    public:
        Workqueue       (int);        
        void Start      (Work);        
        void Schedule   (Work);
};
Workqueue::Workqueue(int thread_count){
    pThreadPool = new pool(thread_count);
}
void Workqueue::Start(Work workToDo){
    pThreadPool->schedule(boost::bind(&Work::Process,this, workToDo.getData()));  
    pThreadPool->wait();
}
void Workqueue::Schedule(Work data){
    pThreadPool->schedule(boost::bind(&Work::Process,this, workToDo.getData()));    
}

用法

Service1Data x;
x.item_data = "testing";
MyWork myWork(x);

Workqueue wq = new Workqueue(5);
wq->Start(myWork);
// will wait until no more work.
delete service1;

现在,要为不同类型的工作/任务实现不同的池,请创建两个具有不同池大小的Workqueue,然后只给其中一个工作类型,另一个工作类别。

注意:上面的代码可能包含语法错误,它只是用来传达设计的。将其视为伪代码。