Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > C++ > ADT and inheritance

Reply
Thread Tools

ADT and inheritance

 
 
maverik
Guest
Posts: n/a
 
      11-17-2009
Hi all.

I have some class that contains only pure virtual functions (class-
interface):

class IButton {
// Contents of the class
};

So when I actually implement some type of button I do it like this

class ImageButton : public IButton {
// Contents of the class
};

It allows me to create a container of buttons without knowledge about
button types that this container would hold:

std::vector<IButton *> m_buttons;

The problem is: when I add some specific function to the inherited
button class then I can call it only in this way:

((ImageButton *)m_buttons[i])->SetImage(...); // I know that i-th
button is typeof ImageButton

Looks ugly a little bit, isn't it?

Of course I can add this specific function to the base class (IButton)

class IButton {
void SetImage(...) = 0;
};

but in this case IButton becomes just a bunch of methods from
different classes.

Is there a way to make it better?

Thanks.
 
Reply With Quote
 
 
 
 
Vladimir Jovic
Guest
Posts: n/a
 
      11-17-2009
maverik wrote:
> Hi all.
>
> I have some class that contains only pure virtual functions (class-
> interface):
>
> class IButton {
> // Contents of the class
> };
>
> So when I actually implement some type of button I do it like this
>
> class ImageButton : public IButton {
> // Contents of the class
> };
>
> It allows me to create a container of buttons without knowledge about
> button types that this container would hold:
>
> std::vector<IButton *> m_buttons;
>
> The problem is: when I add some specific function to the inherited
> button class then I can call it only in this way:
>
> ((ImageButton *)m_buttons[i])->SetImage(...); // I know that i-th
> button is typeof ImageButton
>
> Looks ugly a little bit, isn't it?
>


Yes, c-type casts are always bad.

> Of course I can add this specific function to the base class (IButton)
>
> class IButton {
> void SetImage(...) = 0;
> };
>
> but in this case IButton becomes just a bunch of methods from
> different classes.


What is wrong with that?



--
Bolje je ziveti sto godina kao bogatun, nego jedan dan kao siromah!
 
Reply With Quote
 
 
 
 
maverik
Guest
Posts: n/a
 
      11-17-2009
> > ((ImageButton *)m_buttons[i])->SetImage(...); // I know that i-th
> > button is typeof ImageButton

>
> > Looks ugly a little bit, isn't it?

>
> Yes, c-type casts are always bad.


Ok, I actually use static_cast, so the code looks like
static_cast<ImageButton *>(m_buttons[i])->SetImage(...);

> > Of course I can add this specific function to the base class (IButton)

>
> > class IButton {
> > * * void SetImage(...) = 0;
> > };

>
> > but in this case IButton becomes just a bunch of methods from
> > different classes.

>
> What is wrong with that?


Hmmm... one thing is

class IButton {
// 100 methods from AButton goes here
// ...
// 100 methods from BButton goes here
// ...
// 100 methods from CButton goes here
// ...
};

and the programmer who add new methods always should update the base
class. The other thing is that the purpose of IButton is to include
common operation for all types of Buttons not every operation from
every type. I think it's rather good idea, isn't it? If not, please,
take a minute to explain why.
 
Reply With Quote
 
Daniel Pitts
Guest
Posts: n/a
 
      11-18-2009
maverik wrote:
> Hi all.
>
> I have some class that contains only pure virtual functions (class-
> interface):
>
> class IButton {
> // Contents of the class
> };
>
> So when I actually implement some type of button I do it like this
>
> class ImageButton : public IButton {
> // Contents of the class
> };
>
> It allows me to create a container of buttons without knowledge about
> button types that this container would hold:
>
> std::vector<IButton *> m_buttons;
>
> The problem is: when I add some specific function to the inherited
> button class then I can call it only in this way:
>
> ((ImageButton *)m_buttons[i])->SetImage(...); // I know that i-th
> button is typeof ImageButton
>
> Looks ugly a little bit, isn't it?
>
> Of course I can add this specific function to the base class (IButton)
>
> class IButton {
> void SetImage(...) = 0;
> };
>
> but in this case IButton becomes just a bunch of methods from
> different classes.
>
> Is there a way to make it better?
>
> Thanks.

Usually, the container values aren't configured after the fact, so
you'll never call SetImage on an object reference you got from m_buttons.

std::vector<IButton *> m_buttons;

ImageButton * myButton = new ImageButton();
myButton->SetImage(...);
m_buttons.add(myButton);

IButton should contain methods that you need to support any IButton (in
Java Swing, for example, JButton has a paintComponent method, which can
be overridden by subclasses)

If you are going to cast frequently at runtime, then you've probably got
the wrong static type.






 
Reply With Quote
 
Stuart Redmann
Guest
Posts: n/a
 
      11-18-2009
maverik wrote:
> I have some class that contains only pure virtual functions (class-
> interface):
>
> class IButton {
> // Contents of the class
> };
>
> So when I actually implement some type of button I do it like this
>
> class ImageButton : public IButton {
> // Contents of the class
> };
>
> It allows me to create a container of buttons without knowledge about
> button types that this container would hold:
>
> std::vector<IButton *> m_buttons;
>
> The problem is: when I add some specific function to the inherited
> button class then I can call it only in this way:
>
> ((ImageButton *)m_buttons[i])->SetImage(...); // I know that i-th
> button is typeof ImageButton
>
> Looks ugly a little bit, isn't it?
>
> Of course I can add this specific function to the base class (IButton)
>
> class IButton {
> void SetImage(...) = 0;
> };
>
> but in this case IButton becomes just a bunch of methods from
> different classes.
>
> Is there a way to make it better?


I'd advise to use several containers for the derived class pointers:
one container contains all objects with their base interface IButton,
one
contains pointers to all ImageButton objects, and one contains all
objects of
type SomeOtherButton. This means that registering objects of type
ImageButton includes
registering the object in the list of IButtons as well. This makes the
code
much more readable and maintainable than using dynamic_casts, IMHO.

Regards,
Stuart
 
Reply With Quote
 
James Kanze
Guest
Posts: n/a
 
      11-18-2009
On Nov 18, 12:37 am, Daniel Pitts
<(E-Mail Removed)> wrote:
> maverik wrote:


> > I have some class that contains only pure virtual functions
> > (class- interface):


> > class IButton {
> > // Contents of the class
> > };


> > So when I actually implement some type of button I do it
> > like this


> > class ImageButton : public IButton {
> > // Contents of the class
> > };


> > It allows me to create a container of buttons without
> > knowledge about button types that this container would hold:


> > std::vector<IButton *> m_buttons;


> > The problem is: when I add some specific function to the
> > inherited button class then I can call it only in this way:


> > ((ImageButton *)m_buttons[i])->SetImage(...); // I know that i-th
> > button is typeof ImageButton


That really should be:

ImageButton* p = dynamic_cast< ImageButton* >( m_buttons[i]);
if ( p != NULL ) // special handling...

> > Looks ugly a little bit, isn't it?


> > Of course I can add this specific function to the base class
> > (IButton)


> > class IButton {
> > void SetImage(...) = 0;
> > };


> > but in this case IButton becomes just a bunch of methods
> > from different classes.


> > Is there a way to make it better?


> Usually, the container values aren't configured after the
> fact, so you'll never call SetImage on an object reference you
> got from m_buttons.


> std::vector<IButton *> m_buttons;


> ImageButton * myButton = new ImageButton();
> myButton->SetImage(...);
> m_buttons.add(myButton);


> IButton should contain methods that you need to support any
> IButton (in Java Swing, for example, JButton has a
> paintComponent method, which can be overridden by subclasses)


> If you are going to cast frequently at runtime, then you've
> probably got the wrong static type.


Yes. The question is: what the vector is supposed to contain.
If it's just ImageButton, then it should be
std::vector<*ImageButton**>. Otherwise, users of the vector
*generally* shouldn't use parts of the interface that are unique
to ImageButton.

The one exception I can think of is if you do define an extended
button interface, e.g.:

class IExtendedButton : public IButton
{
// More pure virtual functions with additional
// functionality.
};

It's perfectly reasonable to imagine cases where you want to use
some additional features of ExtendedButton, if they are present,
and fall back to some default handling if they aren't. In such
a case, something like:

for ( std::vector< IButton* >::const_iterator
iter = m_buttons.begin() ;
iter != m_buttons.end();
++ iter ) {
IExtendedButton* extended
= dynamic_cast< IExtendedButton* >( *iter );
if ( extended != NULL ) {
// use added features...
} else {
// use default handling...
}
}

is reasonable. Extending this to a chain of else if... for
each possible type isn't, but for one, or possibly two
extensions, OK.

--
James Kanze
 
Reply With Quote
 
Vladimir Jovic
Guest
Posts: n/a
 
      11-18-2009
maverik wrote:
>>> ((ImageButton *)m_buttons[i])->SetImage(...); // I know that i-th
>>> button is typeof ImageButton
>>> Looks ugly a little bit, isn't it?

>> Yes, c-type casts are always bad.

>
> Ok, I actually use static_cast, so the code looks like
> static_cast<ImageButton *>(m_buttons[i])->SetImage(...);


As James Kanze said, it should have been dynamic_cast

>
>>> Of course I can add this specific function to the base class (IButton)
>>> class IButton {
>>> void SetImage(...) = 0;
>>> };
>>> but in this case IButton becomes just a bunch of methods from
>>> different classes.

>> What is wrong with that?

>
> Hmmm... one thing is
>
> class IButton {
> // 100 methods from AButton goes here
> // ...
> // 100 methods from BButton goes here
> // ...
> // 100 methods from CButton goes here
> // ...
> };
>
> and the programmer who add new methods always should update the base
> class. The other thing is that the purpose of IButton is to include
> common operation for all types of Buttons not every operation from
> every type. I think it's rather good idea, isn't it? If not, please,
> take a minute to explain why.



1) it seams very wrong to have so many methods in one class.

2) can you do what Stuart Redmann suggested? To have different base classes.

From what you wrote, you have a bad design problem.

--
Bolje je ziveti sto godina kao bogatun, nego jedan dan kao siromah!
 
Reply With Quote
 
Daniel Pitts
Guest
Posts: n/a
 
      11-18-2009
James Kanze wrote:
> On Nov 18, 12:37 am, Daniel Pitts
> <(E-Mail Removed)> wrote:
>> If you are going to cast frequently at runtime, then you've
>> probably got the wrong static type.

>
> Yes. The question is: what the vector is supposed to contain.
> If it's just ImageButton, then it should be
> std::vector< ImageButton* >. Otherwise, users of the vector
> *generally* shouldn't use parts of the interface that are unique
> to ImageButton.
>
> The one exception I can think of is if you do define an extended
> button interface, e.g.:
>
> class IExtendedButton : public IButton
> {
> // More pure virtual functions with additional
> // functionality.
> };
>
> It's perfectly reasonable to imagine cases where you want to use
> some additional features of ExtendedButton, if they are present,
> and fall back to some default handling if they aren't. In such
> a case, something like:
>
> for ( std::vector< IButton* >::const_iterator
> iter = m_buttons.begin() ;
> iter != m_buttons.end();
> ++ iter ) {
> IExtendedButton* extended
> = dynamic_cast< IExtendedButton* >( *iter );
> if ( extended != NULL ) {
> // use added features...
> } else {
> // use default handling...
> }
> }
>
> is reasonable. Extending this to a chain of else if... for
> each possible type isn't, but for one, or possibly two
> extensions, OK.


That is one approach. The other is to have IButton have a method which
returns IExtendedButton. The default implementation returns NULL, but
subclasses can return any object they wish (including themselves if they
are extended buttons).

This removes the need for dynamic cast, and makes the concept of
"extended button" more concrete.
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Tree ADT vs Database djcredo@gmail.com Java 9 04-08-2006 06:15 PM
Binary Search Tree Dictionary ADT Gaz Java 2 01-28-2006 10:22 PM
Heirarchical ADT supporting column and row objects Dr Ann Huxtable C++ 1 11-29-2004 03:43 PM
New in C++:Abstract Data Types(ADT) - Base Class SunScreen C++ 2 11-18-2004 04:43 PM
difference between java built in classes and ADT Kenneth Java 4 11-11-2004 09:11 AM



Advertisments