The most basic purpose of a class is to combine data types into a single unit. Classes are blueprints for creating objects of data in a program. A primitive data type defines a certain built-in type of data, int, float, char etc. For example, you can make a variable of type int if you want by writing something like this:
int myVariableName;
This will create a variable of type int. This variable can also be called an
object or instance of the type int.
We can then use this variable to store data.
But what if we wanted a data type which is
capable of storing two ints inside it, or, let's say, three ints and a char?
C++ has no data type like this, but we can create our own data type by combining
already existing data types.
This is called creating a user defined data type, or a class.
The data in the class are called the attributes of the class.
But this is not all. The class we create can also have built-in functions. These functions are called the behavior of the class. They are the possible behaviors the class can exhibit.
A class, therefore, is a grouping together of data and functionality. This means that where we once passed data to a function via the parameters, we now will not need to pass parameters. Instead, the function already has access to the data it needs. The data and the functions are grouped together in one class. All the parts of the class have the same scope. I.e, within the class all the variables are globals.
Here is the basic format of a class:
class anyName
{
// list of variables
type myVariableName; //attributes
function( parameter list )//behavior
{
// some code
}
};//end class
anyName is the name I have given to the class. The name is follow by a set of braces { }. Inside the braces the attributes are listed and the behavior is defined (i.e. the functions of the class are defined or prototyped). After the closing brace there must be a semi-colon. All classes should have this basic form though a class can lack attributes or behaviors.
Here is a simple example of an actual class which follows the above format:
class AClass
{
int a;
void someFunction( )
{
cout << "This is a simple printing behavior of the class AClass. The
attribute of this class is the integer " << a << endl;
}
};//end class
Notice that the function can directly access the attribute "a" in the class. I do not need to pass the function any parameters. This is what I meant when I wrote that classes combine data and functionality. Functions in the class are called member functions. Member functions have access to all the data in the class. That is how I was able to refer to "a" inside the function. For member function "someFunction" it is as though "a" is a global variable.
Member functions can have parameters also. These parameters work like parameters in ordinary functions. That is, I refer to them by name. What if I have a parameter which is also called "a", how do I distinguish between the variable "a" which is an attribute of the class and variable "a" which is a parameter? For this purpose there is the keyword this followed by the symbol ->. Normally, "a" would refer to the attribute "a" but it there is a parameter "a" then "a" refers to the parameter. To refer to the attribute "a" I would need to write "this->a".
For example, this class has a function to print out attribute "a" parameter "a" times:
class AClass
{
int a;
void anotherFunction(long a)
{
if (a < 0)
return;
while (a > 0)
{
cout << "This is attribute a : " " << this->a << endl;
a--;
}
}
};//end class
The keyword this is used to differentiate between an attribute and a parameter of the same name.
Once I have defined this class, I can use it in my main function in my program to create an instance. In the following code excerpt I will create one object of type AClass. I could create several object of the type class AClass but for now I will create just one. If I want to run the function in the class, I will call the function by the the object I have created. Here is an example of a possible excerpt from the main function that uses this class:
AClass myInstance(7800);
myInstance.someFunction();
I created the object myInstance and supplied its attribute the integer 7800. At this point I have an instance of the class AClass and it has data in it. Then, in order to run the function someFunction( ) I wrote the name of the instance I created followed by a dot (the dot operator). Only then did we write the name of the function we wanted to run.
The dot operator is used to refer to functions or data inside an individual instance of the class. The dot operator indicates that we wish to run the function someFunction( ) that is associated with (that is inside) the particular object myInstance. I can also use the dot operator to refer to the data in the instance. For example,if I want to print the attribute "a" of an instance I can write
AClass myInstance(7800);
cout << myInstance.a;
We have seen how a class is a grouping of data and functionality. I can create multiple instances of the same class and access the functions or data of each instance by using the dot operator.
There is no programming problem which requires the use of classes. In fact many computer languages do not have classes at all. But it was found that programming problems can be broken down into component parts and solved with less confusion if classes are used. In very small programs the benefits of classes are small and hard to perceive. But for programs that require large numbers of people to work on them and that contain millions of lines of code, classes make the job of organizing the program infinitely easier. Often when attempting to use classes in a program, the first thing to do with the programming problem is to try to see if there are any real world things which you can model. Then, make classes for them and give the classes useful behavior. This should help to organize your final program.
I have now explained the basics of creating a class and instantiating it (making an instance), but I have not explained how the instance gets created. How does the value I pass at the time I make the instance (7800 in the above example) get into the data of the instance? For this to happen there must be a constructor function which will be run when I create an instance of the class. This is called constructing the instance or object. Every class needs to have a constructor function if it is going to be constructed. Technically, the attempts to construct instances of AClass that I have made in the examples so far on this page will not work. I neglected to write a constructor function. Now I will show you what needs to be added to the class for it to work.
I will rewrite the class AClass and include a simple constructor function:
class AClass
{
int a;
someFunction( )
{
cout << "This is a simple printing behavior of the class AClass. The
attribute of this class is the integer " << a << endl;
}
AClass(int val)
{
a=val;
}
}//end class
I added a function called AClass. Notice that that is also the name of the class. All constructor functions have the same name as the class for which they are written. Also notice the function has no return type. All constructor functions have no return value. This function takes a parameter, val, which is then assigned to the data, int a, inside the class, AClass. If AClass had more than one piece of data I would need to pass several values, and to define my constructor function to take several values.
Normally, all attributes (the data of the class) are assigned initial values by the constructor function. There is no other way to initialize class data. This is the job of the constructor. If you would be able to initialize data in the class definition, then all instances of the class would have the same values. This is not desirable since you will usually want the attributes to be different. Therefore, C++ specifies that you can only initialize attributes of the class via the constructor function. You can of course change the values of the attributes later by writing a function to do this and running that function later with new values. This function might be called setAttributes( ). This is the general normal behavior for a constructor function to have. But theoretically, I can write any code I like in the constructor function. We will learn later that static data is initialized without using the constructor function.
The constructor function is run when we create or construct an object of the class. So, for example, a line like this will run the constructor function:
AClass myVariableName(932);
In this example, I am passing the value 932 to the parameter val. The constructor function then assigns the data attribute "a" the value it stores, 932. This is the function of the constructor function, to assign values to the data in the instance.
There exists a short hand way of writing a constructor function. It is called using a initialization list . Here is an example of that:
class AClass
{
int a;
someFunction( )
{
cout << "This is a simple printing behavior of the class AClass. The
attribute of this class is the integer " << a << endl;
}
AClass(int val):a(val)
{
}
}//end class
What this does is to initialize the attribute "a" with the value val. In this second method we are in essence having our constructor call the constructor function of int and passing it the value. Then our constructor function does not need any code in its body.
These two methods are parallel to the two methods of initializing any primitive data type. We can initialize float f in either of the two following ways:
float f;
f=3.14159;
Or
float f=3.14159;
The second way is more concise just like using the initialization list above.
I have shown you an example of creating an instance of a class by writing the class name followed by the instance name and a set of parenthesis containing any variables to be passed to the constructor function. There exists a second way to create an instance of a class. This method involves creating a pointer and running the new command. Here is an example:
AClass * ptr;/*declare a pointer of type AClass */
ptr = new AClass(7800); /*run AClass's constructor function*/
This will also run the constructor function and pass the value 7800 to the instance being constructed. One important question to ask is in which of the above two lines of code is the constructor function being run? It is being run in the second line, when I pass the value to the command "new". This implies that creating a class pointer does not cause the constructor to be run. This is entirely correct. Creating a pointer is not the same as creating an instance. A pointer is not instantiated.
If we create an instance in this way I no longer use the dot operator to call the function. Instead I use the the arrow operator like this:
AClass * ptr;
ptr = new AClass(7800);
ptr->someFunction();
In general, whenever I have a pointer to an object I use the arrow operator instead of the dot operator.
The dot and arrow operators not only give me access to the function in the object, they also give me access to the data (attributes) of the object. Therefore, if I want to initialize the value of the int a in a particular object to 7800 and then to change the value to 455, I can write:
AClass * ptr;
ptr = new AClass(7800);
ptr->a=455;
Every class has a default constructor. This constructor takes no parameter and simply creates an object where all the attributes are undefined. If you have no attributes in your class then this behavior will suffice. But assuming you do have some attributes you will need to overload the default constructor and supply your attributes with default values, like zero.
Here is what the default constructor of class AClass could look like:
AClass()
{
a = 0;
}
This will create an instance whose "a" is initialized at the value zero.
If I want to create an instance and run the default constructor, I would simple write
Even though the tell tale sign of a function call, the parenthesis, are absent
I am still running the constructor function in this example.
The following, although reasonable, would be incorrect
AClass moo;
AClass moo();
When running the default constructor without the use of the new command, do not use parenthesis.
In all of the above examples I have created an instance of type AClass by running the constructor function. The constructor function does the job of creating the object whether it is run explicitly (using the new command) or implicitly. It is run only once per instance, at instantiation. As we have said, the purpose of the constructor function is to actually create the object and to assign values to the attributes of the class. Moreover, it is, in fact, illegal to assign values in the class definition. You must use the constructor function for this.
So far we have seen a constructor that takes as parameters initial values for the class attributes. We have seen the default constructor that takes no parameters. The third essential constructor takes a reference to another instance of the same class. It is called the copy constructor. Its purpose is to be used when a copy of an existing instance needs to be created. This can happen when, for example, I pass an instance as a parameter to a function. If I pass it by value and not by reference, then the function makes a copy of it. In such a case the copy constructor will be called. The copy constructor can also be called explicitly, by passing an instance to the new command.
There is by default a copy constructor made by the compiler, but it only does a shallow copy, that is it copies all the data, but if a new command was used in the constructor the default copy constructor will not call a new command and the copy made will have a pointer to the same data as the original instance. In fact, all pointer in the copy will point to what ever the original instance is pointing to. The copy will not have its own data. It iwll only have copies of the pointers. This is bad. Therefore as a rule, whenever you use new in your constructor you need to write a copy constructor. This constructor should perform what is called a deep copy. I.e., it should allocate memory of the same size as the original instance and then it should go through an copy all the data being pointed to by pointers in the original instance back to the copy instance.
Even though you can usually rely on the default copy constructor, it is good practice to always write your own copy constructor. That way if you need a deep copy you won't forget to write it.
Here is an example of a copy constructor for the class AClass:
AClass(AClass & other)
{
a=other.a;
}
As you can see, the parameter of this constructor function is another instance of AClass. The function then simply copies the data values of the other instance into the data of the instance being created. No deep copy needs to be preformed.
Notice also that I have used the reference parameter when writing the function prototype of the copy constructor. This is to prevent an infinite loop. If I were to pass by value the parameter, then in order for my copy constructor to run it would need to call a copy constructor (since the function would need to make a copy of the parameter passed). This would cause an infinite loop of function calls. By passing a reference, no copy needs to be created. Alternatively, I could just as well have used a pointer and not a reference.
A destructor function is a function designed to delete memory created for an instance when that instance is destroyed. The destructor function is called whenever I run a delete command on an instance or when ever an instance goes out of scope, as when a function that created an instance returns.
A destructor function can be written though one will be created by default. When must I write a destructor function? Any time I call new in my constructor I must write my own destructor to delete what ever was created with new. All other data will be deleted anyway once it leaves scope. Therefore , the default constructor for AClass is sufficient and I don't need to write one. But I can write one anyway. It is similar to the constructor but is preceded by a tilde.
It is written like this
~AClass() {}
Notice that in this example the destructor function is blank since nothing in the class was created with new. If I had created something with new in my class, I would need to use the delete command in my destructor to deallocate all the memory I created.
In my examples of classes I have defined my functions within the class definition. I could also have simply written the functional prototype in the class definition and then written the function definition after the class. To do this we need to use the scope resolution operator ::. Here is an example of defining a class in this way:
class AClass
{
int a;
void someFunction( );
AClass(int );
};//end class
void AClass::someFunction( );
{
cout << "This is a simple printing behavior of the class AClass. The
attribute of this class is the integer " << a << endl;
}
AClass::AClass(int val)
{
a=val;
}
The class definition simply contains the attributes and the function prototypes (for both the function someFunction and for the constructor function). Then when I actually define the functions I must tell the compiler that I am defining the functions in the class AClass. To do this, I use the scope resolution operator. Theoretically there could be two functions named someFunction, each in separate classes. I need to use the scope resolution operator to specify where the function I am defining is located.
Functions can be defined in the class, or outside it using the scope resolution operator. The second method is preferable for two reasons. First, it is easier to read the class if it is not cluttered with function definitions. Second, there could be a problem of function precedence, i.e. two functions, each using the other requires each function to know about the other before either one can be written. This requires functional prototyping.
The attributes and methods (data and functions) of a class can either be accessible via the object or blocked. If a function, for example, is publicly accessible, then I can run it as I have in the above examples. In all of my example I have assumed that the attributes and methods are all publicly accessible. If, however, a function is private, then only other functions in the class can run it, but it cannot be run by the instance itself. The mechanism for making attributes and methods public is to write the word public followed by a colon before those elements you want to be public. By placing public: as the first line of the class definition, you will make the entire class public. To make the attributes and methods private, write the word private followed by a colon before those elements you want to be private.
By default all the data and functions are private and therefore not accessible via the instance. Therefore, all the preceding examples I have shown will not work until you add the word public: to the class definition. You can make some functions or data private and others public by arranging the words public and private appropriately. The ability to limit access to class attributes and methods achieves the Object Oriented Programming ideal of encapsulation.
For now, just make all the data and functions of your class public. Here is how the class should look:
class AClass
{
public:
int a;
void someFunction( );
AClass(int );
};//end class
void AClass::someFunction( );
{
cout << "This is a simple printing behavior of the class AClass. The
attribute of this class is the integer " << a << endl;
}
AClass::AClass(int val)
{
a=val;
}
The people who theorize about good programming have come up with a concept called Object Oriented design. There are many aspects to this theory. The theory states that we should model all our class after real world things. Since things in the real world have descriptions ( attributes ) and things they do ( behavior, functions, method). So classes have attributes and behavior. The attributes of a class are the data the class will store and use. The behavior is what the class is capable of doing with its data. Another program or class can then ask the class to perform one of its behaviors by running one of the methods in the class.
What is wrong with having a private constructor function? When might it not be wrong and in fact be useful?
Here is an example demonstrating the use of a static method in a very simple class.
Exercise: Think of any real world thing and model it with a class.
© Nachum Danzig December 2003 - January 2010