我有一个类,它有另一个类对象的向量作为成员。在这个类的许多函数中,我必须对向量中的所有对象执行相同的操作:
class Small
{
public:
void foo();
void bar(int x);
// and many more functions
};
class Big
{
public:
void foo()
{
for (size_t i = 0; i < VectorOfSmalls.size(); i++)
VectorOfSmalls[i]->foo();
}
void bar(int x)
{
for (size_t i = 0; i < VectorOfSmalls.size(); i++)
VectorOfSmalls[i]->bar(x);
}
// and many more functions
private:
vector<Small*> VectorOfSmalls;
};
我想简化代码,并找到一种不在每个函数中复制其他向量的方法。
我考虑创建一个接收函数指针的函数,并在向量的每个成员上调用指向函数。但我不确定在C ++中使用函数指针是一个好主意。
我一直在考虑仿函数和functionoids,但它会强迫我为每个函数创建一个类,这听起来像是一种矫枉过正。
另一种可能的解决方案是创建一个接收字符串的函数,并根据字符串调用该命令:
void Big::call_command(const string & command)
{
for (size_t i = 0; i < VectorOfSmalls.size(); i++)
{
if (command == "foo")
VectorOfSmalls[i]->foo();
else if (command == "bar")
VectorOfSmalls[i]->bar();
}
}
void Big::foo()
{
call_command("foo");
}
但它可能工作缓慢(不需要创建字符串而不仅仅是函数调用),并且如果函数具有不同的签名也会产生问题。
那么你会推荐什么?我应该保留现在的一切吗?
编辑:我只能使用STL而不能使用(旧编译器)。
答案 0 :(得分:16)
那么你可以重写for循环以使用迭代器和更多的STL:
void foo() {
std::for_each(VectorOfSmalls.begin(), VectorOfSmalls.end(), std::mem_fun(&Small::foo));
}
void bar() {
std::for_each(VectorOfSmalls.begin(), VectorOfSmalls.end(), std::mem_fun(&Small::bar));
}
除此之外,你可以使用一些宏来避免重新输入那么多,但我并不是那种狂热的粉丝。就个人而言,我喜欢使用命令字符串的单个函数。因为它为您提供了比决策更多的多功能性。
如果你选择使用一个参数决定做什么的单个函数,我会使用枚举和这样的开关,它会比字符串和级联更有效。此外,在您的示例中,您可以决定在循环内执行哪些操作。在循环外检查并拥有循环的冗余副本更有效,因为每个调用只需要决定“哪个命令”。 (注意:如果命令在编译时已知,则可以使命令成为模板参数,听起来就是这样)。
class Big {
public:
enum Command {
DO_FOO,
DO_BAR
};
void doit(Command cmd) {
switch(cmd) {
case DO_FOO:
std::for_each(VectorOfSmalls.begin(), VectorOfSmalls.end(), std::mem_fun(&Small::foo));
break;
case DO_BAR:
std::for_each(VectorOfSmalls.begin(), VectorOfSmalls.end(), std::mem_fun(&Small::bar));
break;
}
};
另外,正如您所提到的,替换&amp; Small :: whatever,成员函数指针并将其作为参数传递是非常简单的。你甚至可以把它变成一个模板。
class Big {
public:
template<void (Small::*fn)()>
void doit() {
std::for_each(VectorOfSmalls.begin(), VectorOfSmalls.end(), std::mem_fun(fn));
}
};
然后你可以这样做:
Big b;
b.doit<&Small::foo>();
b.doit<&Small::bar>();
关于这个和常规参数方法的好处是,如果你改变小以拥有更多例程,则不需要改变Big!我认为这是首选方法。
如果你想要能够处理单个参数,你还需要添加一个bind2nd,这是一个完整的例子:
#include <algorithm>
#include <functional>
#include <iostream>
#include <vector>
class Small {
public:
void foo() { std::cout << "foo" << std::endl; }
void bar(int x) { std::cout << "bar" << std::endl; }
};
class Big {
public:
template<void (Small::*fn)()>
void doit() {
std::for_each(VectorOfSmalls.begin(), VectorOfSmalls.end(), std::mem_fun(fn));
}
template<class T, void (Small::*fn)(T)>
void doit(T x) {
std::for_each(VectorOfSmalls.begin(), VectorOfSmalls.end(), std::bind2nd(std::mem_fun(fn), x));
}
public:
std::vector<Small *> VectorOfSmalls;
};
int main() {
Big b;
b.VectorOfSmalls.push_back(new Small);
b.VectorOfSmalls.push_back(new Small);
b.doit<&Small::foo>();
b.doit<int, &Small::bar>(5);
}
答案 1 :(得分:4)
如果您使用的是std库,则应该查看for_each。
你提到在C ++中使用函数指针可能不是一个好主意,但是 - 让你担心的是速度 - 在担心之前你必须看看这是否是你所处的性能瓶颈区域。
答案 2 :(得分:0)
尝试boost::function和boost::bind:
void Big::call_command(const boost::function<void (Small*)>& f)
{
for (size_t i = 0; i < VectorOfSmalls.size(); i++)
{
f(VectorOfSmalls[i]);
}
}
int main()
{
Big b;
b.call_command(boost::bind(&Small::foo, _1));
b.call_command(boost::bind(&Small::bar, _1, 5));
}