Section 14d

More on Records, Declaring a C++ Class


 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

One Step Forward (++)

Quick Introduction

You have learned that you can create an Abstract Data Type (ADT) from a C programming language struct. This was a great beginning that started a long time ago. However, a newer language was created to take a next step forward from C, and that language is C++. Unlike C, C++ was designed as an Object Oriented Programming (OOP) language from its inception. It is a powerful language, and it has remained in play for a little over forty years now. C++ takes data abstraction to another level in that data can not only be represented by an abstraction, there are tools within the abstraction that can manipulate the data.

The creation of a C++ class will be presented in two parts in this reference. In this section, the declaration of a given ADT will be provided, which will end up being the header (i.e., .h) file. The next succeeding topic will provide the implementation of the member functions which are commonly called methods when they are in classes. This will be a .cpp file.

Declaring the Class

The essence of the header file is to show programmers what the class is and what it does. It does the same for the programs in which it is used. The header file must be included in any files that use the class, and the implementation file must be compiled and linked with the program. This operation will be demonstrated at the end of the next section.

Starting at the Top (of the File)

The first part of the file will hold the precompiler directives. This term describes itself. Before the compiler gets started, these directives tell the compiler to check if it has already compiled the header file once. It is likely that the header file will be included in more than one other file but if it gets compiled more than once, there will be problems. The instructions say, "If you have not seen this label previously (#ifndef), go ahead and define the label (#define) so it won't get compiled again. If you have seen the label previously, just skip to the end of this file where there is an (#endif). The label itself can be anything but the convention is to make it all capital letters with underscores between words, as shown here.

// Precompiler directives /////////////////////////////////////////////////////

#ifndef STUDENT_CLASS_H

#define STUDENT_CLASS_H

Class Declaration Structure

The structure of the class is provided next. While there is no specific standard for how this looks, you are wise to follow some standard. The one provided below works very well.

class StudentClass
{
public:

// class constants

// constructors

// default

// initialization

// copy

// destructor

// overloaded operators

// None

// accessors

// mutators/modifiers

private:

// methods

// data
};

Class declaration. The class is declared with the keyword class and an opening curly brace. Then at the end of the close curly brace, there is a semicolon.

Scope areas. An important note to make at this point is that the class itself is just the plan for what an object will be when it is created, or instantiated. This is the same as the data type int. int is not used as a variable in your program. You declare some variable like age as an int and it knows how to be an integer while the program runs. The same goes for classes. If you have a class named StudentClass, you will not use StudentClass as a variable. From the class StudentClass, you will create an object (i.e., the OOP version of a variable) such as myStudent that knows how to do and be the things that a StudentClass does and is.

However, in most classes there are data and operations to which the user/programmer does not need access. Therefore, there are some public functions and data that are avaiable and some private functions and data that are not available. The keywords public and private are used to identify these groups, and these are followed by a colon, as shown above.

Class constants. Just like your previous programs, there may be a need for constants in the class which can also be used by the user/programmer during class operations. And as before, the class constants are at the top and in the public scope area, as shown above.

Class constructors. Constructors take a little more explanation, but not a lot. The data type int was used as an example above and will be used here as well. When a variable such as age is declared as an int, it has a scope local to whatever function it is in. In other words, the variable age will become usable at the point of being declared and then it will become unusable when the function ceases. This is how abstract data types will work as well. In fact, the code looks almost the same when an object is instantiated . . . with a couple of differences. Consider the following.

// initialize function/variables
int age;
StudentClass myStudent;

In the above code segment, the variable age is declared as an integer and the object myStudent is instantiated as an object of StudentClass. This is not complicated and it is intuitive. Instantiating an object in it simplest form is said to be using a default constructor (back to this in a moment). However, as you know, it is possible to declare an integer and assign it in the same line. The same is true about objects. But remember that objects commonly have more than one data component, so the initialization looks a little different. Consider the following.

// initialize function/variables
int age = 22;
StudentClass myStudent( name, studentID, gender );

Actually, as it turns out, you can declare primitive data such as integers the same way you declare objects. The following accomplishes the same thing as the code above.

// initialize function/variables
int age( 22 );
StudentClass myStudent( name, studentID, gender );

As you might guess, the above object instantiation is called an initialization constructor. Again, this will be discussed soon. The last of the three constructor operations is also familiar to you. Consider the following code.

// initialize function/variables
int age = otherAge;
StudentClass myStudent( otherStudent );

Just like an integer can be declared and assigned data from some other integer, an object can be instantiated with data from another object of the same data type (i.e., the same class). And once again, it should be intuitive that this kind of constructor is called a copy constructor.

So, if all these operations are the same as declaring and defining primitive values, why are there constructors? The answer is pretty straight forward. When you declared an integer variable, the compiler and the operating system simply made space for an integer and gave it a label. However, as you might expect, creating an object is more complicated. For example the examples above show a StudentClass object that holds three data values instead of just one. The data values are of different types, being a C-Style string, an integer, and a character. Each of the three needs to be initialized in the new object in whatever way is necessary. The character and integer are easily assigned but as you recall the C-Style string takes some extra work.

Constructors do this work. They make sure each data value is assigned to its appropriate place inside the object, and in some cases, they also initialize other parts of the object in preparation for the object's use. So, it is still doing the same thing as declaring a variable, it's just that the variable is much more complicated and has to take care of all this setup. And as you might also recognize, since every class will be different, every constructor for each class will be different.

So to recap, the default constructor simply sets up the object to do its job but it doesn't start with any data provided by the user/programmer. The initialization constructor sets up the object and loads the internal data with that provided by the user/programmer. And finally, the copy constructor copies over all the internal data from a previously defined object to the brand new object that is currently being instantiated (i.e., constructed). Let this soak for a bit.

Class destructors. As previously mentioned, each object is only in scope as long as it is between the curly braces within which it was declared. In C, if you declare any dynamic memory, you are responsible to free that memory before the program ends. In C++, it is also possible to have created some dynamic memory while the object was in scope. In the above StudentClass example, the name is created using dynamic memory.

The destructor's job is to free all memory that was allocated during the object's operations so the user/programmer does not have to sweat cleaning up after the object. Good old OOP; it even cleans up after itself. In the case of the integer variable age, its memory is given back to the operating system when it goes out of scope, and in the case of objects, the class developer is responsible for making sure this happens with all the data used in the class. The destructor is how to do that. Unlike the constructor(s) however, the destructor is not called by the user/programmer. The compiler and operating system take care of calling the destructor so once again, OOP takes care of itself.

Overloaded operators. It is actually possible in C++ and some other languages to overload or reuse some operators. As you will soon observe, the left logical shift ("<<") and right logical shift operators (">>") are overloaded and used with stream I/O. String operations also use the plus and equals sign to concatenate and assign string data. This overloading operation is implemented inside the appropriate class as needed, and works rather well.

However, this is more of a bonus expansion on the language as opposed to a fundamental programming concept, so this will not be presented here. That said, some discussion of this will be conducted in a later topic. And with that said, even though it will not be explained here, it is still important to keep a place for it in the structure of a class so the comments for it are provided.

Accessors (aka "getters"). In most cases the data in a given class will be held as private in scope, meaning users/programmers are not permitted to directly manipulate the class data. However, it may be appropriate for the users/programmers to have access to the data, so it is provided in a controlled condition by using a method that will provide the data to the users/programmers without allowing their ability to change it. The accessors in this class allow the users/programmers to get the name, student ID, and gender data with these methods. More will be provided on this when you get to the next topic.

Mutators (aka "modifiers" or "setters"). Same story. Class data should be managed by the class, not the users/programmers. Thus, if some data is to be changed, it is changed under the controlled conditions of a class method that conducts the change in an appropriate way. Again in this class, the mutators in this class allow the users/programmers to set the name, student ID, and gender data with these methods. And again, more will be provided on this when you get to the next topic.

Private methods. In some classes, there are methods that do some background work that is not necessary or appropriate for the users/programmers to do. This particular class has a method that helps manage the name string but it would not be appropriate for a user/programmer to use it as part of a StudentClass operation. Thus, it is private. Note that there may not be private methods in a given class but again, it is a good idea to place the comments in that location so the structure remains the same.

Private data. Once again, as mentioned above, it is most common for the data in a class to be inaccessible to users/programmers so they cannot inadvertently modify or manipulate it. Therefore, the data will almost always be near the bottom of the class declaration with private scope.

Wrapping Up Class Declaration

This appears to be complicated . . . and it is. However, just like any other complicated thing, it is all made of simpler parts. You should be able to identify the parts of a class using the above information well enough to know what is inside the box. The actual code parts of the class have been left out to help make the components more clear. The video will show the construction of the entire header file from top to bottom. Make sure you follow it all the way through so you can be ready to tackle the implementation part of the class development which comes in the next topic.