C++ Classes - eating my lunch (C++ newbie)

Discussion in 'Mac Programming' started by toddburch, May 16, 2007.

  1. toddburch macrumors 6502a

    Joined:
    Dec 4, 2006
    Location:
    Katy, Texas
    #1
    I'm knocking my head against the wall. Here's what I want to do. I have a class called Record. I have another class called Field. A Record is made of multiple Fields, and each Field has several attributes.

    In this code sample, all the field definitions are hard coded. At some point, they will be derived at run time, but that's not my issue right now. I'll typically only have 1 record.

    Also, for indexing through this series of fields at runtime, I'm going to assume that I want to do pointer arithmetic to index through the array. I still need that definition too.

    I can't figure out how to define my classes to get this thing to compile. Any advice is greatly appreciated.

    (I could code this in Ruby with my eyes closed... :()

    Code:
    #include <iostream>
    using namespace std ; 
    
    // Field class definition 
    class Field { 
    	int offset ;      // Offset in the record to this field. 
    	char datatype ;   // Datatype of this field.  char, num, dec, flt, dble 
    	int len ;         // Number of bytes occupied by this field in the input record 
    	int maxlength ;   // length of this field in the output record 
    	int scale ;       // If packed decimal, this is the number of digits to the right of the decimal point. 
    public: 
    	void setv(int offset, int len, char dt, int maxlength, int scale) ; // set the values for the field. 
    	int  get_offset() ; 
    	char get_datatype() ; 
    	int  get_len() ; 
    	int  get_maxlength() ;
    	int  get_scale() ; 
    } ; 
    
    int Field::get_offset()    {  return offset   ; } 
    
    char Field::get_datatype() { return datatype  ; } 
    
    int Field::get_len()       { return len       ; } 
    
    int Field::get_maxlength() { return maxlength ; } 
    
    int Field::get_scale()     { return scale     ; } 
    
    
    // Record class definition 
    class Record { 
    public: 
    	Field field[5] ;   // I need an array of 5 elements.
    	Record() ; // Constructor 
    } ; 
    
    // Constructor for the Record class 
    Record::Record() { 
    	cout << "Here in Record Constructor..." << endl ;  
    	Field field[5] ;     // An array of Fields. 
    	field[0].setv(   0,  1, 'c' ,  1, 0 ) ; // parms are: ( offset, length in bytes, data type,  
    	field[1].setv(   1,  2, 'c' ,  2, 0 ) ; // max length needed in output record, scale for decimal values) 
    	field[2].setv(   3,  2, 'c' ,  2, 0 ) ;
    	field[3].setv(   5,  2, 'c' ,  2, 0 ) ;
    	field[4].setv(   7,  4, 'c' ,  4, 0 ) ;
    }
    
    int main (int argc, char * const argv[]) {
    	Record rec ;          // Initialize the Record & Field layouts 	 
    	for (int i = 0 ; i < Record::field.length ; ++i) { 
    		printf("Record %02d: offset=%02d, length=%02d, type=%c, Max Length=%02d, Scale=%02d\n", 
    				i, 
    				rec.field[i].get_offset() , 
    				rec.field[i].get_len() , 
    				rec.field[i].get_datatype() , 
    				rec.field[i].get_maxlength() , 
    				rec.field[i].get_scale() ) ; 
    	}  
        return 0;
    }
    Todd
     
  2. kainjow Moderator emeritus

    kainjow

    Joined:
    Jun 15, 2000
    #2
    A few things: 1) put your classes in separate .h and .cpp files, and 2) your main() function goes outside of any classes.
     
  3. lazydog macrumors 6502a

    Joined:
    Sep 3, 2005
    Location:
    Cramlington, UK
    #3
    Hi

    Just had a quick look and noticed you've got an error in your for loop. I think it should be:-
    Code:
    for (int i = 0 ; i < rec.field[ i ].get_len() ; ++i)
    

    hope this helps!

    b e n
     
  4. lazydog macrumors 6502a

    Joined:
    Sep 3, 2005
    Location:
    Cramlington, UK
    #4
    Hi Todd

    In your for loop you are accessing the fields of the record directly. I think it would be best to add a couple of methods to Record to allow this:-

    Code:
    class Record
    { 
         public: 
    
    		Record() ; // Constructor 
               
               int size() ;                     // Return the number of fields
               Field & field( int index ) ; // Accessor to a particular field
    
               Field & operator[] ( int index ) ; // An alternative way of providing an accessor to the fields!
     
        private:
    
    		Field field[5] ;   // I need an array of 5 elements.
    
    } ;
    
    b e n
     
  5. iSee macrumors 68040

    iSee

    Joined:
    Oct 25, 2004
    #5
    In regard to getting this to compile, the line
    Code:
    	for (int i = 0 ; i < Record::field.length ; ++i) { 
    
    won't work because of the "Record::field.length" part.

    You probably want to get the number of fields via the record instance (rec) not the class. That would make it "rec.field.length." That won't work either though because simple C arrays don't have a length property. You'd want to add a member function to Record to get the field count, like this:
    int Record::get_fieldCount() { return 5; }

    Then the line becomes
    Code:
    	for (int i = 0 ; i < rec.get_fieldCount() ; ++i) { 
    
    Also, in your Record constructor you are declaring a local variable called field and initializing that. I'm sure you meant to initialize the member variable called field. The local variable takes precedence over the member variable, effectively "hiding" it. Anyway, just remove the line "Field field[5] ; // ..." from the Record constructor.

    That's all I see...
     
  6. lazydog macrumors 6502a

    Joined:
    Sep 3, 2005
    Location:
    Cramlington, UK
    #6
    iSee thanks! I can see what I first posted doesn't make sense.

    Should have been wrt my 2nd post:-

    Code:
    for (int i = 0 ; i < rec.size() ; ++i)
    
    Todd, sorry for the confusion!


    b e n
     
  7. toddburch thread starter macrumors 6502a

    Joined:
    Dec 4, 2006
    Location:
    Katy, Texas
    #7
    Thanks guys. I'm going through this information now, and rereading my book chapters on classes as well. My book doesn't have an example of arrayed instances of one class in another. I'm sure it's basic stuff - but I'm still wrapping my head around the syntax and order of operations.

    Todd
     
  8. iSee macrumors 68040

    iSee

    Joined:
    Oct 25, 2004
    #8
    FYI, At lunch I compiled the code with my and lazydog's fixes and it looks like it works. The only other thing a ran into was that there was no implementation of Field::setv(). I added the obvious code and everything seemed to work.
     
  9. toddburch thread starter macrumors 6502a

    Joined:
    Dec 4, 2006
    Location:
    Katy, Texas
    #9
    Glad to see you're not taking time away from your real job to help!!

    Now, I have faith!

    Todd
     
  10. gnasher729 macrumors P6

    gnasher729

    Joined:
    Nov 25, 2005
    #10
    In the Record constructor, you have an automatic array named "field" with five elements, which by sheer coincidence has the same name as a member of class Record.

    Whatever you store into that array will disappear as soon as you exit the constructor. The member named "field" will remain uninitialised.
     
  11. toddburch thread starter macrumors 6502a

    Joined:
    Dec 4, 2006
    Location:
    Katy, Texas
    #11
    Ahhh.... maybe that's the SIGSEGV (or whatever it was I was blowing out with) I was getting... ;)

    Todd
     
  12. slooksterPSV macrumors 68030

    slooksterPSV

    Joined:
    Apr 17, 2004
    Location:
    Nowheresville
    #12
    If you want to dynamically allocate a certain amount of arrays for a structure or that, this is how I did it with my one program. Hope it helps.

    PHP:
    ...//
    typedef float sk1Vertex[3];
    typedef float sk1Color[3];

    typedef struct {
     
    int vertices// defines how many lines of vertices
     
    int colors//how many lines will have color codes
     
    int colorSkip// defines how many lines before the colors begin e.g. after 3 lines of vertices, start 1 line of color
     
    sk1Vertex *mdlVert;
     
    sk1Color *mdlColor;
    }
    model;

    class 
    STKGL
    {
    public:
     
    void DrawScene(void (*p)());
     
    model *TriangleLoad(model *temchar *Filename);
     
    void DrawTriangle(model*);
     
    STKGL() {;};
     ~
    STKGL() {;};
     
    sk1Vertex *t;
     
    sk1Color *p;
    };

    model *STKGL::TriangleLoad(model *temchar *Filename)
    {
     
    STKGL *Ctemp;
     
    ZeroMemory(temsizeof(model));
     
    int skipNum 0;
     
    int colNum;
     
    FILE *fptr, *fOut;
     
    fptr fopen(Filename"rb");
     
    fscanf(fptr"%d", &tem->vertices);
     
    fscanf(fptr"%d", &tem->colors);
     
    fscanf(fptr"%d", &tem->colorSkip);
     
    //sk1Vertex *mdlVert = new sk1Vertex[tem->vertices];
     
    tem->mdlVert = new sk1Vertex[tem->vertices];
     
    tem->mdlColor = new sk1Color[tem->colors];
    ...
    //
     
  13. toddburch thread starter macrumors 6502a

    Joined:
    Dec 4, 2006
    Location:
    Katy, Texas
    #13
    OK. I made the changes, and I understand why they had to be made. It all makes sense, but my confidence level has yet to reach 100%. That's OK - practice with this stuff will bring more comprehension and more coding speed.

    Now, a question. Or, maybe it's just a point. Or, a p.s. (post script)

    When I implemented the Field::setv function, I copied my member function prototype, which just happened to use the same variable names as were defined as private variables for class Field. (offset, len, dt, maxlength, scale ... although I did use "dt" instead of "datatype")

    The following Field class definition compiled fine:
    Code:
    class Field { 
    	int offset ;      // Offset in the record to this field. 
    	char datatype ;   // Datatype of this field.  char, num, dec, flt, dble 
    	int len ;         // Number of bytes occupied by this field in the input record 
    	int maxlength ;   // length of this field in the output record 
    	int scale ;       // If packed decimal, this is the number of digits to the right of the decimal point. 
    public: 
    	void setv(int offset, int len, char dt, int maxlength, int scale) ; // set the values for the field. 
    	int  get_offset() ; 
    	char get_datatype() ; 
    	int  get_len() ; 
    	int  get_maxlength() ;
    	int  get_scale() ; 
    } ;
    The following was my first crack at the Field::setv member function definition:
    Code:
    void Field::setv(int offset, int len, char dt, int maxlength, int scale) { 
    	offset    = offset ; 
    	len       = len ; 
    	datatype  = dt ; 
    	maxlength = maxlength ; 
    	scale     = scale ; 
    }
    The above compiled fine with no warnings. However, garbage was produced. (With Ruby, this is valid syntax) Output:
    Code:
    Here in Record Constructor...
    Record 00: offset=-1073743368, length=-1610547904, type=c, Max Length=-1610547804, Scale=-1021043573
    Record 01: offset=-1021047669, length=65535, type=c, Max Length=00, Scale=00
    Record 02: offset=00, length=00, type=c, Max Length=7830, Scale=-1073743200
    Record 03: offset=-1073743208, length=7869, type=c, Max Length=71952, Scale=7774
    Record 04: offset=-1073743304, length=-1073743324, type=c, Max Length=4096, Scale=-1073743304
    
    classtest has exited with status 0.
    
    Notice above that type= was correct - it's the only field that did not duplicate the variable name in the parm list.

    After changing the parm list to use generic var names, and changing the assignments, the proper data was produced.
    Code:
    void Field::setv(int a, int b, char c, int d, int e) { 
    	offset    = a ; 
    	len       = b ; 
    	datatype  = c ; 
    	maxlength = d ; 
    	scale     = e ; 
    }
    Output:
    Code:
    Here in Record Constructor...
    Record 00: offset=00, length=01, type=c, Max Length=01, Scale=00
    Record 01: offset=01, length=02, type=c, Max Length=02, Scale=00
    Record 02: offset=03, length=02, type=c, Max Length=02, Scale=00
    Record 03: offset=05, length=02, type=c, Max Length=02, Scale=00
    Record 04: offset=07, length=04, type=c, Max Length=04, Scale=00
    
    classtest has exited with status 0.
    
    So, grasshopper (AKA self), we have learned to not use the same variable names in the function definition. In the function prototype, it is OK to use the same names.

    Todd
     
  14. toddburch thread starter macrumors 6502a

    Joined:
    Dec 4, 2006
    Location:
    Katy, Texas
    #14
    slooksterPSV - without comments & intent, unforunately, it would take me a day to break this down into something meaningful... Sorry. Thanks anyway.

    Todd
     
  15. kainjow Moderator emeritus

    kainjow

    Joined:
    Jun 15, 2000
    #15
    If I remember correctly from one of my C++ classes, whatever variable is most local is used first, if you have a global and local variable of the same name. So you were essentially setting the function parameter to itself. When you viewed the original variables, they were garbage since you didn't set an initial value in your constructor.

    Someone feel free to correct me if I'm wrong ;)

    Thus, the this pointer comes in handy :)
     
  16. toddburch thread starter macrumors 6502a

    Joined:
    Dec 4, 2006
    Location:
    Katy, Texas
    #16
    I even tried this - I forgot to mention it. I'll try it again to be sure...
     
  17. toddburch thread starter macrumors 6502a

    Joined:
    Dec 4, 2006
    Location:
    Katy, Texas
    #17
    OK, more learning.

    I had tried this.offset (et al) for all the fields and got compile errors.

    However, when I tried this->offset (et al) it worked just fine.

    -> is something I haven't read up on yet. Thanks for making me revisit this.

    Todd
     
  18. gnasher729 macrumors P6

    gnasher729

    Joined:
    Nov 25, 2005
    #18
    Just one thing: One problem with C++ that beginners don't notice for a while is that the same identifier can mean so many different things. Like what happened to you: The name of a parameter and the name of a class member were the same, which just creates confusion.

    One set of coding standards that I used and which works very well to avoid confusion:

    1. Every member function is called by using this->function () or object->function (). Every static class function is called as classname::function (). Every function that is not a member or static class function is called as ::function (). No plain function calls whatsoever. Disadvantage: You have to type a few more letters. I feel your pain. Advantage: Everyone seeing a function call knows exactly what goes on.

    2. There is a convention for names of class member variables: We used names like fMember (lower case f, followed by an uppercase letter). You can use whatever convention you want, but stick to it. Your problem would have been avoided, because you would have assigned fLength = length. Obviously nothing that is not a member of a class can have a similar name.

    3. Similar, all global names start with gNameOfGlobalVariable, all static variable names start with sNameOfStaticVariable.

    Beginners care how easy it is to write code. After a while you notice that what is important is how easy it is to read code. And a bit later you notice that it is important to figure out what code is supposed to do, what it actually does, and when these two are different. And a bit later again you realise you want code where every single line indicates that the author clearly understood what is going on.
     
  19. toddburch thread starter macrumors 6502a

    Joined:
    Dec 4, 2006
    Location:
    Katy, Texas
    #19
    First, thanks for your feedback. All the feedback I have received here has been very appreciated. This does not deviate.

    Yes, it can create confusion. However, my intent for the naming convention was primarily that of self-documentation in the function prototype and function definition. When I defined int offset, that is so much clearer to a maintenance programmer than something like int a. However, with the this-> syntax, it is not confusing, or ambiguous at all. I've used other languages, like Ruby, that allows this, and yes, being new to a language, it can definately create confusion. For whatever reason (laziness? No, more of a "if it works, I'll have confirmed a known experience, and if it doesn't, I'll look it up"), I tried the syntax that I was familiar with, and learned from the experience. I see it as forward progress.

    I haven't coded enough yet in C/C++ to develop a sense of need for this standard. However, I'll be the first to agree, 100%, that clear code is worth it's weight in gold. I'll be one of the first to not create ambiguity through actions like creating the same function name for different classes, and I try not to use global and local variables of the same name, etc.

    These are great standards. Perhaps the authors who write the books should at least mention them.

    (take deep breath... hold... release... repeat... sigh....)
    I agree with you 100%. I remember when I was a beginner.

    Actually, even a bit later, you don't want every line of code commented, because you are so familiar with the language and it's constructs and the platform architecture, all you really want is the author's intent of the code block itself. For when you understand that, you are able to confirm or deny the acceptableness of the code towards it intended function, and piecing the whole program together in your head is a much simpler and faster undertaking.

    Todd

    [size=-4](I've been coding longer than most of this forum's members have been alive, just not in this language!)[/size]
     
  20. kainjow Moderator emeritus

    kainjow

    Joined:
    Jun 15, 2000
    #20
    I find it strange when people learn other languages first, instead of C/C++. Learning C/C++ makes everything else seem easy, because you learn how pointers and memory management works. But maybe that's just me :)
     
  21. iSee macrumors 68040

    iSee

    Joined:
    Oct 25, 2004
    #21
    Just to compare and contrast my habits (as a long-time C++ programmer) with gnasher's comments:
    I do exactly this (well actually, I use m as the prefix for private/protected member variables). I recommend that all C++ programmers for this convention.

    I can't really argue against making code easier to read, but I don't generally use these two conventions unless I see a specific need. For example, when writing code that uses MFC and direct Win32 API calls, I use ::function for the Win32 calls because there are a lot of member functions in MFC with the same names as top level function in Win32.

    Also regarding -> vs. .:
    You use -> to access members from an object pointer.
    You use . to access members from an object reference or a direct instance of an object. That is:
    Code:
    class Foo {...};
    
    Foo obj; // an object of type Foo.
    Foo *objPtr = new Foo(); // a pointer to an object of type Foo.
    Foo &objRef = obj;  // a reference to obj
    
    obj.someMemberFunction();
    objPtr->someMemberFunction();
    objRef.someMemberFunction();
    
    delete objPtr; // free the memory allocated with new
    
    For whatever reason, in C++ this is a pointer rather than a reference (as it is in some other languages). So you need to use -> to specify members. Of course there is no difference conceptually between a pointer and a reference--it's mostly just the syntax used with each that is different.
     
  22. toddburch thread starter macrumors 6502a

    Joined:
    Dec 4, 2006
    Location:
    Katy, Texas
    #22
    LOL!! When I learned programming, C didn't exist! Assembler baby! Talk about having to learn what pointers are and do your own memory management!! LOL!

    Todd
     
  23. kainjow Moderator emeritus

    kainjow

    Joined:
    Jun 15, 2000
    #23
    hehe I didn't think about assembly language. I guess all the guys I know who know that know C/C++ inside and out - seems like an obvious next choice of language. Just curious - what did you learn after asm?
     
  24. toddburch thread starter macrumors 6502a

    Joined:
    Dec 4, 2006
    Location:
    Katy, Texas
    #24
    In order:
    • s/370 Assembler
    • REXX
    • SQL (not a programming langauge, per se, but a creature in its own right)
    • s/390 Assembler (relative branching, new hardware instructions)
    • PC Assembler (but very rusty now)
    • dabble in C
    • Javascript
    • z/Architecture Assembler (more of the same - new hardware instructions, 64-Bit)
    • Ruby
    • learning Java
    • learning C/C++

    I cut my teeth on OOP with Ruby. It's a great language to learn for that. Probably my favorite language today. Although, assembler puts the bread on the table. :)

    Todd
     
  25. lazydog macrumors 6502a

    Joined:
    Sep 3, 2005
    Location:
    Cramlington, UK
    #25
    For what it's worth, I prefer to append a _ to all member variables, eg

    Code:
    class tuna
    {
    
    int fish_
    
    } ;
    
    I find this the easiest to read.

    b e n
     

Share This Page