【发布时间】:2017-11-16 10:36:03
【问题描述】:
如何适当缓存用户callbackBegin()生成的userData,并将其发送到用户callbackEnd()。
简单版(无 userData - demo)
我想创建一个支持回调的复杂数据库。对于 MCVE,假设它是 MyArray。
这是一个简单的数组类,支持回调但不支持userData。
#include <iostream>
template<class Derived>class MyArray{ //library - I design it.
public: void push_back(int s){
static_cast<Derived*>(this)->callbackBegin(s);
//do something about array
static_cast<Derived*>(this)->callbackEnd(s);
}
//other fields / functions
};
class Callback : public MyArray<Callback>{ //user's class
public: void callbackBegin(int s){
std::cout<<"callbackBegin"<<std::endl;
}
public: void callbackEnd(int s){
std::cout<<"callbackEnd"<<std::endl;
}
};
int main() {
Callback c;
c.push_back(5); //print: callbackBegin callbackEnd
return 0;
}
它工作正常。
下一步:我想将一些 userData 从Callback::callbackBegin() 传递给Callback::callbackEnd()。
例如,userData 是调用Callback::callbackBegin() 时的时钟时间。
我的糟糕解决方案 (void*& userdata : demo)
这是我实现它的尝试:-
#include <iostream>
#include <time.h>
template<class Derived>class MyArray{
public: void push_back(int s){
void* userData=nullptr; //#
static_cast<Derived*>(this)->callbackBegin(s,userData); //# ugly
//do something about array
static_cast<Derived*>(this)->callbackEnd(s,userData); //# ugly
}
};
class Callback : public MyArray<Callback>{
public: void callbackBegin(int s,void*& userData){ //#
userData=new clock_t(clock()); //# danger
std::cout<<"callbackBegin"<<std::endl;
}
public: void callbackEnd(int s,void*& userData){ //#
clock_t* userDataTyped=static_cast<clock_t*>(userData);
clock_t clock2=clock();
clock_t different=clock2 - (*userDataTyped);
std::cout<<"callbackEnd time(second)="
<<((float)different)/CLOCKS_PER_SEC<<std::endl;
delete userDataTyped; //# danger
}
};
int main() {
Callback c;
c.push_back(5); //print: callbackBegin callbackEnd time(second)=8.5e-05
return 0;
}
它也可以正常工作,但我认为这是一个糟糕的设计(在各种#):-
-
new/delete在 2 个地方:潜在的内存泄漏。
强指针是首选,但我不知道如何。 -
static_cast<clock_t*>(userData)是代码味,至少对我来说是这样。 - (小问题)一个额外丑陋的参数
void*&
问题:什么是设计模式/C++ 魔法可以避免此类问题,同时使MyArray 简洁、易于使用、可维护(即不比简单版本差多少)?
其他说明:
在实际情况下,userData。
因此,我觉得很不情愿添加void&*作为额外参数。
澄清:(已编辑) 少数情况通常需要不同类型的userData 例如Callback1需要clock_t、Callback2需要std::string等建议的解决方案应避免使用
std::function<>或virtual function,因为性能是这里的主要问题。
谢谢。
【问题讨论】:
-
1) 您的基类和派生类是紧密耦合的,其中基类具有派生的知识,因此您可能需要考虑重新设计 2) 将 userdata 设为 Derived 有什么问题?
-
@mnistic 1)
MyArray只知道Derived有 2 个具有特定签名的函数。我认为这是回调中的常见模式,我不认为回调是紧密耦合的。 2) 每个回调实例是userData,而不是每个调用。多线程将变得不可能。无论如何,这是一个我从未想过的好主意。谢谢! -
@cppBeginner 任何人都可以使用静态为所有实例创建一个
userData -
@Nikita 我认为每 1 次调用
push_back()需要 1 个userData实例,而不是每 1 个Callback实例或 1 个程序实例。如果不清楚,请见谅。