I have an abstract class Interface
that declares f()
and g()
methods.
MyClass
implements Interface::g()
, and MyClass
inherits from Base
, which implements its own f()
.
How can I make Base::f()
be the implementation for Interface::f()
in MyClass
?
I don't want to make Base
inherit from Interface
directly, if possible.
class Interface {
public:
virtual void f() = 0;
virtual void g() = 0;
};
class Base {
public:
void f() { }
};
class MyClass : public Interface, public Base {
public:
void g() { }
};
int main() {
MyClass c;
c.f();
c.g();
}
This doesn't work, because f
is still a pure virtual function in MyClass
.
In Java, I would use the following approach, but it obviously doesn't work in C++.
public class MyClass extends Base implements Interface {
public static void main(String argv[]) {
MyClass c = new MyClass();
c.f();
c.g();
}
public void g() { }
}
class Base {
public void f() { }
}
interface Interface {
public void f();
public void g();
}
The concrete problem I'm trying to tackle is the following:
The Interface
in the example above is an abstract class Sender
with different methods related to sending MIDI messages, e.g. update()
(check if a message should be sent, and send it if necessary), and setAddress()
to change the address of the message. The messages are MIDI events. I have to change the address/channel for functions like transposing and selecting different banks.
The Base
class is a class Button
. It has a method update()
that checks whether the button is pressed or not, and calls the (virtual) methods press()
and release()
accordingly.
MyClass
would then implement the press()
and release()
methods, to send the right data when the button state changes.
There are other types of Sender
s as well, and I keep them all in a list to update()
them all at once, regardless of the specific type of Sender
. I also need to be able to change the address of all Sender
s in the list (all at once).
The setAddress()
method has nothing to do with a button, so it doesn't make sense to have Button
inherit from Sender
.
There are also other classes that would implement Button
, but they are not all Sender
s.
Maybe I have to change the structure entirely, but I don't really see a better solution at the moment.
class Button {
public:
void update() {
if ( /* check in hardware whether button is pressed */ )
press();
}
virtual void press() = 0;
};
class Sender {
public:
virtual void update() = 0;
virtual void setAddress(int address) = 0;
};
class ButtonSender : public Button, public Sender {
public:
void update() {
Button::update();
}
void press() {
/* send a message */
}
void setAddress(int address) {
/* change the address */
}
};
int main() {
ButtonSender bs;
Sender *s = &bs;
s->update();
}
Thank you, OP, for posting a more concrete example. It has driven me to take the unusual step of posting a second answer. I hope you find it useful.
What I'm going to do here is to show you an alternative class hierarchy which I hope you will see is better suited to the problem you want to solve. I'm just going to focus on the relationship between class Button
(which is where the fundamental problem lies) and something which I'm going to call a ButtonHandler
.
Now a ButtonHandler
could be anything - Button
certainly doesn't care what it is - it just needs to be notified when the button that it owns is pressed or released. Once you appreciate that that is at the heart of the problem, the solution becomes more obvious. You can, of course, apply this approach to your Sender
class (just derive it from ButtonHandler
and implement OnPress
and OnRelease
), but I'll leave that bit to you.
Finally, as a bit of icing on the cake, I had my Button
s add and remove themselves to / from that list you were talking about so that they can easily all be polled in some kind of loop.
OK, let's go. Let's start with our ButtonHandler
(since this needs to be declared before class Button
), which is simple enough:
#include <set>
#include <iostream>
class Button;
// Our abstract button handler - derive concrete handlers from this
class ButtonHandler
{
public:
// Constructor
ButtonHandler ();
// Destructor (must be virtual!)
virtual ~ButtonHandler ();
virtual void OnPress () = 0;
virtual void OnRelease () = 0;
private:
Button *m_button;
};
OK, not much to see there. Note the pure virtual methods OnPress
and OnRelease
- which are there for concrete classes deriving from this one to override - and the forward declaration of class Button
. We will need to implement the constructor and destructor bodies later, after they have seen the full declaration of class Button
, since they need to call methods on it.
Now for class Button
itself, and the associated ButtonList
. Note that, again, because it deals only in pointers to Button
s, ButtonList
doesn't need to see the full declaration of class Button
, which is kind of handy here:
std::set<Button *> ButtonList;
// class Button
class Button
{
public:
// Constructor
Button (ButtonHandler *button_handler)
{
m_button_handler = button_handler;
ButtonList.insert (this);
}
// Destructor
~Button () { ButtonList.erase (this); }
// Poll the button state
void Poll ()
{
// This would normally check (in hardware) whether button is pressed, but here we just fake it
m_button_handler->OnPress ();
m_button_handler->OnRelease ();
}
private:
ButtonHandler *m_button_handler;
};
// Poll all the buttons that exist
void PollAllButtons ()
{
for (auto button : ButtonList) { button->Poll (); }
}
So what's going on here? Well:
Button
constructor is passed a pointer to a ButtonHander
so that it can call its OnPress
and OnRelease
methods when appropriate.ButtonList
trivial. It just uses a
std::set
, which is documented here (see examples on that page and linked pages). Buttons add and remove themselves from this list as they come and go.PollAllButtons
iterates through all the Button *
s stored in ButtonList
and calls what I chose to call Poll
for each one, which in turn calls the associated ButtonHandler
s OnPress
and OnRelease
methods as appropriate. This uses something called a "ranged for loop", which works with, AFAIK, all the STL container classes.Now the implementation of the ButtonHandler
constructor and destructor. These just create and delete the associated button (so, the Buttonhandler
owns the button):
// ButtonHandler constructor - implementation
ButtonHandler::ButtonHandler ()
{
m_button = new Button (this);
}
// Buttonhandler destructor - implementation
ButtonHandler::~ButtonHandler () { delete m_button; }
And finally, a concrete button handler and minimal test program that is included here to show you that all this stuff does actually work. You will notice (and this is rather nice) that this is unchanged from the previous code I posted (which was just that bit too clever):
// An example concrete button handler
class MyButtonHandler : public ButtonHandler
{
public:
// Constructor
MyButtonHandler (int handler_id) { m_handler_id = handler_id; }
void OnPress () override { std::cout << "MyButtonHandler::OnPress (" << m_handler_id << ")" << std::endl; }
void OnRelease () override { std::cout << "MyButtonHandler::OnRelease (" << m_handler_id << ")" << std::endl; }
// ...
private:
int m_handler_id;
};
// Test program
int main ()
{
MyButtonHandler bh1 (1);
MyButtonHandler bh2 (2);
PollAllButtons ();
}
Output:
MyButtonHandler::OnPress (1)
MyButtonHandler::OnRelease (1)
MyButtonHandler::OnPress (2)
MyButtonHandler::OnRelease (2)
Run it at Wandbox.
So there you go. Very different to yours (because you set off on the wrong track and there was no way back from there). I suggest you take it and run with it, best of luck.