When To Stop Encapsulating? - C++

Started by
23 comments, last by Aardvajk 12 years, 10 months ago
Quick question for those more versed in C++ than I. I'm working on a custom GUI for my demo (Lots of element objects), and I had a question about encapsulation.

Say I have multiple button objects and I'm checking each on mouse move to see if any are highlighted (For picking / rollover purposes). Is it considered better practice to do the checking in the loop, or to have a method in the button object itself invoked for each button that does the checking? Here's my button object, set up currently to have each button check it's own bounding box - I've commented out the bit where it was checking in the loop by accessing the member variables of each button indirectly.


while( ThisButton ) {
//if((MouseX >= ThisButton->X && MouseX <= (ThisButton->X + ThisButton->Width)) && (MouseY >= ThisButton->Y && MouseY <= (ThisButton->Y + ThisButton->Height))) {
if( ThisButton->IsHighlighted( MouseX, MouseY ) ) {
OButton::HighlightedButton = ThisButton;
ThisButton = NULL;
break;
} else if( OButton::HighlightedButton == ThisButton ) {
OButton::HighlightedButton = NULL;
}

ThisButton = ThisButton->NextButton;
}


And here's the method for checking if the button is highlighted:



bool OButton::IsHighlighted( int MouseX, int MouseY ) {
return((MouseX >= X && MouseX <= (X + Width)) && (MouseY >= Y && MouseY <= (Y + Height)));
}


Actually, since HighlightedButton is a static member of OButton I could even set it inside the OButton::IsHighlighted() method
Advertisement
I'd say check as you are, with a method, for the simple reason that you could have the check a virtual method, then later if you want to add a circular button, whose check logic is different, the remainder of the code isn't affected.

This ThisButton->NextButton stuff is a bit concerning though. Why aren't you storing your GUI elements in a standard container?

I'd say check as you are, with a method, for the simple reason that you could have the check a virtual method, then later if you want to add a circular button, whose check logic is different, the remainder of the code isn't affected.

This ThisButton->NextButton stuff is a bit concerning though. Why aren't you storing your GUI elements in a standard container?


Standard container? Like a GUI library or are you referring to C++ templates? If templates it's because no matter how many tutorials I read on them I still don't quite grasp the concept. Which is a shame because I do end up with quite a few objects with similar members. It's just that the only tutorials I ever find on templates are tutorials specifically about templates, which never offer examples of how you would actually use them. And all other (Game programming-specific) tutorials typically omit the use of templates. I'm guessing because explaining game programming concepts is difficult enough without also having to explain templates.

But I revised it a bit, since HighlightedButton is a static member of OButton I just stuck it's assignment in the IsHighlighted():



bool OButton::IsHighlighted( int MouseX, int MouseY ) {
if((MouseX >= X && MouseX <= (X + Width)) && (MouseY >= Y && MouseY <= (Y + Height))) {
HighlightedButton = this;
return true;
} else if( this == HighlightedButton ) {
HighlightedButton = NULL;
}
return false;
}


And my loop is now just:



void CGUI::ProcessMouseMove( int MouseX, int MouseY, bool Left, bool Right ) {
if( !MenuList[CurrentMenu] )
return;

OButton *ThisButton = NULL;
ThisButton = MenuList[CurrentMenu]->FirstButton;

while( ( ThisButton ) && !ThisButton->IsHighlighted( MouseX, MouseY ) )
ThisButton = ThisButton->NextButton;
}


The ThisButton = ThisButton->NextButton is just bog standard linked list traversal.

The ThisButton = ThisButton->NextButton is just bog standard linked list traversal.


I wouldn't call it bog standard. C programmers used to do stuff like this, but it is a pointless design choice in C++. It is never a good idea to mix up the implementation of an object with details of how lists of such object are stored, if for no other reason than it becomes impossible to take advantage of the standard libraries algorithms that generalise operations on lists of objects.

You don't need to understand templates to use the standard containers. You are already using std::string, which is just a typedef of a template and essentially a container of characters. Start with std::vector and never look back.

[source lang="c++"]
class GUIElement
{
public:
virtual ~GUIElement(){ }

virtual bool IsHighlighted()=0;
};

class Button : public GUIElement
{
virtual bool IsHighlighted(){ /* ... */ }
};

std::vector<GUIElement*> elements;

void f()
{
elements.push_back(new Button());
elements.push_back(new Button());

// for simplicity, ignoring iterators for now

for(int i=0;i<elements.size();++i)
{
if(elements->IsHighlighted()) HighlightedElement=elements;
}

for(int i=0;i<elements.size();++i) delete elements;
}
[/source]

It is as simple as suffixing the container type with the class you want in angle brackets. Then std::vector just works the same as a native array really.

There are more appropriate solutions for storing containers of pointers but since you are new to the standard library, I'll ignore that for now.

[quote name='Part_E' timestamp='1307201689' post='4819438']
The ThisButton = ThisButton->NextButton is just bog standard linked list traversal.


I wouldn't call it bog standard. C programmers used to do stuff like this, but it is a pointless design choice in C++. It is never a good idea to mix up the implementation of an object with details of how lists of such object are stored, if for no other reason than it becomes impossible to take advantage of the standard libraries algorithms that generalise operations on lists of objects.

You don't need to understand templates to use the standard containers. You are already using std::string, which is just a typedef of a template and essentially a container of characters. Start with std::vector and never look back.

[source lang="c++"]
class GUIElement
{
public:
virtual ~GUIElement(){ }

virtual bool IsHighlighted()=0;
};

class Button : public GUIElement
{
virtual bool IsHighlighted(){ /* ... */ }
};

std::vector<GUIElement*> elements;

void f()
{
elements.push_back(new Button());
elements.push_back(new Button());

// for simplicity, ignoring iterators for now

for(int i=0;i<elements.size();++i)
{
if(elements->IsHighlighted()) HighlightedElement=elements;
}

for(int i=0;i<elements.size();++i) delete elements;
}
[/source]

It is as simple as suffixing the container type with the class you want in angle brackets. Then std::vector just works the same as a native array really.

There are more appropriate solutions for storing containers of pointers but since you are new to the standard library, I'll ignore that for now.
[/quote]


lol - For the longest time I assumed <Vector>s were templates that stored an x, y, z coordinate and an angle. Y'know... Like a vector. Never realized they were actually containers for linked objects. That's much simpler than the custom methods I was writing to push and pop objects into linked lists and trees.

Crud - Now this means I have to completely repurpose my octree code to use vectors =(
Can I make vectors of vectors of vectors? Hmmm...

Thanks though, Aardvajk.

Actually, std::vector is more similar to a native array than a linked list. std::list implements a linked list for you. std::vector stores all objects in contiguous memory and will copy and destroy its contents when it resizes, so beware.

The name vector seems confusing, but is mathematically correct technically - a homogenous, single dimensional tensor. I believe there was discussion at the time about naming it std::array but this was dropped for some reason.

Yes, you can make a vector of vectors, or a vector of vectors of vectors:

[source lang="c++"]
std::vector<std::vector<int> > ints;

typdedef std::vector<std::vector<int> > ints_vector;

std::vector<ints_vector> getting_silly_now;
[/source]

But a vector of vectors is essentially a ragged array of lists of seperate chunks of memory. If you want a rectangular array, similar to a native 2D array, you are better to use a single vector of size width*height and access via [(y*width)+x] sort of thing.

Again, there are better solutions but don't want to run too far ahead.

Actually, std::vector is more similar to a native array than a linked list. std::list implements a linked list for you. std::vector stores all objects in contiguous memory and will copy and destroy its contents when it resizes, so beware.

Yes, you can make a vector of vectors, or a vector of vectors of vectors:

[source lang="c++"]
std::vector<std::vector<int> > ints;

typdedef std::vector<std::vector<int> > ints_vector;

std::vector<ints_vector> getting_silly_now;
[/source]

But a vector of vectors is essentially a ragged array of lists of seperate chunks of memory. If you want a rectangular array, similar to a native 2D array, you are better to use a single vector of size width*height and access via [(y*width)+x] sort of thing.

Again, there are better solutions but don't want to run too far ahead.


Ok, well for most GUI elements I'll just be loading, indexing, and unloading as statically sized lists, so vector will probably be ok. No 'push to top', 'pop from middle', etc necessary. But that's good to keep in mind for the stuff that won't be so static. And please, be as technical as you freakin' want. I'm sick of tutorials pussy-footing around methods that're considered 'too complicated' even though they're the methods people actually, y'know, use.

[quote name='Aardvajk' timestamp='1307202602' post='4819442']
[quote name='Part_E' timestamp='1307201689' post='4819438']
The ThisButton = ThisButton->NextButton is just bog standard linked list traversal.


I wouldn't call it bog standard. C programmers used to do stuff like this, but it is a pointless design choice in C++. It is never a good idea to mix up the implementation of an object with details of how lists of such object are stored, if for no other reason than it becomes impossible to take advantage of the standard libraries algorithms that generalise operations on lists of objects.

You don't need to understand templates to use the standard containers. You are already using std::string, which is just a typedef of a template and essentially a container of characters. Start with std::vector and never look back.

[source lang="c++"]
class GUIElement
{
public:
virtual ~GUIElement(){ }

virtual bool IsHighlighted()=0;
};

class Button : public GUIElement
{
virtual bool IsHighlighted(){ /* ... */ }
};

std::vector<GUIElement*> elements;

void f()
{
elements.push_back(new Button());
elements.push_back(new Button());

// for simplicity, ignoring iterators for now

for(int i=0;i<elements.size();++i)
{
if(elements->IsHighlighted()) HighlightedElement=elements;
}

for(int i=0;i<elements.size();++i) delete elements;
}
[/source]

It is as simple as suffixing the container type with the class you want in angle brackets. Then std::vector just works the same as a native array really.

There are more appropriate solutions for storing containers of pointers but since you are new to the standard library, I'll ignore that for now.
[/quote]


lol - For the longest time I assumed <Vector>s were templates that stored an x, y, z coordinate and an angle. Y'know... Like a vector. Never realized they were actually containers for linked objects. That's much simpler than the custom methods I was writing to push and pop objects into linked lists and trees.

Crud - Now this means I have to completely repurpose my octree code to use vectors =(
Can I make vectors of vectors of vectors? Hmmm...

Thanks though, Aardvajk.


[/quote]

Yes, you can make vector's of vectors. If I could give you one piece of advice it's stop everything you are doing and read up on c++ standard library datatypes. Until you do, I guarantee you will be creating "bad" code, essentially re-inventing the wheel over and over, while creating untold numbers of bugs implementing code that has been thoroughly vetted by the community.

At the very least, learn the major STL Containers, vector, deque, list and map at the very least. Once you know these it will change the very way you program.

I would also consider learning or at least being aware of Boost the official unofficial library of C++! :) Many common tasks have already been handled in Boost and again this code is well vetted, extremely well documented and damned handy. Something like signals could completely change the fundamental design of your entire project. Of course Smart Pointers are perhaps the most used BOOST template.
Well, for storing a vector of pointers and for making multidimensional arrays I was going to suggest using boost::ptr_vector and boost::multi_array but if you are just starting out using the standard library, I'd get your head round that before delving into boost.

Don't mean to pussyfoot or suggest it is too complicated, just seems sensible to me to have a good idea of what the standard library can and can't do before opening the huge boost::can_of_worms. :)

But a vector of vectors is essentially a ragged array of lists of seperate chunks of memory. If you want a rectangular array, similar to a native 2D array, you are better to use a single vector of size width*height and access via [(y*width)+x] sort of thing.

Again, there are better solutions but don't want to run too far ahead.


I understand the suggestion you are making, but frankly this kinda hackish cludgery is so rife within the C world and should not be encouraged. A 2 dimensional datatype should BE a 2D datatype, even if behind the scenes it is implemented in the manner you are speaking. I would use a vector of vectors before I "faked it" using index math. Besides, your suggestion would not allow for a vector of a non-standard size. I would probably recommend boost multiarray here, if a vector of vectors just doesn't work for you.


EDIT: You commented while I was typing my reply ( stupid real world distractions! ;) ). In a nutshell, I agree with you completely in your follow up comment.

This topic is closed to new replies.

Advertisement