Schedule‎ > ‎

12A: Classes and Objects

Questions, Answers and Review

  • Questions from last class?
  • Questions about homework?

^ top

Library Review

  1. What is an Arduino library? (answer)
  2. What two types of files are required at a minimum for a library?
    1. examples file
    2. header file (.h)
    3. implementation file (.cpp)
    4. keywords file (keywords.txt)
  3. According to the lab, what is a class? (answer)
  4. Which of the following goes into a header file?
    1. Class declaration
    2. #include "Arduino.h"
    3. #ifndef sequence
    4. Implementation code
  5. Which of the following goes into an implementation file?
    1. #include "Arduino.h"
    2. #include header file
    3. Function definitions
    4. Keywords
  6. Besides the two required files, what other files should be included in a library?
    1. Examples files
    2. keywords.txt
    3. Library directives
    4. Style guidelines
  7. What is the best way to package an Arduino Library? (answer)


Improving Our Classes

Learner Outcomes

At the end of the lesson the student will be able to:

  • Add multiple constructors
  • Access member variables
  • Discuss and avoid variable shadowing

^ top

Libraries and Classes

  • We can write our own libraries for the Arduino
  • Writing our own libraries is a way to package code for reuse
  • All the Arduino libraries are written as classes -- an important topic in computer science
  • Arduino uses classes for their libraries because classes are a good way to organize variables and functions into logical modules
  • By writing classes and libraries, we can make that code more easily reusable

Activity: Setup the Working Project (5m)

  1. Download the following working example for our RGB LED class:

    Example project: color_cycle.zip.
    Do NOT make this file a library file.

  2. Get an unzipped copy the project.

    For Windows users, double-click on the downloaded file and drag the color_cycle folder to the desktop.

  3. In the unzipped folder, open the color_cycle.ino file.
  4. Verify you unzipped the project correctly by pressing the Verify button.
    Verify button If you have problems, ask a classmate or the instructor for help as needed.
  5. Upload your sketch to test that it works correctly.

    You should now see the RGB LED cycle through the colors red, green and blue. If you have problems, ask a classmate or the instructor for help.

  6. Save your color_cycle sketch as we will be adding to it in subsequent exercises.
Do NOT make this sketch a library file.

When finished, please help those around you.

Classes

  • A class is a logical collection of variables and functions -- called member variables and member functions
  • As an example, we declared a class for RGB LEDs in RGB_LED.h:
    class RGB_LED {
      public:
        RGB_LED(int redPin, int greenPin, int bluePin, bool commonAnode);
        void setColor(int red, int green, int blue);
      private:
        int _redPin;
        int _greenPin;
        int _bluePin;
        bool _commonAnode;
    };
    
  • Adding items to a class or other structure is known as encapsulation
  • The keyword private means the items that follow are available only inside the class
  • The keyword public means the items that follow are publicly accessible outside the class
  • In general, functions (including constructors) are declared public and variables are declared private

Information Hiding

  • Information hiding is the concept of protecting parts of the design that are likely to change
  • The use of the keyword private is a key part of how we implement information hiding
  • The public parts of the design are know as the interface
  • The key idea is to prohibit access to all parts of the design that may change
  • The reason for public functions and private variables is to allow us to change the variables
  • We cannot easily change the type or quantity of variables in a class that is used by many different software applications
  • Thus we hide the variables and force access through public functions
  • As long as the header remains the same, we can always change the way that functions are written
  • Also, we can write easily add new functions as required

Constructors and Functions

  • Inside the class declaration, we only wrote declarations for constructors and functions
  • Only providing the declarations inside the class is another part of information hiding in C++
  • When we declare functions, we must eventually write the definitions
  • In C++ we write the definitions in the implementation (.cpp) file
  • Then we connect the two parts by including the header (.h) file
    #include "RGB_LED.h"
    
  • The following are the functions definitions we wrote for our example library class
  • We tell the compiler that these are member function definitions by putting ClassName:: before the function name
  • Notice that member functions can access the private member variables

RGB_LED Implementation (.cpp) File

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include "Arduino.h"
#include "RGB_LED.h"

RGB_LED::RGB_LED(int redPin, int greenPin, int bluePin, bool commonAnode) {
  _redPin = redPin;
  _greenPin = greenPin;
  _bluePin = bluePin;
  _commonAnode = commonAnode;
  pinMode(redPin, OUTPUT);
  pinMode(greenPin, OUTPUT);
  pinMode(bluePin, OUTPUT);
}

void RGB_LED::setColor(int red, int green, int blue) {
  if (_commonAnode) {
    red = 255 - red;
    green = 255 - green;
    blue = 255 - blue;
  }
  analogWrite(_redPin, red);
  analogWrite(_greenPin, green);
  analogWrite(_bluePin, blue);
}

Constructor Functions

  • The first member function in the above is called a constructor function
  • The purpose of a constructor is to initialize member variables and setup the Arduino
  • We can always tell a constructor from other member functions because a constructor:
    • has no return type
    • has the same name as the class
  • A constructor is called automatically whenever we construct an object from a class
  • We discuss constructing objects in the next section

Check Yourself

  1. True or false: a class contains (encapsulates) both variables and functions.
  2. To allow only member functions and constructors of an object to access a member variable, use the keyword ________.
  3. True or false: good programming practice is to set the accessibility of all member variables to private.
  4. Public functions are known as the ________ of a class.
  5. True or false: the purpose of a constructor is to initialize all the member variables and setup other things needed for the class.

^ top

Classes and Objects

  • Once we have a class defined, we may construct objects from the class
  • The syntax for constructing an object is:
    ClassName objectName(parameterList);
    
  • Where:
    • ClassName: the name of the class type
    • objectName: the variable of the class type
    • parameterList: the types an names of parameters
  • For example, to construct an RGB_LED object named rgb:
    RGB_LED rgb(11, 10, 9, true);
    
  • The data type of this object (variable) is RGB_LED and the name of the object is rgb
  • Whenever an object is created from a class, the following steps occur:
    1. Memory is allocated for all the member variables:
      _redPin
      _greenPin
      _bluePin
      _commonAnode
    2. A constructor is called to assign values to the member variables
  • For our example
    RGB_LED rgb(11, 10, 9, true);
    
    we ended with an object in memory named rgb with the following values:
    _redPin = 11
    _greenPin = 10
    _bluePin = 9
    _commonAnode = true

The Difference Between Classes and Objects

  • What is the difference between a class and an object?
  • A class is a template for building modules
  • An object is one of the modules build from the template
  • By analogy, a class is like the design of a house (blueprints)
  • We need to construct the house from the design before we can use the object:
    House construction from blueprints

  • Other analogies may help understanding:
    • Cookie cutter and cookies: a class is like a cookie cutter and objects are like cookies
    • Mold and Pots: a class is like a mold and objects are the clay pots coming out of the mold.
    • Dog and Fido: a class is like the concept of a dog and objects are individual animals like Fido.
    • Stencil and drawings: a class is like stencil and objects are the drawings made from the stencil.
  • For the above analogies, which is the class and which is the object?
  • In the next exercise we put these ideas into practice to create a class and then construct objects from the class template

Check Yourself

  1. Of the following, the code that correctly constructs an object for the class Morse is ________.
    1. Morse code(13);
    2. code Morse(13);
    3. Morse = code(13);
    4. Morse(13) code;
  2. True or false: when an object is constructed, C++ sets aside memory for all the member variables automatically.
  3. True or false: every object has separate memory space for its member variables.

^ top

Member Functions

  • Recall that functions declared inside a class are known as member functions
  • To make use of our class, we must define member functions
  • We declare member functions inside the class like:
    class RGB_LED {
      public:
        RGB_LED(int redPin, int greenPin, int bluePin, bool commonAnode);
        void setColor(int red, int green, int blue);
        //... other code omitted
    };
    
  • Each member function declaration (prototype) listed in the class interface must be implemented
  • We define member functions like we define other functions
  • The only difference is we must put ClassName:: in front of the function name
    • Where ClassName is the name of our class
  • The ClassName:: prefix makes it clear to the compiler that we are defining a member function for that class

Mutator Functions

  • The setColor() function is a mutator function - a function that modifies member variables or the device state
  • When a mutator function runs, the value of a member variable changes or the Arduino device changes
  • In this case, the setColor() function changes the state of three Arduino pins

Accessor Functions

  • Another type of function is the accessor function
  • An accessor function queries the object for some information without changing it
  • As an example of an accessor function, we can define isCommonAnode() as follows:
    bool RGB_LED::isCommonAnode() const {
      return _commonAnode;
    }
    
  • The isCommonAnode() function asks a questions about the object
  • Because the function does not change any variable, we add the keyword const
  • Whenever we design an accessor function, we should add the keyword const
  • The const keyword tells that compiler to make sure that the function does not change the object

Member Function definition Syntax

  • The general syntax for defining a function declared inside a class is:

    returnType ClassName::functionName(parameter1, ..., parametern) [const] {
        statements }

  • Where:
    • returnType: the type of the value returned
    • ClassName: the name of the class
    • functionName: the name you make up for the function
    • parameterx: the input values, if any
    • statements: the statements to execute when the function is called
  • Note that the square brackets indicate that const is optional

Function Naming Conventions

  • As mentioned before, use verbs for function names since they perform an action
  • Also start function names with a lower case letter
  • Verbs and lower case are not required by syntax, but is a convention that professional programmers follow

Activity: Add an Accessor Function (4m)

  1. Open the color_cycle sketch from the last exercise.

    Make sure the color_cycle file is NOT a library file.

  2. Add a new public member function declaration (prototype) to the class declaration (in the RGB_LED.h header file)  ith the following prototype:
    bool isCommonAnode() const;
    
  3. Add the implementation of the isCommonAnode() function to the implementation file (RGB_LED.cpp)
    bool RGB_LED::isCommonAnode() const {
      return _commonAnode;
    }
    
  4. To test the new function, add the following code to the setup() function of color_cycle.ino:
    Serial.begin(9600);
    Serial.println(String("Common anode: ") + rgb.isCommonAnode());
    
  5. Upload your code and open the Serial Monitor to test that the new function works correctly.

    The Serial Monitor should display:

    Common anode: 1
    

    If you have problems, ask a classmate or the instructor for help.

  6. Save your color_cycle sketch as we will be adding to it in subsequent exercises.

When finished, please help those around you.

Check Yourself

  1. A function declared inside a class is known as a ________ function.
  2. A valid first line of a member function definition is ________.
    1. int status()
    2. int Motor::status() const
    3. Motor::status()
    4. Motor::int status() const
  3. A function that can modify an object is known as a(n) ________ function.
  4. A C++ standard for accessor functions is to include in the function header the keyword ________.
  5. True or false: member variables can always be accessed by member functions, even when declared private.

^ top

Working with Member Variables

  • Only member functions of a class are allowed to access the private member variables
  • For example, if we tried to access a member variable from loop():
    RGB_LED rgb(11, 10, 9, true);
    rgb._commonAnode = true; // error
    
  • How can we change the value of a private (hidden) member variable from setup() or loop()? (answer)
  • To work with a private member variable, we must use public accessor or mutator function

Get Functions

  • We often need to know the value of private variables
  • For this we use public functions to get the value of private data
  • Called get-functions (a.k.a. accessor functions)
  • A common naming convention is to use the name of the value with the word get prepended
  • However, in the case of a boolean value, the convention is to prepend the word if, like we did with
    bool RGB_LED::isCommonAnode() const {
      return _commonAnode;
    }
    

Set Functions

  • The simplest mutator function is one that changes a single value
  • A common naming convention is to use the name of the value with the word set prepended
  • Thus to set the mode to common anode or cathode, we would add the following to our interface:
    void setCommonAnode(bool mode);
    
  • Mutator functions should verify the value is correct before setting it
  • For example, since price should not be a negative value, we define our function like:
    void RGB_LED::setCommonAnode(bool mode) {
      _commonAnode = mode;
    }
    
  • To call a set-function from a non-member function like setup() we write code such as:
    RGB_LED rgb(11, 10, 9, true);
    // ... other code omitted
    rgb.setCommonAnode(true);
    

Designing Accessor and Mutator Functions

  • Not every member variable needs an accessor function
  • Also, not every variable needs a mutator function
  • In addition, not every get-function needs a matching set-function
  • Remember that the implementation details are hidden
  • Just because a class has member functions prepended with get or set does not necessarily explain how the class is designed
  • For example, a Time class may have the following interface:
    class Time {
    public:
       Time(int hour, int min, int sec);
       Time();
    
       int getHours() const;
       int getMinutes() const;
       int getSeconds() const;
       int secondsFrom(Time t) const;
       void addSeconds(int s);
    
    private:
       int timeInSecs;
    };
    
  • Despite using hours, minutes and seconds in the public interface, all time values are stored as seconds:
    Time::Time(int hour, int min, int sec) {
        timeInSecs = 60 * 60 * hour + 60 * min + sec;
    }
    
  • Then the accessor functions simply convert the seconds to hours, minutes and seconds as needed
    int Time::getMinutes() const {
        return (timeInSecs / 60) % 60;
    }
    

Check Yourself

  1. True or false: we should declare all member variables private. (Why?)
  2. True or false: the usual naming convention for get or set functions is to use the word "get" or "set" followed by the name of the value.
  3. True or false: every member variable must have a get- and set-function.

^ top

Multiple Constructors

  • We can write a class with more than one constructor
  • All constructors have the same name as the class, but have different parameter lists
  • For example, we could write two different constructors in our RGB_LED interface:
    public:
      RGB_LED(int redPin, int greenPin, int bluePin, bool commonAnode);
      RGB_LED(int redPin, int greenPin, int bluePin);
    
  • The technique of defining multiple constructors is known as constructor overloading
  • Having multiple constructors allows us to create an object in different ways
  • By allowing different ways of creating an object, we make our classes more flexible and easier to reuse

Example: Constructor with Four Parameters

  • Here is our original constructor with four parameters
    RGB_LED::RGB_LED(int redPin, int greenPin, int bluePin, bool commonAnode) {
      _redPin = redPin;
      _greenPin = greenPin;
      _bluePin = bluePin;
      _commonAnode = commonAnode;
      pinMode(redPin, OUTPUT);
      pinMode(greenPin, OUTPUT);
      pinMode(bluePin, OUTPUT);
    }
  • To construct an object that calls this constructor, we write code like:
    RGB_LED rgb(11, 10, 9, true);
    

Example: Constructor Definition with Three Parameters

  • If you want to accept only some arguments, code a constructor appropriately:
    RGB_LED::RGB_LED(int redPin, int greenPin, int bluePin) {
      _redPin = redPin;
      _greenPin = greenPin;
      _bluePin = bluePin;
      _commonAnode = true;
      pinMode(redPin, OUTPUT);
      pinMode(greenPin, OUTPUT);
      pinMode(bluePin, OUTPUT);
    }
  • For member variables without a parameter we supply a default value
    _commonAnode = true;
    
  • To construct an object that calls this constructor, we write code like:
    RGB_LED rgb(11, 10, 9);
    

Default Constructors

  • Constructors that can be called with no arguments are known as default constructors
  • Calling a default constructor assigns default values to the member variables
    RGB_LED::RGB_LED() {
      _redPin = 11;
      _greenPin = 10;
      _bluePin = 9;
      _commonAnode = true;
      pinMode(redPin, OUTPUT);
      pinMode(greenPin, OUTPUT);
      pinMode(bluePin, OUTPUT);
    }
  • To construct an object that calls the default constructor, we write code like:
    RGB_LED rgb;
    
  • Notice that there are no parenthesis when calling the default constructor

Implicit Default Constructors

  • If the programmer does not define any constructor, then the compiler supplies an empty default constructor like the following:
    RGB_LED::RGB_LED() { }
    
  • However, if the programmer defines any constructor, the compiler does not supply an implicit default constructor

Check Yourself

  1. For any class, you can define ________ constructors.
    1. 1
    2. 2
    3. 3
    4. as many as you need
  2. A constructor with no parameters that sets the member variables to default values is known as a ________ constructor.
  3. True or false: if you do not write any constructors for a class, the compiler will add one to the compiled code.

^ top

Local Variables, Parameters and Shadowing

  • Shadowing occurs when a local variable or parameter has the same name as a member variable of a class
  • Local variables and parameters with same name as a member variable "hide" the member variable
  • Any use of the variable's name will refer to the local variable or parameter
  • This is the source of many an elusive bug
  • As an example, let us implement the following constructor with a shadowing problem
  • Which lines contain local shadowing in the following program and how do you correct the problem?

Constructor with Shadowing Problem

RGB_LED::RGB_LED(int _redPin, int _greenPin, int _bluePin) {
  _redPin = _redPin;
  _greenPin = _greenPin;
  _bluePin = _bluePin;
  _commonAnode = true;
  pinMode(_redPin, OUTPUT);
  pinMode(_greenPin, OUTPUT);
  pinMode(_bluePin, OUTPUT);
}

Preventing Shadowing Problems

  • We should not use the same name for a local variable or parameter and a class member variable
  • That is why we have been following the leading underbar convention when naming member variables
  • Remember that a parameter is a local variable, just initialized in a special way
  • Thus you should use a different name for a parameter and a member variable

What the Shadow Knows

  • We correct shadowing by making sure a parameter name is different than a member variable name
  • For example:
    RGB_LED::RGB_LED(int redPin, int greenPin, int bluePin) {
      _redPin = redPin;
      _greenPin = greenPin;
      _bluePin = bluePin;
      _commonAnode = true;
      pinMode(_redPin, OUTPUT);
      pinMode(_greenPin, OUTPUT);
      pinMode(_bluePin, OUTPUT);
    }
    
  • In the above code, we changed the name of the parameters
  • For example, one parameter that was _redPin is now redPin (without the leading underscore)
  • We changed the other parameters as well

^ top

Exercise 1: (10m)

In this exercise we explore problems with shadowing.

Specifications

  1. Start the Arduino IDE with your sketch from the last activity
  2. Add the following constructor to the class declaration (in the RGB_LED.h header file).
    RGB_LED(int _redPin, int _greenPin, int _bluePin);
    
  3. Add the following constructor to the implementation (.cpp) file.
    RGB_LED::RGB_LED(int _redPin, int _greenPin, int _bluePin) {
      _redPin = _redPin;
      _greenPin = _greenPin;
      _bluePin = _bluePin;
      _commonAnode = true;
      pinMode(_redPin, OUTPUT);
      pinMode(_greenPin, OUTPUT);
      pinMode(_bluePin, OUTPUT);
    }
    
  4. Modify the color_cycle.ino file by changing the code:
    RGB_LED rgb(11, 10, 9, true);
    
    to:
    RGB_LED rgb(11, 10, 9);
    
  5. Compile the sketch to verify you copied the code correctly.
  6. Upload your code to test and observe the effect of shadowing.
  7. Make a plain text file named shadowing.txt, copy the following questions into the file and then write answers to the questions.
    1. What do you see when the sketch runs?
    2. What is the cause of the problem?
    3. How do you correct the problem?
  8. Correct the shadowing problem to ensure you understand the solution and answer to questions 3 above.
  9. Save your shadowing.txt file to submit to Canvas with the next lab.

When finished, please help those around you.

^ top

Wrap Up and Reminders

  • For the next homework, see the schedule
  • When class is over, please shut down your computer.
  • Complete unfinished exercises from today before the next class meeting

^ top

ċ
color_cycle.zip
(2k)
Edward Parrish,
Nov 15, 2015, 8:49 PM