9: Coding Classes and Objects

What We Will Cover


Continuations

Homework Questions?

Questions from last class?

What type of stream must be used to send data from a program to a file?

  1. input stream
  2. output stream
  3. character stream
  4. binary stream

9.1: Introduction to Object-Oriented Programming

Objectives

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

  • Describe the concept of encapsulation and data hiding
  • Describe the relationship between a class and an object
  • Code instance variables, constructors, and instance functions
  • Create objects from classes

9.1.1: About Object-Oriented Programming

Procedural and Object-Oriented Programming

  • There are two major approaches to programming:
    • Procedural programming
    • Object-oriented programming (OOP)
  • So far, we have mostly used procedural programming
  • Procedural programming is about how to use functions to solve problems
  • Work is organized into modules using functions (see lesson 6.3.1)
  • Procedural programming requires you to pass data via function parameters
  • Passing data becomes a problem as programs become more complex
  • Almost always specifications for a program change over time
  • Specification changes often require the data passed around to change
  • Changing the data requires many of the procedures to change
  • This causes lots of added work for the programmer
    • Which causes lot of added bugs

Classes and Encapsulation

  • OOP takes a different approach to handling data
  • It combines both data and procedures into objects
  • An object is a software entity that contains data and procedures
    • Data are stored in variables
    • Procedures are functions that operate on the data
  • To protect data, OOP uses encapsulation and data hiding
  • Encapsulation: inclusion of a number of items into a single unit

    Data hiding: restricting other program code from accessing data stored in a class

  • When data is hidden from other program code, access is performed through functions
    • Publicly available functions are known as an object's interface
  • Controlling access through functions helps prevent accidental data corruption
  • Access functions check a new value before assigning it to a variable
  • In addition, changing the way data is stored, the variable's type, is easy
    • You only need to change the access functions
  • Other program code calling the functions do not need to change

Object Reusability

  • Encapsulation used by OOP provides another benefit: reusability
  • Objects are not standalone programs
  • Rather, they are used by programs that need their services
  • This allows one programmer to develop an object to perform one task
    • Such as displaying a graphical image
  • Another programmer can use the object in his or her program
    • Such as a Web browser

9.1.2: Defining Classes for Objects

  • Before an object can be created, it must be designed by a programmer
  • The programmer defines the fields and functions and writes a class
  • Think of a class as a "blueprint" for creating objects
  • Thus, a class is not an object, but a description of an object
  • Programs use a class to create, in memory, as many objects of a specific type as needed
  • Each object created from a class is called an instance of the class

Syntax

    class className {
        permissionLabel1:
            member1;
        permissionLabel2:
            member2;
        ...
    };

For Example

  • The following is the code for a class named Product
  • Class declarations contain three general sections:
    • Constructors
    • Instance functions
    • Instance variables
#include <iostream>
using namespace std;

class Product {
public:
    // Constructors
    Product();
    Product(string newName, double newPrice);
    // Instance functions
    string getName() { return name; }
    double getPrice() { return price; }
    void setName(string newName);
    void setPrice(double newPrice);
private:
    // Instance variables
    string name;
    double price;
};

// no-arg constructor
Product::Product() {
    name = "Unknown";
    price = 0.0;
}

Product::Product(string newName, double newPrice) {
    setName(newName);
    setPrice(newPrice);
}

void Product::setName(string newName) {
    if (newName.length() == 0) {
        name = "Unknown";
    } else {
        name = newName;
    }
}

void Product::setPrice(double newPrice) {
    if (newPrice > 0.0) {
        price = newPrice;
    } else {
        price = 0.0;
    }
}

// For testing
int main() {
    Product prod("Milk", 3.95);
    cout << prod.getName() << endl;

    return 0;
}
  • Note that there are two sections to the code
  • The class is declared between the curly braces following the class name
  • Class member functions are defined after the class declaration
  • To show that the function belongs to the class, you use the class name and the scope resolution operator "::"
  • void Product::setName(string newName) {
        //...
    }
    

Private Members and Data Hiding

  • Note the keywords private and public
  • Referred to as access specifiers or visibility modifiers
    • private: can only be accessed by member functions of this class
    • public: can be accessed by member functions of any class
  • Setting private accessibility for all data is a good design practice
  • Protects data by controlling access only through the public functions
  • Allows us to change how data may be stored at some future time
    • Without affecting code outside the class

Programming Style: Class Naming Conventions

  • Use nouns to name classes -- they represent objects
  • Start class names with a capital letter
  • Capital letter differentiates classes from functions and variables

9.1.3: Coding Instance Variables

  • We now look at each section in more detail
  • Instance variables store the attributes (data) of an object
  • Each object has its own copy of these variables
  • Syntax for declaring instance variables:
  • type variableName
  • For now, use the private access modifier for all instance variables
  • The type can be any primitive type or class name
  • Instance variables work just like variables you have used previously
  • Only difference is they are declared inside the class and outside of any function body

Examples

    private:
        string title;
        double price;
        Product myProduct;
    

9.1.4: Coding Constructors

  • Constructors are called whenever an object is created from a class
  • Its primary purpose is to initialize the instance variables
  • Constructors typically have two sections: declaration and definition
  • Constructor declarations (prototypes) are placed inside the class declaration
  • Constructor definitions are placed outside the class declaration
  • Syntax for declaring constructors:
  • ClassName(parameterList);
    
  • Place constructors in the public section of the class declaration
    • Your program calls the constructor when creating new objects
  • Constructors must use the same name and capitalization as the class name
  • public:
        // Constructors
        Product();
        Product(string newName, double newPrice);
    
  • Constructors can have zero or more parameters, just like functions
  • You can have many constructors in a class, but each must have a different signature
  • The name of the constructor combined with the parameter list form its signature

Example: Constructor Definition with no Parameters

  • If you do not want to accept arguments for instance variables, code a constructor with no parameters:
  • Product::Product() {
        name = "Unknown";
        price = 0.0;
    }
    
  • Note that constructors with no parameters are called no-arg constructors

Example: Constructor Definition with two Parameters

  • If you want to accept arguments for all instance variables, code a constructor with parameters for each variable:
  • Product::Product(String newName, double newPrice) {
        setName(newName);
        setPrice(newPrice);
    }
    

Example: Constructor Definition with one Parameter

  • If you want to accept only some arguments, code a constructor appropriately:
  • Product::Product(String newName) {
        setName(newName);
        price = 0.0;
    }
    

Default Constructors

  • If the programmer does not define a constructor, then the compiler supplies an empty one like the following:
  • public:
        Product() {}
    
  • However, if the programmer defines any constructor, the compiler does not
  • You should always define your own no-arg constructor

9.1.5: Coding Instance Functions

  • Instance functions define the operations that can occur on an object
  • Performing these operations is sometimes referred to as passing messages
  • Thus, objects use instance functions to pass messages to each other
  • The syntax for declaring instance functions:
  • returnType functionName(parameter list);
    
  • For example:
  • public:
        string getName() { return name; }
        double getPrice() { return price; }
        void setName(string newName);
        void setPrice(double newPrice);
    
  • In general, you should declare most class functions public
  • However, if you want to prevent other classes from using a function, declare it private
  • The name of the function combined with the parameter list form its signature
  • You can have many functions with the same name in a class
    • However, each must have a different signature

Inline Functions

  • Note that some functions define the function body along with the declaration
  • These are known as in-line functions
  • It is common practice to code such short function bodies in the class definition section

Function Naming Conventions

  • As mentioned before, use verbs for function names
    • Appropriate since they perform an action
  • Also start function names with a lower case letter
  • Not required, but is a convention that professional programmers follow

Set Functions

  • When variables are declared private, you may still need to modify them
  • Use public functions to allow modification of the data
  • Called set functions (a.k.a. mutator functions)
  • Use the name of the variable with the word set prepended
  • Such functions should verify the value is correct before setting it
  • For example:
  • void Product::setPrice(double newPrice) {
        if (newPrice > 0.0) {
            price = newPrice;
        } else {
            price = 0.0;
        }
    }
    

Get Functions

  • Also, you often need access to private variables
  • Use public functions to access the data
  • Called get functions (a.k.a. accessor functions)
  • Use the name of the variable with the word get prepended
  • For example:
  • string Product::getName() {
        return name;
    }
    

9.1.6: Creating Objects From Classes

  • After defining a class, we can create objects
  • Syntax for creating an instance of a class:
  • ClassName objectName(argumentList);
    
  • You must call the constructor with the correct sequence and type of arguments
  • If the class has more than one constructor, the constructor matching the argument list is used

For Example

  • We can code a main function to create an instance of our class
  • When the new operator is used, memory space is allocated for the object variables
  • The call to the constructor function copies the argument values to the parameters
  • int main() {
        Product prod("Milk", 3.95);
        cout << prod.getName() << endl;
    }
    
  • When the constructor is called, memory is allocated for the class variables
  • private:
        string name;
        double price;
    
  • Next, the constructor starts executing
  • You can trace the program flow through the code of the class:
  • Product::Product(string newName, double newPrice) {
        setName(newName);
        setPrice(newPrice);
    }
    
    void Product::setName(string newName) {
        if (newName.length() == 0) {
            name = "Unknown";
        } else {
            name = newName;
        }
    }
    
    void Product::setPrice(double newPrice) {
        if (newPrice > 0.0) {
            price = newPrice;
        } else {
            price = 0.0;
        }
    }
    
  • Functions setName() and setPrice() are called with the appropriate arguments
  • Function setName() assigns the instance variable name the parameter newName
  • Function setPrice() assigns the instance variable price the parameter value of newPrice
  • One object, an instance of the class, has now been created and initialized
  • You can create as many objects as you need for your program
  • Once created, your program can access each object using its public functions:
  • cout << prod.getName() << endl;

9.1.7: Summary

  • Procedural programming becomes cumbersome as specifications change
  • Changes to data may require many functions to change
  • Object-oriented programming (OOP) gets around this problem by combining data and procedures into objects
  • OOP then addresses the problem of data protection through encapsulation and data hiding
  • Encapsulation and data hiding allow you to change the inner workings of a class without affecting other classes.
  • Classes are like a template or blueprint for creating objects
  • Objects are constructed from these classes
  • When you define classes, you code three general sections:
    • Instance variables
    • Constructors
    • Instance functions
  • Instance variables store the data of an object
  • Constructors are called whenever an object is created from a class
  • Instance functions are the operations that can occur on the object
  • Access modifiers control access to variables, constructors and functions
    • private: can only be accessed by member functions of this class
    • public: can be accessed by member functions of any class
  • Use private for all variables and public for most functions
  • Use "set" functions to control setting of new values to private variables
  • Use "get" functions to return values of private variables
  • You create an instance of a class like the following:
  • Product prod("Milk", 3.95);
    

Exercise 9.1

  1. Start a text file named exercise9.txt.
  2. Prepare the exercise header as described in the HowTo on submitting exercises
  3. Label this exercise: Exercise 9.1
  4. Submit all exercises for today's lesson in one file unless instructed otherwise
  5. Complete the following and record the answers to any questions in exercise9.txt.

Specifications

  1. Write a definition for a class named Rectangle that has instance variables length and width.
  2. Write a constructor that sets the length and width variables.
  3. Also code get and set functions for both of the variables.
  4. Q1: What are the names of the four functions?

  5. Write a main function that instantiates one or more Rectangle objects.
  6. Submit your Rectangle.cpp file as part of this weeks exercises.

You can use the following code to get started:

#include <iostream>
using namespace std;

class Rectangle {
public:
    // Put one or more constructors here

    // Put instance functions here

private:
    // Put instance variables here

};

// Put function definitions here

// For testing
int main() {
    // place statements to instantiate objects here

    return 0;
}

9.2: Using Objects in Programs

Objectives

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

  • Produce code that uses objects
  • Use the functions of objects to accomplish required tasks
  • Code classes that use other classes

9.2.1: Working With Objects

  • In OOP, only the simplest programs use a single object
  • More common is to have several objects work together in your program
  • Each object performs one specialized task
  • Often times, one of the tasks is to control the operation of the entire program
  • In this section we look at how use sets of objects to create a program
  • But first we discuss a few odds and ends

Including Files

  • Recall the #include directive
  • #include <iostream>
  • It turns out that you can include your own files into another program file
  • Syntax:
  • #include "myfile.cpp"

Programs and the main() Function

  • Each program, or application, can have only one main() function
  • Thus, to use our Product class in other programs, we will need to remove its main() function
#include <iostream>
using namespace std;

class Product {
public:
    // Constructors
    Product();
    Product(string newName, double newPrice);
    // Instance functions
    string getName() { return name; }
    double getPrice() { return price; }
    void setName(string newName);
    void setPrice(double newPrice);
private:
    // Instance variables
    string name;
    double price;
};

// no-arg constructor
Product::Product() {
    name = "Unknown";
    price = 0.0;
}

Product::Product(string newName, double newPrice) {
    setName(newName);
    setPrice(newPrice);
}

void Product::setName(string newName) {
    if (newName.length() == 0) {
        name = "Unknown";
    } else {
        name = newName;
    }
}

void Product::setPrice(double newPrice) {
    if (newPrice > 0.0) {
        price = newPrice;
    } else {
        price = 0.0;
    }
}
  • Note that without a main() function we can no longer compile the code
  • Causes a linker error because every program must have a main() function

9.2.2: Example Application

  • Following is the productapp program
  • It includes the Product class using the #include directive
  • #include "Product.cpp"
  • Has a main function that starts an application
  • Creates Product objects as specified by the user
#include <iostream>
using namespace std;

#include "Product.cpp"

// For testing class Product
int main() {
    char choice = 'Y';
    string name;
    double price;
    while ('Y' == choice || 'y' == choice) {
        cout << "Enter a product name: ";
        cin >> name;
        cout << "Enter the price for a "
             << name << ": ";
        cin >> price;

        Product prod(name, price);
        cout << "You entered:"
             << "\n   Name: " << prod.getName()
             << "\n   Price: " << prod.getPrice()
             << endl;

        cout << "Enter another product? ";
        cin >> choice;
    }

    return 0;
}
  • Note the following code that instantiates (creates) the object
  • Product prod(name, price);
    
  • Also has code to use functions of a Product object
  • prod.getName()
    prod.getPrice()
    

9.2.3: Calling Object Functions

  • To call functions of an object, code the object name followed by a period, the function name and the arguments:
  • objectName.functionName(argumentList);
  • For example, to send no arguments and return a value:
  • string name = prod.getName();
  • Sending an argument and returning no value:
  • prod.setName(name);
  • When you send more than one argument, type a comma between each argument
  • Also, you can call a function as part of a statement:
  • cout << "You entered:"
         << "\n   Name: " << prod.getName()
         << "\n   Price: " << prod.getPrice()
         << endl;
    
  • Function calls are completed before performing other operations

9.2.4: Tracing Code

  • Object-oriented code is more complex to trace
  • Many objects can be instantiated from each class
  • Any of these objects can have the same function called
  • However, the program still executes only one statement at a time
  • The secret to tracing the code is tracking which object is executing a function at any one time
  • When tracing object-oriented code, you need to track both the object and method
  • For example, to trace the flow of ProductApp:
  • main()
        prod.Product()
            prod.setName()
            prod.setPrice()
        prod.getName()
        ...
    
  • Note that when an object's function is called we include the object's name

9.2.5: Summary

  • OOP typically has many objects working together in one program
  • Each object performs one specialized task
  • To call functions of an object, you use their reference variable:
  • prod.getName();
  • To trace the flow of an OOP program, you need to track both the:
    • object name
    • function name
    prod.setName()

Exercise 9.2

  1. Label this exercise: Exercise 9.2
  2. Complete the following and record the answers to any questions in exercise9.txt.

Specifications

  1. Finish tracing the productapp program by listing the object names and function calls in the order of their occurrence:
  2. objName.functionName()

    For example, the first method few function calls are:

    main()
        prod.Product()
            prod.setName()
            prod.setPrice()
        prod.getName()
        ...
    
  3. Record your sequential listing in your exercise9.txt file.
  4. In addition, record answers to the following questions:

Q1: In productapp.cpp, what is the number of the line that instantiates the Product object?

9.3: More Class Features

Objectives

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

  • Code no-arg constructor calls correctly
  • Describe how C++ passes objects to functions
  • Describe how to prevent "local shadowing"

9.3.1: More About Declaring an Object

  • Constructors are called in the object declaration
  • We can add the following two constructors to out productapp.cpp program
  • Product defaultProduct;
    Product prod("Milk", 3.95);
    
  • Notice the difference in how the two objects are initialized
  • First statement calls the no-arg constructor
  • Second statement calls a different constructor
  • It is an error to call the no-arg constructor like this:
  • Product defaultProduct2(); // wrong
  • However, the compiler will not catch the error!
  • It is legal syntax to declare a function like defaultProduct2() with no parameters
  • Trying to use defaultProduct2() will cause an error
  • defaultProduct2.getName();  // causes error

9.3.2: Local Variables and Shadowing

  • Local variables with same name as a class variable hide the class variable
  • Any use of the variable's name will refer to the function variable
  • This is the source of many an elusive bug
  • Thus, you should not use the same name for a local variable and a class variable
  • Remember that a parameter is like a local variable but initialized in a special way
  • Thus you should use a different name for a parameter and a class variable

9.3.3: Destructors

  • Destructors are public member functions with the same name as the class, proceeded by a tilde '~'
  • For example, the destructor of Product would be:
  • ~Product();
  • Destructors are called automatically when an object is destroyed
  • Like constructors are used to set up objects, destructors are used to shut down objects
  • Destructors become more important when you start using pointers

9.3.5: Summary

  • You must use care when calling a default constructor
    • Do NOT use parenthesis
    Product defaultProduct2(); // wrong
  • Local variables with same name as a class variable hide the class variable
    • Known as "shadowing"
  • You should not use the same name for a local variable and a class variable

Exercise 9.3

  1. Label this exercise: Exercise 9.3
  2. Complete the following and record the answers to any questions in exercise9.txt.

Specifications

The following object has errors that prevent its correct operation:

#include <iostream>
using namespace std;

class MyRectangle {
public:
    MyRectangle(double length, double width);
    void showData();

private:
    double length;
    double width;

};

MyRectangle::MyRectangle(double length, double width) {
    length = length;
    width = width;
}

void MyRectangle::showData() {
    cout << "length: " << length
         << "\nwidth: " << width
         << endl;
}

// For testing
int main() {
    MyRectangle rec(3.0, 5.0);
    rec.showData();

    return 0;
}
  1. Correct the errors and submit your corrected code as part of this weeks exercises.
  2. Q1: What error messages are displayed by the compiler, if any, before you correct the errors?

    Q2: Which function contains the errors?

    Q3: What is the name of these type of errors?

Wrap Up

Home | WebCT | Announcements | Day Schedule | Eve Schedule
Course info | Help | FAQ's | HowTo's | Links

Last Updated: November 04 2004 @16:06:12