Advertisement

C++ Dilemma! Please help

Started by May 19, 2001 09:16 PM
6 comments, last by benjamin bunny 23 years, 8 months ago
I''ve been writing a nasty hybrid version of C++ and C since I learnt the basics of classes about a year ago, using all sorts of nasty tricks to save on typing (like, for example, declaring attributes as public so I don''t have to write interface methods for them). Well, my conscience has finally caught up with me as I realise someone besides me may need to read my code someday, so I''m trying to mend my ways. Anyway, I need some advice, preferably from C++ coders who are less lazy and more knowledgeable than myself. Here''s a semi-hypothetical situation: Suppose I have the following typedef: typedef struct { float pos[3]; int ID; }vertex_t; And I have a class: Cmodel, and in that class I have 4 objects of class Climb (read:Class,limb), and within each Climb class I have 2 objects of class Cmesh, which in turn contains an array of objects of type vertex_t. (Assume the classes aren''t nestled). sort of like this: class Cmesh { private: vertex_t ver[100]; } class Climb { private: Cmesh mesh[2]; } class Cmodel { private: Climb limb[4]; } Now, if I want to be able to set and get the vertices from outside the model class, if I follow the rules of encapsulation as I understand them, I''ll have to write these methods in Cmesh: vertex_t getVertex(int ID){return ver[ID];} void setVertex(int ID,vertex_t ver_in){ver[ID]=ver_in;} Then these methods in Climb: vertex_t getMeshVertex(int meshNo, int ID) { return mesh[meshNo].getVertex(ID); } void setMeshVertex (int meshNo, int ID, vertex_t ver_in) { mesh[meshNo].setVertex(ID,ver_in); } and these methods in Cmodel: vertex_t getLimbMeshVertex (int limbNo,int meshNo, int ID) { return limb[limbNo].getMeshVertex(meshNo,ID); } setLimbMeshVertex (int limbNo,int meshNo, int ID, vertex_t ver_in) { //etc } With a few more attributes I want to be able to access, the list of interface functions starts to get as long as my arm. Now, this strikes me as incredibly inefficient in terms of CPU time, not to mention typing, so I was wondering if there was a better way which I''m missing, which still abides by the fundamentals of OO programming, or if I really must resign myself to typing interface methods for the rest of my life www.elf-stone.com

____________________________________________________________www.elf-stone.com | Automated GL Extension Loading: GLee 5.00 for Win32 and Linux

It is ineffecient but with today''s computers this isn''t like the problem it posed back then. Unless you''re programming for a palm or something.

I guess that while you have to painstakingly write out the methods, it also becomes less convulated because you only have to look at the client model (in your case CModel). This is the benefits of data-hiding and you really won''t have to bother with the internal methods of CLimb and CMesh (unless you haven''t finished it yet).

But if there''s a better way, I want to hear it.
Advertisement
Here''s my take on this:
I think that whenever you are asking yourself ''Should I make
this variable public or private?'', you should ask yourself
what you would gain by making it private. For example, if
you have something like this:

class Test
{
public:
int GetVar() { return(m_var); }
void SetVar(int var) { m_var = var; }

private:
int m_var;
};

That is a waste of time, in my opinion. Adding the functions
costs you time, and you gain nothing. So I think everytime
you make a variable private, you should have a good reason
for doing so.

I often find myself having to choose between a loss of speed
or a loss of abstraction, and I think that sometimes it is ok
to let speed win. For example, a ''GetString()'' function that
returns a pointer to a string, rather than copying a string
to a buffer, might not be as safe, but sometimes I find it''s
just more practical to go with speed.

In the end, I think that it is better to have a balance of
efficiency and abstraction, rather than following a coding
philosophy, such as OO, blindly. That''s my $0.02.

------Blah



Declare your classes to be a friend classes of each other (and therefore able to access private data members directly [unless this has changed in recent compiler specifications]). Then you can use simple array notation:

return limb[m].mesh[n].ver[o];

This is dangerous, though, as it bypasses the safeguards of data hiding and encapsulation, and totally exposes your variables to users of your classes. So access specifiers it is. However, if you're only writing container classes (ie array-type data), you might want to consider overloading the [] operator to acheive the same textual effect as above, _with_ the safeguards intact.


---
Those who can do nothing criticize; those who can, critique.

Edited by - Oluseyi on May 19, 2001 11:34:52 PM
It doesn''t have to be that much code. You can use references to ease things. If you have a function that returns a reference you don''t also need a function to set the value. You can inline the access functions to eliminate call overhead. Also, instead of get/set for each element, you can generalize access for the whole array with a member function returning a pointer to the array. Also, you can group some of the function calls, generally you will be setting the vertices in one mesh at a time, so you don''t need to keep looking up the same mesh in an array.
LeeIsMe is right - references ease things tremendously. Consider the following:

class Cmesh
{
// other stuff
vertex_t &operator[](int n) {return ver[n];}
};

class Climb
{
// other stuff
Cmesh &operator[](int n) {return mesh[n];}
};

class Cmodel
{
// once again, other stuff
Climb &operator[](int n) {return limb[n];}
};

Cmodel mod;
// initialization of all data members
vertex_t req_vert = mod[j][k][l];

Believe it or not, it works.


---
Those who can do nothing criticize; those who can, critique.

Edited by - Oluseyi on May 19, 2001 11:43:20 PM
Advertisement
For the sake of effiency, is this case you should either make the vertex data public, or protected and use the friend clause.

I think you have abstracted to too fine a level of granulality. There really is no difference, to the graphics engine, between a leg & an arm. There should be one CMesh class and one CMultiMesh class and maybe one CBiPed class that handle all of the meshes themselves. There should not be a CFarmer, who has a CHeadMesh & CRakeMesh etc...

class names should start with two capital letters, prefix member variables with ''m_''; globals get a g_ & statics get s_ (locals get no prefix), and postfix function parametes with ''_''

write comments that briefly explain the objective (the why not the how) of the class and methods.


i.e.

//////////////////////////////////////////
//CMesh
// Persist mesh data
//
class CMesh
{
//...
operator<<(...);
operator>>(...);
};


//////////////////////////////////////////
//CBiPed
// Flyweight for character animations
//
class CBiPed
{
virtual BOOL Render(...);
//...
CMesh* m_meshUpperSkull;
CMesh* m_meshLowerLeftArm;
//...
};

Magmai Kai Holmlor
- The disgruntled & disillusioned
- The trade-off between price and quality does not exist in Japan. Rather, the idea that high quality brings on cost reduction is widely accepted.-- Tajima & Matsubara
Thanks for the responses. This gives me a much better idea of what I can get away with. I like the idea of returning references to data rather than having seperate get and set members.

Magmai, I appreciate the advice on comments, models and suchlike, but the situation was hypothetical. If ever I do write a character class, I''ll bear it in mind though. As for hungarian notation, I''ve never been too good at following that, due largely to the fact that I don''t like typing long words.

www.elf-stone.com

____________________________________________________________www.elf-stone.com | Automated GL Extension Loading: GLee 5.00 for Win32 and Linux

This topic is closed to new replies.

Advertisement