Make your own free website on Tripod.com
C++ Ripped Apart :: Section 9
Author: Mike Ware
Website: [warebiz] :: "The Programmer's Domain" - http://warebiz.tripod.com
Email: warebiz@yahoo.com
Copyright © 2003 Michael Shawn Ware, All Rights Reserved.


You are here --> [Section 9 :: OPP => Classes and Related Topics]

"Jump To Articles"
    --> Classes
    --> Function Overloading
    --> Operator Overloading
    --> Static Variables


Classes

Before elaborating on classes, I want to introduce the concepts of object oriented programming (OOP: pronounced "hoop", but with a silent 'h') because when you create classes, you are actually using OOP. What is an object and all this talk about object oriented programming? An object is more or less just some thing that knows how to perform operations (perhaps to itself). In C++ programming, an object can be called an instance of a class (or object type). An instance is simply a variable of the object type. Since everything is centered around an object, we call this type of programming object oriented. The object type (or class) provides the instance of the class with characteristics, such as data members and function members it can use. Some data may be kept entirely private, which means it can only be accessed by the object itself, and some other data may be kept public, which means that "outside" members such as non-member functions have access to it. OOP makes C++, as well as other high level languages very powerful, and it is why C++ and other languages are capable of building high-quality software.

A class is a combination of data and functions joined together to form an object type. The object type defines the operations that are necessary to manipulate the object. By creating a class, we can take advantage of data hiding, which is very important in programming. Data hiding is physically located data in the class (inside the class object type) that is hidden from the "outside", which is everything in the program not defined in the class type. The data can only be accessed or changed by functions that are apart of the class (accessor methods). The declaration of a class is similar to that of a structure [see Structures in Section 8], except that usually there will be member functions along with data members. There will also be some type of syntax for distinguishing members that are available for public use and ones that will be kept private.

The best way to learn classes is by studying a simple class object type. The following is a very simple class object type:


class myClass
{
     private :
                  int data1;
     public :
                  void setData( int d )
                      {    data1 = d;    }

                  void displayData( )
                      {    cout << endl << "Data is " << data1;    }
};



NOTE: Some programmers claim it is not good programming practice to include function definitions in a class object type. In the above class, I have included definitions for setData( ) and displayData( ) simply because they are small functions and really only have one purpose.

In the above myClass class specification, the only data member is declared as private, which means that nothing outside of the class can directly modify or use its value. The only two member functions are declared as public, which means that "outside" users can call these functions. The public functions provide access to the private data member (accessor methods), and it is the only way that anything "outside" the class, such as an instance declared of the object type, can access the data member.

NOTE: It is not a requirement that all public elements be member functions and all private elements be data members, or vice versa. You may very well place data members in the public domain, and you may also place member functions in the private domain. Also note the required semi-colon following the closing bracket of the class specification.

Since the idea behind using classes is for programming centered around an object, when you go to call member functions which are defined in the class object type, you must first declare an instance of the class object type. For example, it would make no sense at all to immediately try to call     setData( )     in a program. How would the compiler know where to setData( ) or what to setData( ) for? Obviously, an instance of the class needs to be instantiated. Then use that instance to call the member function to access the private data member. The following code segment serves this purpose using our previous class:


    myClass myObject;
    .
    .
    .
    myObject.setData(121); // uses the dot operator "." to access the member function

The above code assigns 121 to myObject.data1. There are two ways to make the class object available for use in a program:

    1 - Declare the class object type before the main( ) function or earlier in the main( ) function [not recommended inside the main( ) ].
    2 - Put the class object type declaration in a header file and #include the header file [see Creating User-Defined Header Files (Section 8)].

Some object oriented languages refer to calls to member functions as messages. For example, consider:

    myObject.displayData( ) --> can be thought of as "sending" a displayData message to myObject.

Constructors
In the above example, there is only one way to assign a value into data1, which was a private data member. We had to access the setData( ) public member function and give data1 a value accordingly. Constructors are used to initialize the object when it is created (upon instantiation) without needing a separate call to another member function. A constructor can be thought of as an "automatic" initialization of an object. In C++, a constructor is given the same name as the class name and has no return value or even a return type. If it does have an argument list, it will only contain values to initialize the class attributes (data members).

If we wanted to add a constructor to our myClass example, we could add the following code to the class object declaration:


class myClass
{
     private :
                  int data1;
     public :
                  // constructor with no arguments
                  myClass ( )
                      { data1 = 0; }

                  // constructor with one argument
                  myClass ( int dataIn )
                      { data1 = dataIn; }

                  void setData( int d )
                      {    data1 = d;    }

                  void displayData( )
                      {    cout << endl << "Data is " << data1;    }
};


Now, when we create an instance of our class, if we do not pass an argument during declaration, the constructor with no arguments will be used to assign a value of 0 to the private data member of the object. If we do provide an argument, the constructor with one argument will be used to assign the private data member of the object the value of the argument. For example, consider the following object declarations:


myClass obj1;     // uses the constructor with no arguments
myClass obj2( 650 );     // uses the constructor with one argument

obj1.data1 will now hold a value of 0
obj2.data1 will now hold a value of 650

Notice in our updated class specification there are two constructors. You can follow the rule in C++ when using constructors that you can have the same names for functions as long as they have different parameter lists or signatures [see below: Function Overloading]. Although I failed to mention it earlier, when a function definition is placed within a class specification, it is said to be defined inline. An inline function definition is really just a request to the compiler asking for the code to be directly linked with any instances of the class. Since it is just a request, it is not considered good programming practice. When a member function is not inline, the actual definition must be placed elsewhere, either after the main( ) function with all other definitions or in a separate auxiliary source file (and used as a project file).

Default Parameters
When variables and expressions are passed to functions as arguments, they are associated with a corresponding parameter in the actual function heading. The parameter must be of the same type as the argument, but it may given a different name and can also be given the opportunity to have a default value. Just like regular functions, constructors of a class may have default parameters. For example, consider the following modifications to our previous myClass class:


class myClass
{
     private :
                  int data1;
                  int data2;
                  int data3;

     public :
                  // constructor with 3 default parameters
                  myClass ( int d1 = 0, int d2 = 0, int d3 = 0 )
                      { data1 = d1;
                        data2 = d2;
                        data3 = d3; }

                  void setData1( int d )
                      {    data1 = d;    }

                  void displayData1( )
                      {    cout << endl << "Data is " << data1;    }
};



If you diligently look at the class specification, you will notice that it only has one constructor, 2 new private data members have been added, and the arguments in the constructor are being used as default parameters. A default parameter is only used if the argument's value is not being passed to the function. This is beneficial because it enables you to reduce the needed number of constructors. Previously, we had two constructors, now we have one. Our "new" constructor can handle the following situations during the declaration of instances:


myClass obj1, obj2(35), obj3(12, 78), obj4(11, 43, 98);

Overview of Declaration
All private data members in obj1 will have a value of 0.
obj2.data1 would hold 35, but all other data members will have a value of 0.
obj3.data1 would hold 12 and obj3.data2 would hold 78, but obj3.data3 would hold 0.
All private data members is obj4 will been assigned the value of the arguments being passed to the constructor.

We can now handle four situations by using one constructor. Without default parameters, we would have had to create four constructors.

Another convenient way of sending data into a constructor is by using a base/member initialization list. The base/member initialization list is said by some programmers to be slightly faster than other initializations. The general form is as follows:


class className
{
     private :
                  type1 data1;
                  type2 data2;
                  type3 data3;
     public :

                  // constructor using base/member initialization list
                  className (type1 param1, type2 param2, type3 param3) :
                         data1(param1), data2(param2), data3(param3)
                    {  }
};


For another example, consider the following modifications made to our previous myClass class:


class myClass
{
     private :
                  int data1;
                  int data2;
                  int data3;

     public :
                  // base/member initialization list
                  myClass ( int d1 = 0, int d2 = 0, int d3 = 0 ) :
                         data1( d1 ), data2( d2 ), data3( d3 )
                   {  }

                  void setData1( int d )
                      {    data1 = d;    }

                  void displayData1( )
                      {    cout << endl << "Data is " << data1;    }
};


Default Copy Constructor
The default copy constructor provides a way to initialize an object to have the same values as another existing object that is of the same class. You don't even have to define or create the means to handle this situation because the default copy constructor is a "built-in" member function. All classes have their own default copy constructor. The default copy constructor has one argument, which is an instance of the class to be copied to the calling object. There are two ways to use the default copy constructor:

One way is to simply send the object to be copied as an argument when declaring the instance, such as:


    myClass object1(object2);

Or, you can simply use a form of the assignment statement, such as:


    myClass object1 = object2;

Both of these two methods will perform a member by member copy of values from object2 into object1.

Defining Class Member Functions
In order for the compiler to associate a function as a member of a class, you have to specify that it is a member of the class. This is done by using the class name along with the scope resolution operator ( :: ) in the function heading. Where have you seen the scope resolution operator? You use it when you are setting formatting flags to control the form of output [see Output Manipulators (section 5)]. The scope resolution operator basically tells the compiler that the code following it is to be associated with something. In our case, it will let the compiler know that the function is to be associated with the specified class. For example, suppose we placed a new function called     determineData( )      in our myClass object type. It should now resemble the following:


class myClass
{
     private :
                  int data1;
                  int data2;
                  int data3;

     public :
                  myClass ( int d1 = 0, int d2 = 0, int d3 = 0 ) :
                         data1( d1 ), data2( d2 ), data3( d3 )
                   {  }

                  void setData1( int d )
                      {    data1 = d;    }

                  void displayData1( )
                      {    cout << endl << "Data is " << data1;    }

                  void determineData1( int d1 );
};


Notice that     determineData1( )      is the only member function in myClass which is not inline. We now need to define it. We could use the following code using the scope resolution operator and class name to associate the function with the myClass specification:


void myClass :: determineData1( int d1 )
{
     if (d1 < 0)
            cout << "Data1 is of negative value." << endl;
     else if (d1 == 0)
            cout << "Data1 is of neutral value." << endl;
     else
            cout << "Data1 is of positive value." << endl;
}// end myClass :: determineData1( )


Notice the name of the class and the scope resolution operator preceding the name of the member function and parameter list. This is how the compiler knows to associate     determineData1( )      as being a member function of myClass.

Returning Objects From Functions
To see how functions work with objects, I have provided a simple function to be used with our myClass class that will return the sum of the values of two objects (instances of the class). The function will be called     addData( )    . Update the myClass class specification to the following:


class myClass
{
     private :
                  int data1;
                  int data2;
                  int data3;

     public :
                  myClass ( int d1 = 0, int d2 = 0, int d3 = 0 ) :
                         data1( d1 ), data2( d2 ), data3( d3 )
                   {  }

                  void setData1( int d )
                      {    data1 = d;    }

                  void displayData1( )
                      {    cout << endl << "Data is " << data1;    }

                  void determineData1( int d1 );

                  myClass addData( myClass obj );
};


Then put the following member function definition with the other functions either in a main file or in an auxiliary file:


myClass myClass :: addData( myClass obj )
{
     myClass temp;

     temp.data1 = data1 + obj.data1;
     temp.data2 = data2 + obj.data2;
     temp.data3 = data3 + obj.data3;

     return temp;
}// end myClass :: addData()



The function above would return the sum of obj (which was passed to the function as an argument) and the object which called upon the addData( ) function, (commonly called the this object). If we wanted to use this function, we would have to "return" the value directly into an object in our program. We could use:


    myClass obj1, ojb2, obj3;

    // obj1 and obj2 get values

    obj3 = obj1.addData(obj2);


This would add the values contained in obj2 and obj1 and store the result in obj3.

Destructors
A destructor is a member function that is called automatically when an object is destroyed. It has the same name as the class of which it is a member (which means that it has the same name as any constructors) except that it is preceded by a tilde ( ~ ). Like constructors, destructors have no return type and no parameters. The most common use of destructors is for deallocating any memory that is dynamically allocated [see Dynamically Allocating / Releasing Memory (section 5)] for the object by the constructor. Until you actually allocate memory for an instance, you don't really need to declare or define a destructor. However, just for an example, let's add a destructor to our previous myClass example:


class myClass
{
     private :
                  int data1;
                  int data2;
                  int data3;

     public :
                  myClass ( int d1 = 0, int d2 = 0, int d3 = 0 ) :
                         data1( d1 ), data2( d2 ), data3( d3 )
                   {  }

                  ~myClass() { }; // this is the destructor

                  void setData1( int d )
                      {    data1 = d;    }

                  void displayData1( )
                      {    cout << endl << "Data is " << data1;    }

                  void determineData1( int d1 );

                  myClass addData( myClass obj );
};



MEMORY USAGE NOTE: Every instance of a class will get a "copy" of the class's data members; however, all instances of a given class will use the same member functions. The member functions are created and placed into memory only one time (when they are defined). Memory for an instance's data members is allocated when the object is created.

The following is a simple complete program demonstrating most of the material covered in this section. The code was written using Microsoft Visual C++ V.6 compiler.


\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

#include <iostream.h>
#include <stdlib.h>

class myClass
{
	private:
		int data1;
		int data2;
		int data3;

	public:
		myClass(int d1 = 0, int d2 = 0, int d3 = 0):
			data1(d1), data2(d2), data3(d3)
			{  }

		void setAll(int d1, int d2, int d3);

		myClass addData(myClass objIn);

		void displayData();

};

int main()
{
	myClass obj1, obj2(2, 4, 6), obj3, obj4;
	int data1, data2, data3;

	cout << "Enter a value for data1: ";
	cin >> data1;
	cout << "Enter a value for data2: ";
	cin >> data2;
	cout << "Enter a value for data3: ";
	cin >> data3;

	obj1.setAll(data1, data2, data3);
	obj4 = obj1.addData(obj2);

	cout << endl << endl;
	cout << "Obj 1" << endl;
	cout << "-----------------------" << endl;
	obj1.displayData();

	cout << "Obj 2" << endl;
	cout << "-----------------------" << endl;
	obj2.displayData();

	cout << "Obj 3" << endl;
	cout << "-----------------------" << endl;
	obj3.displayData();

	cout << "Obj 1 + Obj2" << endl;
	cout << "-----------------------" << endl;
	obj4.displayData();

	cout << endl << endl;
	return EXIT_SUCCESS;
}// end main()

void myClass :: setAll(int d1, int d2, int d3)
{
	data1 = d1;
	data2 = d2;
	data3 = d3;
}// end myclass :: setAll()


myClass myClass :: addData(myClass objIn)
{
	myClass temp;
	temp.data1 = data1 + objIn.data1;
	temp.data2 = data2 + objIn.data2;
	temp.data3 = data3 + objIn.data3;
	return temp;
}// end myClass :: addData()


void myClass :: displayData()
{
	cout << "      Data 1 = " << data1 << endl;
	cout << "      Data 2 = " << data2 << endl;
	cout << "      Data 3 = " << data3 << endl << endl;
}// end myClass :: displayData()



///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


Function Overloading

When you use multiple constructors for a class, you are actually overloading functions. You can also overload operators and "regular" non-class functions. To overload a "regular" non-class function, simply create multiple functions using the same function name but with each having a different signature, which is the name, number of parameters, order and types of the parameters, and the class to which it belongs (does not include the return type); the overloaded functions must have different parameter lists so the compiler knows which function to use depending order and types of the arguments. For example, consider the following overloaded     swap( )     function:


void swap(int &x, int &y)
{
     int temp;
     temp = x;
     x = y;
     y = temp;
}


void swap(double &x, double &y)
{
     double temp;
     temp = x;
     x = y;
     y = temp;
}


void swap(char x[], char y[], int STRLEN)
{
     char temp[STRLEN];
     strcpy(temp, x);
     strcpy(x, y);
     strcpy(y, temp);
}


This     swap( )     function is defined for integers, floating point values (double), and strings. All three may be included in the same program. When the function is called within the program, the compiler will determine which function to use by evaluating the parameter list of the calling function.

Operator Overloading

Just as we can overload functions, we can also overload operators. Overloading is the definition of two or more functions or operators with the same identifier. The purpose of overloading an operator is to enable the programmer to write code in a more natural style with intuitive means. If an operator has not been defined or overloaded for a class, when a programmer tries to use that operator on or with an object of that class type, the program will generate some type of error because the compiler will have no clue as to the code is attempting to do. The compiler will immediately try to use the "built-in" meaning for the operator, which will not be able to "handle" the object of the class. For example, consider the addition operator ( + ). This is a built-in operator that is used for addition purposes (actually, just for integer types the built-in ( + _ op is defined three different ways: one for short, one for int, and one for long). Suppose we have two instances of a class named obj1 and obj2, and we try to add them together using the "built-in" operator:


    obj1 + obj2

Since we haven't overloaded the addition operator to handle these instances, the compiler will try to use the "built-in" operator and will generate an error; the "built-in" operator does not have access to the object's data.

We need to overload the addition operator so we can use it for instances of our class type. When we overload a particular operator for a class, the overloaded operator function will be called whenever that particular operator is used on objects of the class. Overloaded operators always have a name of the following form:


    operatorOP( )

where OP is the operator symbol you want to overload. For example, a member function named     operator+( )     will overload the addition operator for the class.

Before we get into how we can actually overload functions, we need first cover a few guidelines to follow when overloading operators:

You can overload any C++ operator except the member dot operator ( . ), the conditional expression operator ( ?: ), the pointer to member operator ( .* ), the scope resolution operator ( :: ), and the sizeof operator ( sizeof(int) ).

An overloaded operator must use the same number of operands as the corresponding "built-in" operator.

You cannot change the precedence or associativity of an operator.

You cannot create new operator symbols.

You cannot change the meanings of the operators for any "built-in" type.

Now, let's overload. An overloaded operator is "called" whenever you use the corresponding operator symbol with objects of the class type. For example, if fraction1 and fraction2 are instances of a Fraction class, then the statement:


    if (fraction1 == fraction2 )
    .
    .
	.


would be using the overloaded ( == ) "is equal to" operator for class Fraction (assuming we have defined it for the class). The actual statement works as follows:

The object to the left of the operator will be the "calling" object (often called the this object), and the object to the right of the operator will be "passed" to the overloaded operator function as an argument. Thus,     fraction1 == fraction2     would be equivalent to (and can also be written as):


    fraction1.operator==(fraction2)

The value returned from the overloaded operator is the value of the expression. For example, the     operator==( )     function must return a value of true ( 1 ) or false ( 0 ) because the expression is a logical ( boolean ) expression. This type of evaluation occurs for all overloaded operators except the expression being evaluated may not be of boolean type.

For an example, let's go back to our previous simplified version of myClass class and attempt to overload some operators. Consider the following class specification:


class myClass
{
     private :
                  int data1;
                  int data2;
                  int data3;

     public :
                  myClass ( int d1 = 0, int d2 = 0, int d3 = 0 ) :
                         data1( d1 ), data2( d2 ), data3( d3 )
                   {  }

                  ~myClass() { }; // this is the destructor

                  void setData1( int d )
                      {    data1 = d;    }

                  void displayData1( )
                      {    cout << endl << "Data is " << data1;    }

                  void determineData1( int d1 );

                  myClass addData( myClass obj );

                  bool operator==( const myClass& objIn );

                  myClass operator+( const myClass& objIn );

                  myClass& operator=( const myClass& objIn );
};


Notice that the class specification now contains three overloaded operator prototypes: "is equal to" ( == ), addition ( + ), and the assignment operator ( = ). Let's overload each operator one by one.

If we want to overload the "is equal to" operator so we can use the ( == ) in a natural style within our program, we would have to place the following function definition with all other definitions:


bool myClass :: operator==( const myClass& objIn )
{
     if ( data1 == objIn.data1 && data2 == objIn.data2 && data3 == objIn.data3 )
           return true;
     else
           return false;
}// end myClass :: operator==() overload


The return type is bool because the function will return a value of 1 (true) or 0 (false). We are passing the argument as const so we can gaurd against any type of accidental change to the object. Also notice that we passed the argument by reference. We must do this because we are passing an object that contains data members and in order to have access to the data members, we must have access to the internal memory locations for them.

We can now use the "is equal to" op in a more "natural" coding style in our program by using:


myClass obj1, obj2;
.
.
// obj1 and obj2 get values
.
.
if ( obj1 == obj2 )
    cout << "Object 1 is equal to Object 2." << endl;

The definition for the overloaded addition ( + ) operator is as follows:


myClass :: myClass operator+( const myClass& objIn )
{
        myClass temp;
        temp.data1 = data1 + objIn.data1;
        temp.data2 = data2 + objIn.data2;
        temp.data3 = data3 + objIn.data3;
        return temp;
}// end myClass :: operator+()



We can now use the addition op in a more "natural" coding style in our program by using:


myClass obj1, obj2, sum;
.
.
// obj1 and obj2 get values
.
.
sum = obj1 + obj3;

When overloading the assignment ( = ) op, we must return a reference to the calling object so we can mimic the "built-in" assignment op. Recall earlier when the "calling" object was referred to as the this object. The this object is simply the currently active object (the object that can directly modify itself). Since we have to return a reference to the "calling" object, we can simply return the this object. The following is the definition for the overloaded assignment op for myClass:


myClass& :: myClass operator=( const myClass& objIn )
{
        data1 = objIn.data1;
        data2 = objIn.data2;
        data3 = objIn.data3;
        return *this;
}// end myClass :: operator=()


We can now use the assignment op in a more "natural" coding style in our program by using:


myClass obj1, obj2, obj3, obj4;
.
.
// obj1 and obj2 get values
.
.
obj3 = obj1;
obj4 = obj2;

A complete program illustrating the use of overloaded operators for our simple myClass class is provided after the next section. I would now like to cover static variables so you can see how they work with objects in the program provided below. Read on for more...

Static Variables

Static variables, sometimes referred to as static automatic variables, are used when it is necessary for a function to "remember" the value of a variable from the first call of the function to the next call. For example, in the following program, the function     getAverage()     calculates a "running" average by remembering the sum and the number of values between calls to the function:


///////////////////////////////////// USING DEV C/C++ V.4 COMPILER ///////////////////////////////////

#include <iostream.h>
#include <stdlib.h>

float getAverage(float newValue);

int main()
{
      float data, average;

      cout << "Enter a numeric value (-999 to stop): ";
      cin >> data;
      while (data != -999)
      {
            average = getAverage(data);
            cout << "Current average is: " << average << endl;
            cout << "Enter a numeric value (-999 to stop): ";
            cin >> data;
      }

      system("PAUSE");
      return EXIT_SUCCESS;
}// end main()

float getAverage(float newValue)
{
      static float total = 0;
      static int count = 0;

      count++;
      total += newValue;
      return (total / count);
}// end getAverage()

///////////////////////////////////// USING DEV C/C++ V.4 COMPILER ///////////////////////////////////

A class may contain static data members (any function can have static variables in order to retain their value between function calls). The purpose of static data members in a class is to enable all instances of the class to "share" the same memory location for that member. No matter how many instances of the class are created, there will only be one "copy" of the static data member. Static data members are visible only within the class, but their "lifetime" is the entire execution time of the program.

For example, suppose an object needs to know how many other objects of its type are currently "active" in the program. The class could contain a static data member used to store the needed value and "share" it with all instances. This concept as well as overloaded operators is illustrated in the following complete program:


//////////////////////////////////// USING MICROSOFT VISUAL C++ V.6 COMPILER ///////////////////////////

#include <iostream.h>
#include <stdlib.h>

class myClass
{
	private:
		int data1;
		int data2;
		int data3;

		static int count; // declares static member but does not define it

	public:
		myClass(int d1 = 0, int d2 = 0, int d3 = 0):
			data1(d1), data2(d2), data3(d3)
			{ count++; }

		void setAll(int d1, int d2, int d3);
		int getCount();
		void determineData(int dataIn);
		myClass addData(myClass objIn);
		void displayData();
		bool operator==( const myClass& obj );
		bool operator!=( const myClass& obj );
		myClass operator+( const myClass& obj );
		myClass operator-( const myClass& obj );
		myClass operator*( const myClass& obj );
		myClass& operator=( const myClass& obj );

};

int myClass :: count = 0; // defines static member count outside of class specification


int main()
{
	myClass obj1, obj2(2, 4, 6), obj3, obj4;
	myClass add, sub, mult;
	int data1, data2, data3;

	cout << "There are " << obj2.getCount() << " current objects in the program." << endl << endl;

	cout << "Enter a value for data1: ";
	cin >> data1;
	cout << "Enter a value for data2: ";
	cin >> data2;
	cout << "Enter a value for data3: ";
	cin >> data3;

	obj1.setAll(data1, data2, data3);
	obj4 = obj1.addData(obj2);

	cout << endl << endl;
	cout << "Obj 1" << endl;
	cout << "-----------------------" << endl;
	obj1.displayData();

	cout << "Obj 2" << endl;
	cout << "-----------------------" << endl;
	obj2.displayData();

	cout << "Obj 3" << endl;
	cout << "-----------------------" << endl;
	obj3.displayData();

	cout << "Obj 4" << endl;
	cout << "-----------------------" << endl;
	obj4.displayData();

	// use overloaded arithmetic ops
	add = obj1 + obj2;
	sub = obj4 - obj1;
	mult = obj2 * obj3;

	// display results from arithmetic ops

	cout << endl << endl;
	cout << "Obj 1 + Obj 2" << endl;
	cout << "-----------------------" << endl;
	add.displayData();

	cout << "Obj 4 - Obj 1" << endl;
	cout << "-----------------------" << endl;
	sub.displayData();

	cout << "Obj 2 * Obj 3" << endl;
	cout << "-----------------------" << endl;
	mult.displayData();

	obj1 = obj2;


		cout << "	// perform relational comparisons
	if (obj1 == obj2)Obj 1 is now equal to Obj 2" << endl;

	if (obj3 != obj4)
		cout << "Obj 3 is not equal to Obj 4" << endl;


	cout << endl << endl;
	return EXIT_SUCCESS;
}// end main()

void myClass :: setAll(int d1, int d2, int d3)
{
	data1 = d1;
	data2 = d2;
	data3 = d3;
}// end myclass :: setAll()


void myClass :: determineData(int dataIn)
{

	if (dataIn < 0)
		cout << "Data is currently negative." << endl;
	else if (dataIn > 0)
		cout << "Data is currently positive." << endl;
	else
		cout << "Data is currently neutral." << endl;
}// end myClass :: determineData()


myClass myClass :: addData(myClass objIn)
{
	myClass temp;
	temp.data1 = data1 + objIn.data1;
	temp.data2 = data2 + objIn.data2;
	temp.data3 = data3 + objIn.data3;
	return temp;
}// end myClass :: addData()


void myClass :: displayData()
{
	cout << "      Data 1 = " << data1 << endl;
	cout << "      Data 2 = " << data2 << endl;
	cout << "      Data 3 = " << data3 << endl << endl;
}// end myClass :: displayData()


bool myClass :: operator==( const myClass& obj )
{
	return ( data1 == obj.data1 && data2 == obj.data2 && data3 == obj.data3 );
}// end myClass :: operator==()


bool myClass :: operator!=( const myClass& obj )
{
	return ( data1 != obj.data1 || data2 != obj.data2 || data3 != obj.data3 );
}// end myClass :: operator!=()


myClass myClass :: operator+( const myClass& obj )
{
	myClass temp;
	temp.data1 = data1 + obj.data1;
	temp.data2 = data2 + obj.data2;
	temp.data3 = data3 + obj.data3;
	return temp;
}// end myClass :: operator+()


myClass myClass :: operator-( const myClass& obj )
{
	myClass temp;
	temp.data1 = data1 - obj.data1;
	temp.data2 = data2 - obj.data2;
	temp.data3 = data3 - obj.data3;
	return temp;
}// end myClass :: operator-()


myClass myClass :: operator*( const myClass& obj )
{
	myClass temp;
	temp.data1 = data1 * obj.data1;
	temp.data2 = data2 * obj.data2;
	temp.data3 = data3 * obj.data3;
	return temp;
}// end myClass :: operator*()


myClass& myClass :: operator=( const myClass& obj )
{
	if (this != &obj)
	{
		data1 = obj.data1;
		data2 = obj.data2;
		data3 = obj.data3;
	}
	return *this;
}// end myClass :: operator=()

int myClass :: getCount()
{
	return (count);
}// end myClass :: getCount()


//////////////////////////////////// USING MICROSOFT VISUAL C++ V.6 COMPILER ///////////////////////////

You have reached the end of the C++ Ripped Apart tutorial. Currently, the tutorial content covers information I have learned throughout my first two programming classes in college. I should have more articles posted when I complete my third programming class this fall; articles should be posted by late December 2003. If you have encountered any problems in the tutorial or have a question, simply contact me at the address provided above. Until then, happy coding...

Back to Top