对不起,这可能是一个愚蠢的问题。我显然误解了面向对象编程的基本原理。我习惯了C,现在正在尝试使用C ++。
我在一个名为Button的类中有一些按钮。每个按钮都有不同的功能。我想要写的东西是这样的:
Button button1;
Button button2;
...
void button1::onClick () {
...
}
void button2::onClick () {
...
}
但这不起作用("按钮1不是类,命名空间或枚举" - 是的,我知道!)。我知道我可以为每个按钮单独创建一个类:
class button1_class : public Button {
public:
void onclick () {
...
}
} button1;
class button2_class : public Button {
...
}
但对我而言,感觉'当我确定它只会有一个成员时,上课是错误的。
我使用的是Allegro 5的GUI库Agui。
修改
感谢您的回复。虽然它们都是有用的(我认为)所有有效答案,但实际上还没有人说过#34;不,你不能用自己独特的方法拥有一个对象,因为......"
因此,例如,如果object1的类型为ObjectClass,则不允许object1拥有object1唯一的方法(成员函数),而是仅拥有 定义为的方法ObjectClass的一部分。是对的吗? 对不起,我没有包括我的实际用例。我更感兴趣的是只是让我的头围绕OOP,这样我就可以自己完成。
EDIT2 更详细地看一下响应,我认为使用lambda表达式是可能的,它不会像我想象的那样。再次感谢
答案 0 :(得分:7)
自然的C ++方式就像vsoftco解释的那样,具有虚拟和继承。
但是,如果您的Button
类已经完成了所有需要,并且按钮之间唯一的变化是要执行的唯一(trhow-away)操作,您可能需要考虑这个替代方法:
class Button {
function<void()> f;
public:
Button(function<void()> mf) : f(mf) {}
void onClick() { f(); }
};
您的类的这个变体使用 function
对象(将其视为一种函数指针,但使用起来要灵活得多)。
然后您可以将其与 lambda-functions 一起使用,如下例所示:
int main(int ac, char**av)
{
Button button1([&]() { cout << "Hello 1!\n"; });
Button button2 ([]() { cout << "Hello 2!\n"; });
button1.onClick();
button2.onClick();
}
答案 1 :(得分:3)
如果按钮具有不同的功能,最好的办法是创建一个BaseButton
类,在其中将onclick()
标记为virtual
(或将其设为纯虚拟,这将使BaseButton
一个抽象类),然后从BaseButton
派生出彼此的按钮,确保在每个派生类中重写onclick()
。然后,您需要通过引用或指针BaseButton
来使用按钮,这样您就可以实现所谓的&#34;多态行为&#34;。
例如:
class BaseButton
{
virtual void onclick() {/*implement here or declare pure virtual*/}
};
class RedButton: public BaseButton /* overrides only onclick */
{
void onclick() override { /*specific implementation for Red Buttons */}
};
class ShinyRedButton: public RedButton /* overrides only onclick */
{
void onclick() override { /*specific implementation for Shiny Red Buttons */}
};
然后使用它(C ++ 14智能指针)
std::unique_ptr<BaseButton> bb = new ShinyRedButton;
bb->onclick(); // will pick up the "right" ShinyRedButton::onclick()` function
答案 2 :(得分:3)
你可以通过多种方式做到这一点。
使用Button
类,其中按钮对象具有指向调用的方法的指针onClick
。在C中你可以使用回调来做到这一点,你也可以在C ++中这样做:
class Button {
using funType = void(void);
public:
Button(funType* callback) : function(callback) { }
void onClick() { function(); }
private:
funType* function;
};
但请注意,函数指针容易出错,编译器无法真正内联,一般应避免使用。此方法也适用于无捕获的lambdas。
Button red([] { std::cout << "Red button\n"; });
Button green(&green_button_function);
动态创建具有不同Button
方法的不同onClick
个对象。 C ++有一种机制来执行此操作,称为模板:
template <class Fun>
class Button {
public:
Button(Fun f) : functor(f) { }
void onClick() { functor(); }
private:
Fun functor;
};
template <class Fun>
Button<Fun> make_button(Fun f) { return Button<Fun>(f); }
我在这里省略了有关参考的详细信息。
然后,您可以通过以下方式使用带有回调的Button
类以及lambdas:
auto green = make_button([] { std::cout << "Green button pressed!\n"; });
auto red = make_button(&red_button_function);
您需要在此方法中使用auto
,否则您必须手动指定功能的类型,这是不可能的,例如:对于lambda对象。
使用vsoftco所示的多态,您可以在其中为每个Button
功能创建单独的类。或者,您可以创建一个ButtonAction
抽象类,Button
具有引用。然后,您在不同的类中实现不同的功能,但保持一个Button
类。这称为strategy pattern:
class ButtonAction {
public:
virtual void onClick() = 0;
};
class Button {
public:
Button(std::unique_ptr<ButtonAction> action) :
action_(std::move(action)) {}
void onClick() { action_->onClick(); }
private:
std::unique_ptr<ButtonAction> action_;
};
class RedButtonAction : public ButtonAction {
void onClick() override { red(); }
};
class GreenButtonAction : public ButtonAction {
void onClick() override { green(); }
};
使用此方法需要在Button
ButtonAction
s
unique_ptr
s
Button red(std::unique_ptr<ButtonAction>(new RedButtonAction));
Button green(std::unique_ptr<ButtonAction>(new GreenButtonAction));
答案 3 :(得分:2)
你是对的,如果每个按钮基本相同但需要绑定不同的事件处理程序,那么为每个按钮实现一个新类型并不完全正确。
相反,您的Button
类型会有一个成员函数,允许用户附加&#34;一个事件处理程序,以及一个调用它的成员函数。
class Button
{
public:
Button()
: onClickHandler()
{}
void setOnClickHandler(std::function<void()> callback)
{
onClickHandler = callback;
}
friend class UI;
private:
void onClick()
{
onClickHandler();
}
std::function<void()> onClickHandler;
};
然后您的用户执行:
void foo()
{
std::cout << "Some buttons do this!\n";
}
Button btn;
btn.setOnClickHandler(foo);
您的程序内部会设置一些内容,以便您的窗口管理员(我以上认为它是一个名为UI
的类)调用btn.onClick()
你,因为你&#34;附加&#34; foo
,最终会调用foo
。
(在现代C ++中,你可能会使用lambda函数来整理它,但上面是一个简单的示例来展示一般的设计理念。)
通过这种方式,您可以将不同的处理程序附加到不同的Button
实例,但Button
接口本身是稳定的。
这与您在JavaScript中操作DOM的方式类似。
答案 4 :(得分:1)
使用std :: function是关键。如果您的可调用(lambda,函数,成员函数)很大,您将听到虚拟调用和潜在的内存分配。这实现了对单个类型执行不同回调的要求,而没有定义类继承。同样使用统一初始化使得使用lambda构造Button类非常方便,而无需手动创建构造函数。
实例: http://coliru.stacked-crooked.com/a/f9007c3f103f3ffe
#include <functional>
#include <vector>
using namespace std;
struct Button
{
function<void()> OnClick;
};
int main()
{
vector<Button> buttons =
{
{[] { printf("Button0::OnClick()\n"); }},
{[] { printf("Button1::OnClick()\n"); }},
{[] { printf("Button2::OnClick()\n"); }},
};
for(auto&& button : buttons)
button.OnClick();
}
答案 5 :(得分:1)
您的Agui library支持信号系统,其成员函数为addActionListener
。
这允许您从agui::ActionListener
派生一个类来执行用于一个或多个按钮的特定任务:
class SimpleActionListener : public agui::ActionListener
{
public:
virtual void actionPerformed(const agui::ActionEvent &evt)
{
std::cout << "Button pushed" << std::endl;
}
};
上面的对象可以通过以下方式附加到按钮的“按下”操作:
SimpleActionListener simpleAL;
button1.addActionListener(&simpleAL);