|
-
February 8th, 2008, 05:41 AM
#1
Preventing methods from being inherited
Hi everyone! As usual, I come to you with an almost absurd question.
I'm coding some classes for a MIDI application. As probably some of you know, the MIDI channel events (the most commonly used ones) are a string of three bytes, where the first byte determines what kind of message the event is. For example, 0x9n is a Note On on channel n, while 0x8n is a Note Off on that channel. Other bytes specify parameters.
What I would now want to do is to simplify message creation by defining a class for each kind of message. Like a class MIDINoteOn, a class MIDINoteOff, a class MIDIControlChange and so on. Since most of the functionality and of the status of these objects is common, I would define a base class MIDIChannelEvent from which the other would inherit. It would contain an array of three bytes (or a vector maybe, for safety reasons) and the necessary methods.
The problem is: the generic class could allow the user to specify what kind of message it should be (through a SetEventKind() method), so that a generic channel event can be a Note On or a Note Off or anything else, but the derived classes shouldn't (a Note On should always be just a Note On). In a sense, the very purpose of having one class per kind is exactly to let the user create a Note On without even knowing that it's almost the same as a Control Change.
But if I make the SetEventKind() method public in the base class, then the derived classes will inherit it (and it will be public). I thought of making the method reserved in the base class, but since I need the user of the class to access it I'd have to define a public "wrapper" for it, and this wrapper would be inherited as public. I also thought of making the inheritance private or protected (class MIDINoteOn : protected MIDIChannelEvent), but there's some functionality in the base class that needs to be public.
What would you suggest in order to have the method "not being inherited"? Thanks everyone!
I owe Paul McKenzie a pizza.
I am no expert; but they say I can make concepts easy to understand. That's why newbies questions are mine!!! XD
-
February 8th, 2008, 06:00 AM
#2
Re: Preventing methods from being inherited
It's difficult to understand the context here without some code, but have you considered inheriting publically, but making the class functions you want invisible protected?
Code:
class A
{
public:
int publicFunction(){return 5;}
virtual ~A(){}
protected:
int protectedFunction(){return 7;}
};
class B : public A
{
};
int main()
{
B b;
std::cout << b.publicFunction() << std::endl; //OK
std::cout << b.protectedFunction() << std::endl; //compile error
};
That way class B can see the protected member functions of class A but the functions are not public to those outside.
-
February 8th, 2008, 06:12 AM
#3
Re: Preventing methods from being inherited
It seems your class hierarchy is upside-down - the substitution principle says that you can us a subtype wherever you can use a supertype, so a MIDIChannelEvent is a bottom rather than a top of the hierarchy (much like null is a bottom of all types in other OO languages where you can use it for any reference).
This is a common problem where capability and structure appear to work in opposite directions on a lattice.
You could have a MIDIGenericChannelEvent which has the mutator function, which would let the other channel events be of fixed type, though if you want a MIDIGenericChannelEvent which is configured as a note on event to expose functions specific to MIDINoteOn, then you may need a more dynamic form of typing than C++ inheritance supplies out of the box.
You also might want to consider what client code which uses the generic events looks like - it may be that you really only want a factory, or it's OK for not all of the functionality to be exposed.
-
February 8th, 2008, 06:27 AM
#4
Re: Preventing methods from being inherited
Hi PredicativeNormative! I'll comment on your suggestion before posting an example of code.
What you suggest is one of the solutions I came up with, but lacks a feature I need. The functionality that I don't want to inherit *is intended to be public* in the base class. I'd want it to work like this:
Code:
class A
{
public:
int GordianKnot();
int SimpleAsHell();
};
class B : public A
{
public:
/*int SimpleAsHell(); // same version of A */
protected:
/*int GordianKnot(); // same version of A */
};
int main(int argc, char **argv)
{
A base;
B derived;
A.SimpleAsHell(); // OK
A.GordianKnot(); // OK
B.SimpleAsHell(); // OK
B.GordianKnot(); // Can't do
return 0;
}
There's no need even to declare the methods virtual and re-define them. What I can't understand is how to mix the public inheritance of some members with the private/protected inheritance of other members.
Edit
In response to kirkham: I know it might be a design problem. Basically I might have a factory, with a static CreateNoteOn() method that produces a MIDIChannelEvent with the first half-byte set to 9 and a similar method for each of the other events. That way, the SetEventKind() could be protected at least, and the user wouldn't be able to edit it.
But I'm writing a class library, so the client code *might* want to do such things as "convert all NoteOff events to NoteOn with velocity 0" (as it's sometimes done on some hardware). So *in the general case* the first half-byte might need changing.
Last edited by Sk#; February 8th, 2008 at 06:36 AM.
I owe Paul McKenzie a pizza.
I am no expert; but they say I can make concepts easy to understand. That's why newbies questions are mine!!! XD
-
February 8th, 2008, 07:01 AM
#5
Re: Preventing methods from being inherited
It does seem your inheritance is upside-down, that said, if you require a more complex relationship you can always restructure using virtual inheritance.
Code:
#include<iostream>
class MIDIChannelEvent
{
protected:
virtual void function1(){}
virtual void function2(){}
virtual void function3(){}
virtual void function4(){}
virtual void function5(){}
virtual void function6(){}
virtual ~MIDIChannelEvent() = 0;
};
MIDIChannelEvent::~MIDIChannelEvent(){}
class MIDINoteOn : virtual public MIDIChannelEvent
{
public:
void function1(){MIDIChannelEvent::function1();}
void function2(){MIDIChannelEvent::function2();}
virtual ~MIDINoteOn(){}
protected:
void functionON(){}
};
class MIDINoteOff : virtual public MIDIChannelEvent
{
public:
void function3(){MIDIChannelEvent::function3();}
void function4(){MIDIChannelEvent::function4();}
virtual ~MIDINoteOff(){}
protected:
void functionOFF(){}
};
class MIDIControlChange : virtual public MIDIChannelEvent
{
public:
void function5(){MIDIChannelEvent::function5();}
void function6(){MIDIChannelEvent::function6();}
virtual ~MIDIControlChange (){}
protected:
void functionChange(){}
};
class MIDIGenericChannelEvent : public MIDIControlChange,MIDINoteOff,MIDINoteOn
{
public:
//I've made all public here which isn't what you'll want but shows you how to do it.
void function1(){MIDINoteOn::function1();}
void function2(){MIDINoteOn::function2();}
void function3(){MIDINoteOff::function3();}
void function4(){MIDINoteOff::function4();}
void function5(){MIDIControlChange::function3();}
void function6(){MIDIControlChange::function4();}
void functionON(){MIDINoteOn::functionON();}
void functionOFF(){MIDINoteOff::functionOFF();}
void functionChange(){MIDIControlChange::functionChange();}
};
int main()
{
MIDIGenericChannelEvent b;
b.functionON();
b.functionOFF();
};
-
February 8th, 2008, 08:10 AM
#6
Re: Preventing methods from being inherited
Virtual inherintance, huh? Didn't know it. Your example seems pretty explanatory; could you point me to a good discussion of what it is (especially what drawbacks or hidden traps it has)? Thanks for the suggestion, it should be what I was looking for!
I owe Paul McKenzie a pizza.
I am no expert; but they say I can make concepts easy to understand. That's why newbies questions are mine!!! XD
-
February 8th, 2008, 10:13 AM
#7
Re: Preventing methods from being inherited
I don't have too much time to look around, but a quick google search has presented the following two to start with:
http://en.wikipedia.org/wiki/Virtual_inheritance
http://www.parashift.com/c++-faq-lit...heritance.html
The first one gives a quick insight into an example where you may want to use virtual inheritance and should help you understand what it is. The second one deals with technical aspects of inheritance and is a link to the faq-lite.
I would like to point out, that I would seriously reconsider your class structure before diving into virtual inheritance. There is no point in using a sledge hammer to crack a nut.
-
February 8th, 2008, 12:08 PM
#8
Re: Preventing methods from being inherited
 Originally Posted by Sk#
But I'm writing a class library, so the client code *might* want to do such things as "convert all NoteOff events to NoteOn with velocity 0" (as it's sometimes done on some hardware). So *in the general case* the first half-byte might need changing.
You have to consider whether you care about identity; it's trivial to iterate over a container of base class pointers and replace elements with new elements of a different type if a condition is satisfied. If instead you want to change the data inside an object, preserve its identity but mutate its type that's a different matter. So think how your client code which uses such mutating objects will look, and then work out what you need from them - in the OP you asked for a construct which allow the client to mutate only generic events, but in your example the client is mutating specific events. It may be that you only want to store the raw data and can construct flyweights for the specific event types.
-
February 8th, 2008, 12:17 PM
#9
Re: Preventing methods from being inherited
The desire to utilize an existing (out of your control) class to provide functionallity, but to expose only functionallity that is a (potentially improper) subset of functionallity is quite common.
There are two basic methodologies that are well accepted design patterns when using C++.
1) non-public inheritance (typically private, but protected can also be used if you want to allow people to derive from your class and gain access to functionallity that you have masked).
2) aggregation. Youe create a data member of the existing class type.
Both of these require that "wrappers" be written. While this does involve some "typing" overhead, the use of inline methods can (in most cases) completely eliminate any runtime overhead (which is negligable and can usually be ignored in any case).
TheCPUWizard is a registered trademark, all rights reserved. (If this post was helpful, please RATE it!)
2008, 2009,2010
In theory, there is no difference between theory and practice; in practice there is.
* Join the fight, refuse to respond to posts that contain code outside of [code] ... [/code] tags. See here for instructions 
* How NOT to post a question here
* Of course you read this carefully before you posted
* Need homework help? Read this first
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|