在c++中将指向成员函数的指针传递给父函数
Passing pointer to member function to parent in c++
如何使以下代码正常工作?我不能让成员静态,Parent不知道Child,我也无法获得boost。我不使用虚拟函数的原因是Child类应该能够定义1-N处理程序。
class Parent
{
public:
void registerFileHandler(string ext, memFuncPtr);
};
class Child : public Parent
{
Child()
{
registerFileHandler("jpg", &Child::loadJpg);
registerFileHandler("png", &Child::loadPNG);
}
void loadJpg(string filename);
void loadPNG(string filename);
};
编辑:答案很多。最适合我的是使用关键字擦除、std::bind和std::function,它们当然依赖于c++11。以下是一个完整的可编译示例:
#include <string>
#include <map>
#include <iostream>
#include <functional>
using namespace std;
class Parent
{
public:
void load(string filename)
{
// See if we can find a handler based on the extension.
for(auto it = handlers.begin();it!=handlers.end();it++)
if(filename.substr(filename.size()-it->first.size(), it->first.size())==it->first)
it->second(filename);
}
template<typename Class>
void registerFileHandler(Class* p, void (Class::*func)(string), string ext)
{
using namespace std::placeholders; //for _1, _2, _3...
handlers[ext] = std::bind(func, p, _1);
}
private:
map<string, std::function<void(string)> > handlers;
};
class Child : public Parent
{
public:
Child()
{
registerFileHandler(this, &Child::loadJpg, "jpg");
registerFileHandler(this, &Child::loadPNG, "png");
}
void loadJpg(string filename)
{
cout << "loading the jpeg "<< filename << endl;
}
void loadPNG(string filename)
{
cout << "loading the png "<< filename << endl;
}
};
int main(int argc, char* argv[])
{
Child child;
child.load("blah.jpg");
child.load("blah.png");
return 0;
}
std::function
和std::bind
:怎么样
class Parent
{
public:
void registerFileHandler(string ext, const std::function<void(string)> &f)
{
}
};
class Child : public Parent
{
public:
Child()
{
using namespace std::placeholders; //for _1, _2, _3...
registerFileHandler("jpg", std::bind(&Child::loadJpg, this, _1));
registerFileHandler("png", std::bind(&Child::loadPNG, this, _1));
}
...
您需要某种形式的类型擦除。假设你不能使用任何已经存在的复杂的(boost::function
,std::function
(,那么你可以推出自己的:
class MemFuncPtr {
void *obj;
void (*caller)(void*, string);
public:
MemFuncPtr(void *obj, void(*caller)(void*, string)) : obj(obj), caller(caller) {}
void operator()(string filename) {
caller(obj, filename);
}
};
class Child : public Parent
{
Child()
{
registerFileHandler("jpg", MemFuncPtr(this, &jpgcaller));
registerFileHandler("png", MemFuncPtr(this, &pgncaller));
}
void loadJpg(string filename);
void loadPNG(string filename);
private:
static void jpgcaller(void *obj, string filename) {
static_cast<Child*>(obj)->loadJpg(filename);
}
static void pngcaller(void *obj, string filename) {
static_cast<Child*>(obj)->loadPng(filename);
}
};
我认为您可以使用带有指向成员模板参数的指针的函数模板来消除那些static
函数。但如果我在没有测试的情况下编写代码,我可能会把它弄得一团糟…
关于如何传递函数指针(您可能需要稍微重新设计(以及正确使用继承,有很多建议。。
class FileLoader
{
virtual void load() = 0; // real load function
};
class LoadManager
{
// Here is your registry
std::map<std::string, std::uniqe_ptr<FileLoader>> _loaders;
};
class JpegLoader : public FileLoader
{
};
class BitmapLoader : public FileLoader
{
};
// etc.
// Now register these with the LoadManager and use from there...
这个设计看起来是不是更清晰了一点?显然,这个建议是基于你在那里发布的简单片段,如果你的架构更复杂,那就另当别论了。。。
要使代码正常工作,registerFileHandler()
应该向将加载图像的实际子对象传递一个指针。
最好定义static Child Child::loadPNG(string filename)
,但这取决于Child和Parent对象的实际作用
class Child; // unfortunately this is needed
class Parent
{
public:
void registerFileHandler(string ext, void (Child::*mem_fn) (string), Child* obj) {
// ^^^^the member func ^^^^^^^^ //the object to be used
(obj->*mem_fn)("test");
}
};
class Child : public Parent
{
Child()
{
registerFileHandler("jpg", &Child::loadJpg, this);
registerFileHandler("png", &Child::loadPNG, this);
// You need to pass this.
}
void loadJpg(string filename);
void loadPNG(string filename);
};
然而,你所做的可能有些过头了。你为什么不能用这样的东西?
class Parent
{
public:
virtual void registerFileHandler(string ext) = 0;
};
class Child : public Parent
{
Child()
{
registerFileHandler("jpg");
registerFileHandler("png");
}
void loadJpg(string filename);
void loadPNG(string filename);
virtual void registerFileHandler(string ext) {
loadJpg(ext);
loadJpg(ext);
}
};
声明明智,你可以逃脱
class Parent
{
public:
template <typename Child>
void registerFileHandler(string ext, void (Child::*memFuncPtr)(string filename));
};
当然,这并不能解决如何存储这些指针的问题。一种常见的解决方案是
struct Handler {
virtual ~Handler() { };
virtual void Load(Parent*, std::string) = 0;
};
template<typename Child>
struct ChildHandler {
void (Child::*memFuncPtr)(string filename);
virtual void Load(Parent* p, string filename) {
dynamic_cast<Child&>(*p).*memFuncPtr(filename);
}
};
这当然是另一种类型擦除形式。
class Parent
{
public:
// so we don't need to static-cast at the call site
template <typename ChildT>
void registerFileHandler(string ext, void (ChildT::*func)(string)) {
doRegisterFileHandler(ext, static_cast<void (Parent::*func)(string)>(func));
}
void doRegisterFileHandler(string ext, void (Parent::*func)(string));
};
class Child : public Parent
{
Child()
{
registerFileHandler("jpg", &Child::loadJpg);
registerFileHandler("png", &Child::loadPNG);
}
void loadJpg(string filename);
void loadPNG(string filename);
};
你照常调用函数:
this->*func(string)
在父级中。
规范说这应该有效(C++03中的5.2.9/9,C++11中的5.2.9/12(,MFC在消息映射中使用它。然而,Microsoft Visual C++是一个编译器,它实际上在所有情况下都能而不是工作。因为微软提出了这种"成员指针的最小表示法",根据类是否使用多重继承或虚拟继承,指向成员的指针的表示方式不同。因此,如果子级使用多重继承,而父级不使用,则无法执行强制转换,因为指向具有多重继承的类的成员的指针不适合指向具有单一继承的类成员的指针。编译器可以选择始终使用泛型格式,但使用和不使用泛型格式编译的代码是二进制不兼容的。
- QMetaObject invokeMethod的基于函数指针的语法
- C++-试图将函数指针推回到另一个CPP文件中的矢量时出错
- c++r值引用应用于函数指针
- 模板函数指针和lambda
- 是否可以将llvm::FunctionType转换为C/C++原始函数指针
- 带有类的函数指针
- () 函子后面的括号,而不是函数指针?
- 全局作用域中函数指针的赋值
- 使用"Task"函数指针队列定义作业管理器
- 将成员函数指针作为参数传递给模板方法
- 如何创建对象函数指针C++映射?
- 匹配函数指针作为模板参数?
- 通过函数指针定义类范围之外的方法
- 存储在类中的函数指针
- C++从函数指针数组调用函数
- 将返回值存储在函数指针数组的指针中是如何工作的?
- 整数键映射到头文件中的成员函数指针
- 从类成员函数到类 C 函数指针的转换
- 如何将内联匿名函数分配给C++函数指针
- 将字符缓冲区强制转换为函数指针