Thread: General Class Structure

  1. #1
    Registered User
    Join Date
    Dec 2013
    Location
    Oregon
    Posts
    7

    General Class Structure

    I am trying to update my old C code to C++ , and trying to grasp C++ at the same time.

    It's not clear to me when creating a class, which variables I should declare as private (which are basically behaving as global variables to member functions) verses those I should simply pass as parameters.

    1) Do I take ALL parameters and return values of functions I am converting from C to C++ and make them private members?

    2) If I don't have to make private members of all parameters and returned values of functions, what criteria do I use to determine which parameters should be privatized to the class versus those which can simply be supplied externally? e.g, cTest.SetVals(4,5), where the variables set to 4 and 5 respectively are not necessarily member variables in the sense that they are only defined in the context of the function, not as stand alone variables in the class.

    3) Is my approach wrong to treat the member variables as global variables to the member function as in the example below: mVal, mDrop and mDun were defined as private variable members but they are not passed as parameters but simply show up as global references.

    Code:
    //drop => drop value.   dun: //0 = MOA, 1 = MILs
    void AS1113::SetMOA(){
        
        float val;
        unsigned char step;
    
        
        #define MAX_POS        3     //supports a maximum MOA 2*3 = 6
        
            
        if(mDun)        //If in mils
        val = mDrop * MIL_CONVT;   //convert to MOA
        else {val = mDrop;}
        
        step = (unsigned char)floor((MAX_POS - (val/MOA_RESOLUTION)) + 0.5);
            
        mVal = TRAY >> step;
        
        WriteLED();
            
    }
    4) If you are not supposed to use these member variables globally, is the only way to pass these values by self-referencing the class, as in the example below? By self-referencing, I mean that the class basically has to pass an object created from itself as a parameter.

    Code:
    void AS1113::WriteLED(AS1113& a){
        
        int temp;
    
        //Clock in LSB First
        
        gpio_set_gpio_pin (OEN);
        
        for(short x=0; x < LED_CYCLES; x++){
            temp = a.mVal & LSB_MASK;
            if (temp)
                gpio_set_gpio_pin (SDI);
            else 
                {gpio_clr_gpio_pin (SDI);}
            
            cpu_delay_us(1, FOSC0);
            gpio_set_gpio_pin (SCK);
            cpu_delay_us(1, FOSC0);
            gpio_clr_gpio_pin (SCK);
            a.mVal = a.mVal >> 1;
        }//end for
        
        
        gpio_clr_gpio_pin (SDI);
        //Pulse LOAD HIGH for 1us
        cpu_delay_us(1, FOSC0);
        gpio_set_gpio_pin (LOAD);
        cpu_delay_us(1, FOSC0);
        gpio_clr_gpio_pin (LOAD);
        
        //Pulse OE LOW for 1us
        cpu_delay_us(1, FOSC0);
        gpio_clr_gpio_pin (OEN);
        
        
    }

  2. #2
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    Think about like this:
    Separate variables into three groups:
    - Local variables: define these inside the functions in the class as usual.
    - Stateful variables (i.e. variables whose values you need to remember between functions calls and must share between all functions in the class): make these member variables. As a rule, try to make all member variables as private and user setters/getters to access them.
    - External variables: pass these as parameters.

    So what are stateful variables? Well, first you must decide what a class does. What is its purpose? After that you can decide what information that object when created must remember until it goes away.
    Take a calendar as example. The calendar must remember between function calls (e.g. for as long as it lives), all the appointments made in it. Therefore, it makes that these variables are stateful and hence member variables.
    But there is also external information such as when you want to make an appointment, then the date, time and possibly name. These you must pass to the object when calling an appropriate function to make an appointment.

    Do not under any circumstances pass an instance of the object itself to one of its member functions (e.g. 4). All objects have a keyword called "this" that refers to itself.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  3. #3
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,336
    Treating member variables as "global variables to member functions" is not a very good way to think of things, because each object will have its own member variables. I don't really see what your idea of an object is in your stated code, so I can't give all that directly relevant an example, but as an example:
    Code:
    #include <iostream>
    
    class Foo {
        public:
            Foo(int x);
            int process();
        private:
            int state;
    };
    
    Foo::Foo(int x) : state(x) {};
    
    int Foo::process() {
        return state++;
    }
    
    int main(void) {
        Foo tom(4), harry(8); // initializes the state of tom and harry
        std::cout << tom.process() << std::endl; 
        std::cout << harry.process() << std::endl;  
        std::cout << tom.process() << std::endl;
    }
    Although I didn't pass any state variable into the process function, it is acting on a particular object (either tom or harry) so it will use either tom's version of state or harry's, depending. So the value of "state" isn't really global, but tied to the object it belongs to.

  4. #4
    Registered User
    Join Date
    Dec 2013
    Location
    Oregon
    Posts
    7

    Class Structure

    Thanks for the advice, Elysia and tabstop. It's just a different way of thinking about things that I am not very familiar with. In terms of the word "global", I am using this term rather loosely. I understand that these member variables carry over with each object, so they are not truly global. However, I see them as global in the context of an individual object, with respect to member functions included in that object. They are no longer passed as parameters as they were in C but can be addressed directly within the function call itself. In terms of self-referencing, I took this from a tutorial on C++, but it appears to me that this gets ugly very quickly the more levels involved ( ie, a function calling another function which calls yet another)

    I just have to get used to eliminating parameterized functions.

  5. #5
    Registered User
    Join Date
    Dec 2013
    Location
    Oregon
    Posts
    7

    Is this the correct approach?

    If I still wanted to maintain some semblance of parameter passing, does the following code snippet represent the correct approach.

    .hpp

    Code:
    class I2C {
          
           protected:
                  unsigned char mDataW;
                  unsigned char mDataR;
                  unsigned char mCondition;
                 
           public:
                 
                  I2C();
                  unsigned char GetData(){return mDataR};
                  void SetData (unsigned char Data) 
                          {mDataW = Data};
                  void SetCondition (unsigned char Condition)
                          {mCondition = Condition;}
          
                  void I2C_Start ();
                  void Bus_Not_Busy ();
                  void I2C_Stop ();
                  void I2C_Write(unsigned char Data);
                  unsigned char I2C_Read(unsigned char Condition);
           };
    Corresponding .CPP file (portion) to Pass Parameters

    Code:
    
    
    
    //Write a byte followed by an ACKNOWLEDGE. If no Acknowledge is forthcoming, an error
    //is generated
    //INPUT: data byte
    //OUTPUT: error = 1
    void I2C::I2C_Write (unsigned char Data){
     
      unsigned char temp = 0;
      unsigned char error = 0;
     
      SetData(Data);   //mDataW = Data
     
      for ( unsigned char x = 0; x < 8; x++ ){
     
        temp = mDataW & MSB8;  //Mask the highest-most bit and put result in temp
     
        if (temp == MSB8){
           gpio_set_gpio_pin(SDA);}
        else {
           gpio_clr_gpio_pin(SDA);}
     
        cpu_delay_cy(DELAY_ELEMENT);
      
        gpio_set_gpio_pin(SCL);              //SCL line goes high
     
        mDataW = mDataW << 1;
     
        cpu_delay_cy(DELAY_ELEMENT);
      
        gpio_clr_gpio_pin(SCL);
     
        }//end for(x)- exits after the 8th Clock Cycle
     
     
        //The Slave indicates proper reception of a command by pulling the DATA
        //pin LOW (ACK bit) after the FALLING Edge of the 8th SCK
     
        //data line released
            gpio_enable_gpio_pin(SDA);      //Set SDA as an INPUT
     
            cpu_delay_cy(DELAY_ELEMENT);
     
            gpio_set_gpio_pin(SCL);              //SCL line goes high
     
            cpu_delay_cy(DELAY_ELEMENT);
          
            try{  
                  error = (unsigned char)gpio_get_pin_value(SDA);  //Release when SDA line goes LOW - ACKNOWLEDGE
                                                                                              //ACKNOWLEDGE is a LOW SIGNAL.
                  if (error){
                         throw "Communication Error: No Return Acknowledge!";
                  }
     
            };
     
         gpio_clr_gpio_pin(SCL);   //Bring SCL LOW
     
         cpu_delay_cy(DELAY_ELEMENT);
        
         gpio_set_gpio_pin(SDA);      //Set SDA as an OUTPUT
     
     
    }//end I2C_Write ()
    Corresponding .CPP file (portion) to Return By Value

    Code:
    //BASIC Read a byte, followed by an ACK to read more data or a NACK to
    //end transmission.
    //INPUT: Condition = ACK or NACK.  Use NACK if acquiring last byte.  This will
    //be followed by a Stop Condition.
    //OUTPUT: byte of data.
    unsigned char I2C::I2C_Read (unsigned char Condition){
     
             mDataR = 0x00;
     
             SetCondition(Condition);  //mCondition = Condition;  
     
             gpio_enable_gpio_pin(SDA);      //Set SDA as an INPUT
     
             for ( unsigned char x = 0; x < 8; x++ ){
     
                    gpio_set_gpio_pin(SCL);              //SCL line goes high
     
                    cpu_delay_cy(DELAY_ELEMENT);
     
                    mDataR = mDataR << 1;
     
                    if((unsigned char)gpio_get_pin_value(SDA)) mDataR++;
     
                    cpu_delay_cy(DELAY_ELEMENT);
     
                    gpio_clr_gpio_pin(SCL);                       //Bring SCL LOW
     
                    cpu_delay_cy(DELAY_ELEMENT);
     
     
             }//end for()
     
             //Generate Extra Clock for Acknowledge or Not Acknowledge
             if (mCondition == ACK) {
                    gpio_clr_gpio_pin(SDA);}
             else {
                    gpio_set_gpio_pin(SDA);}       //Set SDA as an OUTPUT}
     
             cpu_delay_cy(DELAY_ELEMENT);
     
             gpio_set_gpio_pin(SCL);
     
             cpu_delay_cy(DELAY_ELEMENT);
     
             gpio_clr_gpio_pin(SCL);
     
             cpu_delay_cy(DELAY_ELEMENT);
            
             return(mDataR);
     
    }//end I2C_Read ()
    
    

  6. #6
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    Read and write data does not need to be persistent because their usefulability ends when the function ends.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  7. #7
    Registered User
    Join Date
    Dec 2013
    Location
    Oregon
    Posts
    7
    Are you saying they don't need to be private members?

  8. #8
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    They do not need to exist. At all.
    You already have the data passed in as arguments. There is no need to save those arguments anywhere because those arguments are only useful until the functions end. Put simply in C: you don't need global variables to store that data. The data is useless when the functions end.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. [General Help] Dynamic Arrays and General Pointer syntax
    By Amberlampz in forum C Programming
    Replies: 22
    Last Post: 07-26-2011, 10:58 PM
  2. Replies: 4
    Last Post: 05-31-2011, 11:34 AM
  3. Use a class or structure?
    By RealityFusion in forum C++ Programming
    Replies: 6
    Last Post: 08-29-2005, 06:46 AM
  4. General Class
    By petruccigp in forum C++ Programming
    Replies: 3
    Last Post: 06-01-2005, 10:34 AM
  5. Replies: 1
    Last Post: 11-23-2003, 08:51 AM

Tags for this Thread