将类成员函数作为参数从同一类的另一个成员函数传递

pass class member function as argument from another member function of the same class

本文关键字:函数 成员 一类 另一个 参数      更新时间:2023-10-16

我提供了我正在处理的问题的一个更简单的版本。

我有一个类Entity,它有一个user对象的map。为了简单起见,我只添加了一个元素(name)。我已经围绕userMap对象实现了gettersetter

wrapper函数对我来说是必需的,用于处理多线程情况下的关键情况。(此处未显示)。我通过一些SO线程以某种方式实现了一个工作示例。

但正如您所看到的,当在procedure函数中使用wrapper函数时,我必须在三个地方编写(id,arg)元组参数。是否有一种方法可以通过为函数参数设置某种占位符来实现相同的功能,以便调用包装器看起来很干净,即wrapper(1,static_cast<void*>(&name),function_ptr)

#include <iostream>
#include <functional>
#include <map>
using namespace std;
class Entity {
public:
struct user {
int id;
string name;
};
map<int,user> userMap;
void getUserName(int id,void * arg);
void setUserName(int id,void * arg);
void procedure();
void wrapper(int id,void*,std::function<void(int,void*)>);
};
void Entity::getUserName(int id,void *arg) {
auto iter = userMap.find(id);
if(iter != userMap.end()) {
*static_cast<string*>(arg) = iter->second.name; 
}
}
void Entity::setUserName(int id,void *arg) {
auto iter = userMap.find(id);
if(iter != userMap.end()) {
iter->second.name = *static_cast<string*>(arg);
}
}
void Entity::wrapper(int id,void *arg,std::function<void(int,void*)> func) {
cout <<"nSome Other critical task based on id"<< endl;
// then call the callback
func(id,arg);
}
void Entity::procedure() {
cout <<"Procedure starts"<<endl;
user u = {};
u.id = 1;
u.name = "abc";
this->userMap[1] = u;
string name;
wrapper(1,static_cast<void*>(&name),[this](int id,void*arg){
getUserName(id,arg);
});
cout <<"name :"<< name << endl;
cout <<"nSome processing..."<<endl;
name = "def";
wrapper(1,static_cast<void*>(&name),[this](int id,void*arg){
setUserName(id,arg);
});
cout <<"nSome more processing..."<<endl;
wrapper(1,static_cast<void*>(&name),[this](int id,void*arg){
getUserName(id,arg);
});
cout <<"name :"<< name << endl;
cout <<"Procedure ends"<<endl;
}
int main() {
Entity E;
E.procedure();
}

输出:

Some Other critical task based on id
name :abc
Some processing...
Some Other critical task based on id
Some more processing...
Some Other critical task based on id
name :def
Procedure ends

谢谢!

我将从一个直接的答案开始,但请继续阅读下面的警告。

您尝试执行的操作的一个更简单的语法是将第三个参数的类型更改为wrapper()。不要将成员函数转换为非成员函数,只需将成员函数作为参数即可。

void wrapper(int id, void* arg, void (Entity::* func)(int,void*))
{
cout <<"nSome Other critical task based on id"<< endl;
(this->*func)(id, arg);
}

然后你的电话会变得更简单:

wrapper(1, static_cast<void*>(&name), &Entity::getUserName);
wrapper(1, static_cast<void*>(&name), &Entity::setUserName);

但是这真的是你想要采取的方法吗?你依赖于人们记住调用包装器,而不是直接调用其他成员函数。如果你在某个时候忘记使用包装器,你能根据症状找到原因吗?你想试试吗?

一种更稳健的方法是将关键任务放在自己的函数中,然后让getter和setter调用它。更像:

void criticalStuff(int id)
{
cout <<"Some critical task based on idn";
}
void Entity::getUserName(int id, string & arg)
{
criticalStuff(id); // Critical stuff first.
auto iter = userMap.find(id);
if(iter != userMap.end()) {
arg = iter->second.name; 
}
}
void Entity::setUserName(int id, const string & arg)
{
criticalStuff(id); // Critical stuff first.
auto iter = userMap.find(id);
if(iter != userMap.end()) {
iter->second.name = arg;
}
}

任何添加新字段的人在编写新的getter和setter时都必须记住关键内容,但更常见的情况(调用这些函数)会自动处理关键内容。此外,您还可以恢复参数的类型检查,因为似乎不再有理由将内容强制转换为void *

在我看来,强迫使用lambda会给自己带来问题,我知道它们很有趣,但通常用处不大。一般来说,保持简单愚蠢(KISS),在您的情况下,只需将函数划分为两个单独的函数。

class Entity {
public:
...            
void getUserName(int id,void * arg);
void setUserName(int id,void * arg);
void procedure();
void criticalTask(int id); // critical task, based on id
};
void Entity::getUserName(int id,void *arg) {...}
void Entity::setUserName(int id,void *arg) {...}
void Entity::criticalTask(int id) {
cout <<"nSome Other critical task based on id"<< endl;
}
void Entity::procedure() {
cout <<"Procedure starts"<<endl;
...
criticalTask(id);
getUserName(id, &name);
...
}
int main() {
Entity E;
E.procedure();
}