在我的设计中,有一个类从文件中读取信息。读取信息表示作业(为简单起见,它是一个整数,即“作业ID”)。文件读取器类可以接受可以处理此类作业的对象。现在我的想法是,制作一个接口,例如“IJobHandler”有一个纯虚函数“DoJob()”然后你可以调用类似
的东西FileReader fr;
Class1 c1; // has a base class IAcceptor with virtual method HandleJobId()
Class2 c2; // has a base class IAcceptor with virtual method HandleJobId()
fr.Register(c1);
fr.Register(c2);
fr.doJob(1); // calls c1.HandleJobId()
fr.doJob(2); // class c2.HandleJobId()
这样可以正常工作。但是,如果某个班级可以处理两个或更多工作ID,会发生什么?但是这个类只能实现一种方法(HandleJobId())。以下不是很好:
fr.Register(c1, c1::Handle_1())
或类似的东西?
也许我现在的意图不是很清楚。但是你将在下面更大的代码示例中看到它。对于大代码块感到抱歉,但我不知道如何解释它......
class IAcceptable
{
public:
// interface; implementors should return map of job-ids (int)
// and a kind of pointer to a method which should be called to
// handle the job.
virtual std::map<int, SOME_KIND_OF_FUNCTION_POINTER> GetJobIds() const = 0;
};
class Class12 : public IAcceptable
{
public:
void Handle_1(){} // method to handle job id 1
void Handle_2(){} // method to handle job id 2
virtual std::map<int, SOME_KIND_OF_FUNCTION_POINTER> GetJobIds() const
{
std::map<int, SOME_KIND_OF_FUNCTION_POINTER> intToMethodMap;
// return map, which says: "I can handle job id 1, by calling Handle_1(), so I give you c12 pointer to this method"
// (same thing for job id 2 and Handle_2())
intToMethodMap.insert(std::pair<int, SOME_KIND_OF_FUNCTION_POINTER>(1, POINTER_TO_Handle_1);
intToMethodMap.insert(std::pair<int, SOME_KIND_OF_FUNCTION_POINTER>(2, POINTER_TO_Handle_2);
return intToMethodMap;
}
};
class Class34 : public IAcceptable
{
void Handle_3(){} // method to handle job id 3
void Handle_4(){} // method to handle job id 4
virtual std::map<int, SOME_KIND_OF_FUNCTION_POINTER> GetJobIds() const
{
std::map<int, SOME_KIND_OF_FUNCTION_POINTER> intToMethodMap;
// return map, which says: "I can handle job id 3, by calling Handle_3(), so I give you c12 pointer to this method"
// (same thing for job id 4 and Handle_4())
intToMethodMap.insert(std::pair<int, SOME_KIND_OF_FUNCTION_POINTER>(3, POINTER_TO_Handle_3);
intToMethodMap.insert(std::pair<int, SOME_KIND_OF_FUNCTION_POINTER>(4, POINTER_TO_Handle_4);
return intToMethodMap;
}
};
class FileReader
{
public:
// register an IAcceptable
// and add its handlers to the local list
void Register(const IAcceptable& acc)
{
m_handlers.insert(acc.GetJobIds());
}
// if some job is to do, search for the job id and call
// the found function
void doSomeJob(int i)
{
std::map<int, SOMEFUNCTION>::iterator specificHandler = m_handlers.find(i);
// call here (specificHandler->second)()
}
private:
std::map<int, SOMEFUNCTION> m_handlers;
};
int main()
{
Class12 c12; // can handle job id 1 and 2
Class34 c34; // can handle job id 3 and 4
FileReader fr;
fr.Register(c12);
fr.Register(c34);
fr.doSomeJob(1); // should lead to this call: c12->Handle_1()
fr.doSomeJob(2); // c12->Handle_2();
fr.doSomeJob(3); // c34->Handle_3();
fr.doSomeJob(4); // c34->Handle_4();
}
好吧,也许设计是我的问题,有人可以给我一个如何让它变得更好的提示:)
答案 0 :(得分:1)
这是一个完整的例子:
class IAcceptable;
class DelegateBase
{
public:
virtual void Call() = 0;
};
template <class Class> class Delegate: public DelegateBase
{
public:
typedef void (Class::*Function)();
Delegate(Class* object, Function f): func(f) {}
virtual void Call() { (object->*func)(); }
private:
Class* object;
Function func;
};
class IAcceptable
{
public:
// interface; implementors should return map of job-ids (int)
// and a kind of pointer to a method which should be called to
// handle the job.
virtual std::map<int, DelegateBase*> GetJobIds() = 0;
};
class Class12 : public IAcceptable
{
public:
void Handle_1(){} // method to handle job id 1
void Handle_2(){} // method to handle job id 2
virtual std::map<int, DelegateBase*> GetJobIds()
{
std::map<int, DelegateBase*> intToMethodMap;
// return map, which says: "I can handle job id 1, by calling Handle_1(), so I give you c12 pointer to this method"
// (same thing for job id 2 and Handle_2())
intToMethodMap.insert(std::pair<int, DelegateBase*>(1, new Delegate<Class12>(this, &Class12::Handle_1)));
intToMethodMap.insert(std::pair<int, DelegateBase*>(2, new Delegate<Class12>(this, &Class12::Handle_2)));
return intToMethodMap;
}
};
class Class34 : public IAcceptable
{
void Handle_3(){} // method to handle job id 3
void Handle_4(){} // method to handle job id 4
virtual std::map<int, DelegateBase*> GetJobIds()
{
std::map<int, DelegateBase*> intToMethodMap;
// return map, which says: "I can handle job id 3, by calling Handle_3(), so I give you c12 pointer to this method"
// (same thing for job id 4 and Handle_4())
intToMethodMap.insert(std::pair<int, DelegateBase*>(3, new Delegate<Class34>(this, &Class34::Handle_3)));
intToMethodMap.insert(std::pair<int, DelegateBase*>(4, new Delegate<Class34>(this, &Class34::Handle_4)));
return intToMethodMap;
}
};
class FileReader
{
public:
// register an IAcceptable
// and add its handlers to the local list
void Register(IAcceptable& acc)
{
std::map<int, DelegateBase*> jobIds = acc.GetJobIds();
m_handlers.insert(jobIds.begin(), jobIds.end());
}
// if some job is to do, search for the job id and call
// the found function
void doSomeJob(int i)
{
std::map<int, DelegateBase*>::iterator specificHandler = m_handlers.find(i);
specificHandler->second->Call();
}
private:
std::map<int, DelegateBase*> m_handlers;
};
int _tmain(int argc, _TCHAR* argv[])
{
Class12 c12; // can handle job id 1 and 2
Class34 c34; // can handle job id 3 and 4
FileReader fr;
fr.Register(c12);
fr.Register(c34);
fr.doSomeJob(1); // should lead to this call: c12->Handle_1()
fr.doSomeJob(2); // c12->Handle_2();
fr.doSomeJob(3); // c34->Handle_3();
fr.doSomeJob(4); // c34->Handle_4();
return 0;
}
要调用成员函数,我们需要一个对象;所以你的地图不应该只包含方法指针,而是包含一个可以封装完整调用的东西:一个对象+一个方法指针。那是代表在这里的东西。
为了确保方法被正确调用,即使它是在子类中定义的,我们需要正确地存储派生对象和方法指针(没有转换)。因此,我们将Delegate作为模板,将派生类作为其参数。
这意味着基于不同子类方法的委托是不兼容的,不能放入地图中。为了解决这个问题,我们引入了一个公共基类DelegateBase和虚函数Call()。可以在不知道存储对象/方法的确切类型的情况下调用Call(),并将其分派给类型正确的实现。现在我们可以在地图中存储DelegateBase *指针。
同时查看boost :: function和boost :: bind,它们提供了上述的概括,我认为它们也可以用于您的目的。
答案 1 :(得分:0)
这类问题有几种解决方案。
如果您有一个可以处理多个不同作业的课程,请单独进行 函数,最简单的解决方案是包装它,有几种类型,例如:
class JobsOneAndTwo
{
public:
void doJobOne();
void doJobTwo();
};
class JobOne : public AbstractJob, JobsOneAndTwo
{
public:
virtual void doJob() { doJobOne(); }
};
class JobTwo : public AbstractJob, JobOneAndTwo
{
public:
virtual void doJob() { doJobTwo(); }
};
如果这通常发生在作业集中,您可以创建一个模板(over 两个或moer成员函数指针)来生成单独的包装器 功能
或者,您可以分派该类的数据成员:
class JobOneAndTwo : public AbstractJob
{
int myJob;
public:
JobOneAndTwo(int id) : myJob( id ) {}
void JobOne();
void JobTwo();
virtual void doJob()
{
switch ( myJob ) {
case 1:
JobOne();
break;
case 2:
JobTwo();
break;
}
};
在这种情况下,每次传递一次时都会实例化两次 构造函数的不同参数。
在我见过的大多数情况下,当一个班级可以处理两份工作时,就是这样 因为这两个工作只在一些参数上有所不同;这真的很公正 上面第二个解决方案的变体,除了你不切换到 调用不同的成员函数,你只需使用参数(传递 在基本功能中进入构造函数。
更一般地说,不要忘记你的具体工作类可以有 数据及其行为可以通过这些数据进行修改。你可以 使用不同的数据注册单个类的多个实例。
答案 2 :(得分:0)
typedef void (IAccaptable::*SOME_KIND_OF_FUNCTION_POINTER)();
...
Register(1, (SOME_KIND_OF_FUNCTION_POINTER)(&Class12::Handle1));
警告:此C样式转换仅适用于单继承。 (好吧,实际上,使用多继承也可以编译转换器,但是当使用指向非第一个基类的成员函数的funcPtr调用(derivedObject->*funcPtr)()
时,将调用它而不使用derivedObject指针经过适当调整以指向属于该基础的适当子对象,最有可能导致崩溃。)
更好但更复杂的解决方案是注册小调用者对象而不是成员函数指针。在调用处理函数时,这些调用者对象可以适当地转换目标对象。
class CallerBase
{
public:
virtual void Call(Base* object) = 0;
};
template <class Derived>
struct Caller: public CallerBase
{
public:
typedef void (Derived::*Function)();
Caller(Function f): func(f) {}
virtual void Call(Base* object)
{
Derived* derived = static_cast<Derived*>(object);
(derived->*func)();
}
private:
Function func;
};
Register(1, new Caller<Derived>(&Derived::F));
然后你的地图将包含CallerBase *指针,一旦找到合适的调用者,你就会caller->Call(object)
。如果此调用中的object
是Derived *,则它将隐式转换为Base *,但虚拟Caller<Derived>::Call()
函数会在实际调用该方法之前将其强制转换为Derived *。
答案 3 :(得分:0)
所以你说你有很多处理程序,每个处理程序可以处理任意数量的作业ID,你想要注册任意数量的处理程序,并让所有处理程序处理给定的作业。
为此,让每个处理程序实现此接口:
struct Handler
{
virtual bool canHandle(job_id_t id) const = 0;
virtual void doJob(job_it_t id) = 0;
};
要注册处理程序,只需将指针存储在容器中:
std::vector<Handler*> handlers;
然后,如果您需要做一份工作,请迭代容器并发送:
handleJob(job_it_t id)
{
for (std::vector<Handler*>::iterator it = handlers.begin(), end = handlers.end(); it != end; ++it)
{
if ((*it)->canHandle(id))
(*it)->doJob(id);
}
}
答案 4 :(得分:-1)