CSC 226

 programming

Introduction to Programming with C++


Intro to C++ Classes


Objectives

Important Definitions


Classes Introduction

We have learned that Bjarne Stroustrup developed the C++ language to improve upon C, but so far we have not yet learned much about the differences between these two languages. A simple answer to this implicit question is that C is basically a subset of C++; in other words, most C programs will run when compiled with a C++ compiler, but the reverse is not true. Unlike C, C++ is an object-oriented language, where the data in the program is conceptually grouped together with the functions that work on the data. In this assignment, we will begin to experiment with object-oriented programming.

We learrned last time that you can create your own compound types in C++, using two mechanisms: structures and classes.  Classes are the biggest difference between C and C++ because it is with a class that one can create an object, the basis of  "object-oriented programming."  In fact, one of the earliest names for C++ was C with Classes.

One of the most important programming structures in C++ is called a class. In most object-oriented programming languages such as C++, a class is a user-defined type that includes both

  1. a set of member variables associated with it,
  2. as well as a set of member functions.
Each class definition corresponds to some concept in the real world, and the functions that operate on an object of that class correspond to the ways real-world objects interact. In object oriented programming, programs are made up of a collection of classes and member function definitions, where the functions operate on specific data of that class.

Note that the C structure or "struct" that we saw in our previous assignment only includes a set of variables but does not have any functions contained in it. However, the idea of including member functions make intuitive sense. For example, a "time" structure should correspond to the way people record the time of day, and the operations we defined correspond to the sorts of things people do with recorded times. Similarly, a "point" structure could correspond to the mathematical concept of a point, and operations such as calculating the distance between the two are important and should be closely associated with it.

Member functions of a class differ from the other C++ functions in only two main ways:

  1. When we call a member function, we will invoke it on a specific class object. People sometimes describe this process as "performing an operation on an object," or "sending a message to an object."
  2. The member function is declared inside the class definition to make the relationship between the class and the function explicit.
Unfortunately, the syntax of the C++ class differs somewhat from the syntax of a structure, but we will be able to make a simple translation. The process to take some functions and transform them into member functions is mechanical, and one simply follows a sequence of steps.

Structures versus Classes

We will see how to convert a structure into a class.

Consider the following code that would not only store time but also display it:

  1. a structure that could handle times
    // Define a structure that records the minutes, hours and seconds for 
    // a time period

    struct Time {
    int hour, minute;
    double second;
    };
  2. a function that could print times:
    // A function that takes as input a Time structure and outputs the time 
    // stored in it.

    void printTime(Time mytime) {
    cout << mytime.hour << ":" << mytime.minute << ":" << mytime.second << endl;
    }
To display the time in currentTime which is of type Time, we would have to pass currentTime as an argument to the printTime(...) function:
Time currentTime = { 9, 14, 30.0 };	// Create and initialize the time data 
printTime( currentTime ); // Output the time data
We could convert this structure into a class named Time that includes a member function called print(...) that does the exact same thing.
  1. The first step is to change the name of the function from printTime(...) (that is currently independent of any classes) to Time::print(). The scope resolution operator, ::, is between the name of the class from the name of the function, linking the print() function to the Time class object.
  2. The second step is to realize that we no longer need the myTime parameter because print() is a member function of the class that contains the data (the member variables).

    Conceptually, you are switching from asking a function to print something, which you give as input, to asking an object to "print itself". This function will no longer have a parameter named myTime but will refer to the object as current object. The kind of member variable access seen in the print() member function is called implicit because the name of the class object does not appear in the member function explicitly. Features like this are one reason member functions are often more concise than other functions.

  3. Finally, note here that this function is not supposed to change anything, to we can indicate that fact by adding the keyword "const" after the paramters list (which in this case is empty).
We must also declare the data as member variables accessible to all functions of that class, resulting in a possible definition as follows:
class Time {
// The following two lines declare the variables to hold the values. Note that the variables are used
// directly in the print() member function

int hour, minute;
double second;

// Now we declare and define the member function to print the time
void Time::print() const {
cout << hour << ":" << minute << ":" << second << endl;
}
}; // Recall that the class definition ends with a semicolon.
Like regular functions, one can include a member function declaration in the class definition and then write the function definition later:
class Time {
int hour, minute;
double second;

// Now we just declare the member function to print the time here and define it later
void Time::print() const;
}

// some extra code

// Function definition. Notice that the variables hour, minute and second are
// packaged together with the print() function, so when using the "Time::" resolution,
// the compiler knows where they are defined

void Time::print() const {
cout << hour << ":" << minute << ":" << second << endl;
}
;
Declaring a variable to refer to an object of type Time is similar to declaring a struct. Once you have that object, you can invoke the print() function on that object:
Time currentTimeObject; // currentTimeObject refers to an object of time Time 

// some extra code to set the values of the member variables.

// Finally, have the object print itself.

currentTimeObject.print();

Constructors

There are two ways to give values to the member variables in structures or classes. One way we have already encountered is to initialize when you declare the the structure:
Time testTime = {9, 14, 30.0};  // initial values of 9 hours, 14 minutes, 30.0 seconds
This by itself is very limiting because (1) we may not know the values when we declare an object or structure, (2) we may know the values in a format that will not work (such as when we only how many seconds and want the program to automatically figure out the hours, minutes and seconds), and (3) these values may change.

If Time is a structure, we can give use a makeTime(...) function to take as input the number of seconds and create a Time structure with the appropriate values:

// This function takes as input a positive number and splits 
// it into hours, minutes and seconds

Time makeTime(double secs) {
Time mytime;
mytime.hour = int(secs / 3600.0); // Note the casting into integer
secs -= mytime.hour * 3600.0;
mytime.minute = int(secs / 60.0);
secs -= mytime.minute * 60.0;
mytime.second = secs;
return mytime; // Note this is what the function should return
}

In the case of classes, the corresponding function is so common that there is a special syntax for these constructors, which looks like:

Time::Time(double secs) {
hour = int(secs / 3600.0);
secs -= hour * 3600.0;
minute = int(secs / 60.0);
secs -= minute * 60.0;
second = secs;
}
Notice that:
  1. The constructor must have the SAME NAME AS THE CLASS, and no return type because the instance variables of the class are modified in the function. When we assign values to hour, minute and second, the compiler implicitly knows we are referring to the instance variables of the object because the scope resolution operator indicates this connection.

    Alternatively, we can refer to the new object---the one we are constructing---inside the constructor function with the keyword this:

    	Time::Time(double secs) {
    this.hour = int(secs / 3600.0);
    secs -= this.hour * 3600.0;
    this.minute = int(secs / 60.0);
    secs -= this.minute * 60.0;
    this.second = secs;
    }
    The changes are indicated in red.
  2. There is no need to declare a local variable for a new time object as in the case with the function that worked with structures; this function is referring to the member variables automatically.

Initialize or Construct?

We initialize Time structures using curly-braces:
Time bedTime = { 3, 35, 0.0 };
Using constructors for a class, we have a different way to declare and initialize objects, which is a syntax that is a cross between a variable declaration and a function call:
Time bedTime(seconds);
This statement declares that the variable bedtime is of type Time and calls the constructor, passing the value of seconds as an argument. The system allocates space for the new object and the constructor initializes its instance variables.

We can certainly overload the constructor so that declaring and initializing an object of type Time looks similar to the way we did with the struct variable. The function can be declared in the class definition and defined elsewhere as follows:

class Time {

// code for other parts of the Time class.

Time( int h, int m, double s ); // The function declaration
};

// other code for other parts of the program.

Time::Time( int h, int m, double s ) {
hour = h;
minute = m;
second = s;
}

To invoke this constructor, we use the same syntax as before, except that the arguments have to be two integers and a double:

        Time currentTime(9, 14, 30.0);



Classes and Objects

To reiterate class is a definition of an object. A class resembles a struct with the formal difference being the following: only that all struct members are public by default while all classes members are private.

In practice, however, most people use a struct when they are just creating a compound type, while they use a class when they want that compund type to have associated member functions as well as member variables.

The simplest definition of a class is:

class classname{
// members
}


Here is an example of a small class which we will discuss in the classroom:

/* This is a small example of a class */
#include <iostream>
#include <cstdio>

using namespace std;

class Ebook{

public:
    Ebook(int numpages) ; // Constructor
    void setBookmark( int currentplace) ;
    int getCurrentPage( );

private:
    int pageCount;
    int currentPage;
};

int main() {
    cout << "Let's create a new e-book named mybook with 128 pages." <<endl;
    Ebook mybook(128);
    cout << "Let's create a new e-book named newbook with 100 pages." <<endl;
    Ebook newbook(100);
    cout << "Next let's set a bookmark in mybook at 56 pages." <<endl;
    mybook.setBookmark(56);
    cout << "Next let's look at where the bookmarks are." <<endl;
    cout << "Current Page in mybook: " << mybook.getCurrentPage() << endl;
    cout << "Current Page in newbook: " << newbook.getCurrentPage() << endl;
    return 0;
}

Ebook::Ebook(int numpages) {
    pageCount = numpages; //These assign the parameters to the member variables.
    currentPage = 1;
}

void Ebook::setBookmark(int currentplace) {
    currentPage = currentplace; }

int Ebook::getCurrentPage( ) {
    return currentPage;
}



Assignment Specifics

This assignment must be done individually, but you are encouraged to converse.

  1. Define a new class for a type called CounterType.  An object of this type is used to count things, so it records a count that is a non-negative whole number.  This means the class needs a private member varaible called counter.
  2. Include a default constructor which sets the counter to 0.
  3. Include a second constructor with one arguement which sets the counter to the value specified in the arguement.
  4. Include a public member function called increment which increases the count by 1.
  5. Include a public member funciton called decrement which decreases the count by 1, but does not allow the count to become negative.
  6. Include a public "accessor" member function called getcount which returns the curent count.
  7. Include a public member function called writecount which outputs the count to the screen.
  8. Create a driver main which tests all of the member functions.

Be sure to also do the following:

When you are finished writing and testing your assignment, drop your source code, YourUserNameA13.cpp into the A13 dropbox on Moodle.

Copyright © 2012 | http://faculty.berea.edu/pearcej/CSC226/