C++98 中获取并继续调用当前类不知道的类方法的最简单方法是什么?

What is the simplest way in C++98 to get and continue to call a class method, the type of which the current class does not know?

本文关键字:类方法 不知道 最简单 方法 是什么 获取 继续 调用 C++98      更新时间:2023-10-16

示例: 假设我们正在开发一个简单的图形界面:

我们有窗口和控件。我们想要什么:

  • 有一个实现 GUI 功能的基窗口类 对用户隐藏。
  • 从基窗口中,用户继承特定窗口。
  • 有一个按钮类,用户可以将其实例添加到他的窗口中。
  • 用户可以将窗口的方法转移到按钮,单击此按钮时将执行。

我对如何到达最后一点感兴趣。

伪代码的种类:

class BaseWindow;
class Button
{
public:
Button(BaseWindow* parent)
{
parent->AddButton(this);
}
void SetBehavior(/*Owners pointer and owner's methos*/)
{
/* Save Owner pointer and owner's method*/
}
void Clicked(/*coords*/)
{
if(/*coords == my coords*/)
{
/*Call Owner's method*/
}
}
};
class BaseWindow
{
vector<Button*> Buttons;
WindowClicked(/*coords*/)
{
for (std::vector<Button*>::iterator it = Buttons.begin(); it != Buttons.end(); ++it) 
{
it->Clicked(/*coords*/);
}
}
public:
void AddButton(Button* butt)
{
Buttons<<butt;
}
};
class UserWindow:public BaseWindow
{
Button MyButton;
public:
void FunctionForButton(Button* butt){ cout<<"Say Hello, my sweet button";}
UserWindow():MyButton(this)
{
MyButton.SetBehavior(/*Put here my Function for Button and my pointer*/);
}
};

您可以考虑使用一些"纯"抽象类(在概念上最好将其视为接口,所以我会这样称呼它们!)来创建用于处理这些事件的临时协议。

例子:

class IEventHandler
{
public:
virtual ~IEventHandler() { }
virtual void HandleEvent(IEventData* const pEventData) = 0;
};

IEventHandler提供了一个简单的抽象基类接口来处理事件,声明一个名为HandleEvent的方法,接受指向IEventData的指针的参数。

class IEventData
{
public:
virtual ~IEventData() = 0;
};
inline IEventData::~IEventData()
{
}

IEventData提供了一个简单的抽象基类接口,用于向IEventHandler::HandleEvent提供数据。

然后(使用您原来的伪代码作为起点,用更多的伪代码点缀:-)):

class BaseWindow;
class Button
{
public:
Button(BaseWindow* parent)
: pEventHandler_(NULL)
{
parent->AddButton(this);
}
void SetBehavior(IEventHandler* pEventHandler)
{
/* Save Owner pointer and owner's method*/
pEventHandler_ = pEventHandler;
}
void Clicked(/*coords*/)
{
if(/*coords == my coords*/)
{
IEventData* pData = new CoordinateEventData(/*coords*/);
/*Call Owner's method*/
pEventHandler_->HandleEvent(pData);
delete pData; /* if ownership not transferred to handler */
}
}
protected:
IEventHandler* pEventHandler_;
};
class BaseWindow
{
vector<Button*> Buttons;
WindowClicked(/*coords*/)
{
for(auto i:Buttons)
{
i->Clicked(/*coords*/);
}
}
public:
void AddButton(Button* butt)
{
Buttons<<butt;
}
};
class UserWindow:public BaseWindow
{
Button MyButton;
public:
void FunctionForButton(Button* butt){ cout<<"Say Hello, my sweet button";}
UserWindow():MyButton(this)
{
IEventHandler* pHandler = new CoordinateEventHandler(/*things and stuff*/);
MyButton.SetBehavior(pHandler);
}
};

您可以将适用于Button::Clicked方法中的坐标的数据打包到CoordinateEventData中。

您将转换/调整UserWindow::FunctionForButton方法的实现以CoordinateEventHandler::HandleEvent方法。

或者,你可以UserWindow"实现"(派生自)IEventHandler并使用来自(或通过调用)UserWindow::FunctionForButton的代码实现IEventHandler::HandleEvent,并使用MyButton.SetBehavior(this)将其连接起来。

像往常一样,您需要注意指针所有权以避免泄漏。

希望能给你一些想法...

使用std::function<ReturnCode(Args...)>

假设您有一个 UI 上下文结构:

struct UI_context {
mouse_position mouse;
keyboard_state keyboard;
};

假设按钮支持几种操作:

struct button_trigger {
bool click;
bool enter;
bool touch;
bool pen;
};

您可以要求按钮执行以下几项操作:

struct click_result {
bool toggle;
bool flash;
bool wiggle;
};

然后你会存储一个std::function< click_result( button_trigger, UI_context ) >.

当然,一个std::function<void()>可能就是你想要的。 或者std::function<void(mouse_location)>.

任何具有与您提供的签名兼容std::functionoperator()的内容都可以使用。 函数指针或 lambda 是最常见的两种。

您可能希望添加一种注册和删除回调的方法,和/或拥有令牌系统。