







Study with the several resources on Docsity
Earn points by helping other students or get them with a premium plan
Prepare for your exams
Study with the several resources on Docsity
Earn points to download
Earn points by helping other students or get them with a premium plan
A program is a precise sequence of steps to solve a particular problem. This course includes basic programming structure like loops, operator, memory allocation, reference, pointers etc. It teaches how to be a good programmer. This lecture handout is about: Assignment, Operator, Overloading, Pointer, Self, assignment, Function, Conversions, Constructor, Classes, Definition, String, Length, Space
Typology: Exercises
1 / 13
This page cannot be seen from the preview
Don't miss anything!








Deitel & Deitel - C++ How to Program Chapter. 7, 8
7.5, 8.1, 8.2, 8.7, 8.
As earlier discussed, overloading of operators is carried out in classes on some occasions to enable ourselves to write a code that looks simple and clean. Suppose, there is a class and its two objects, say a and b , have been defined. Addition of these objects in the class by writing a + b will mean that we are adding two different objects (objects are instance of a class which is a user defined data type). We want our code to be simple and elegant. In object base programming, more effort is made in class definitions, as classes are data types that know how to manipulate themselves. These know how to add objects of their own type together, how to display themselves and do many other manipulations. While discussing date class in the previous lecture, we referred to many examples. In an example, we tried to increment the ‘date’. The best way is to encapsulate it in the class itself and not in the main program when we come around to use the date class.
At first, we ascertain whether there is need of an assignment operator or not? It is needed when we are going to assign one object to the other, that means when we want to have expression like a = b. C++ provides a default assignment operator. This operator does a member-wise assignment. Let’s say, we have in a structure of a class three integers and two floats as data members. Now we take two objects of this class a and b and write a = b. Here the first integer of a will have the value of first integer of b. The second will have the value of second integer and so on. This means that it is a
member-wise copy. The default assignment operator does this. But what is to do if we want to do something more, in some special cases?
Now let’s define a String class. We will define it our self, without taking the built in String class of C. We know that a string is nothing but an array of characters. So we define our String class, with a data member buffer, it is a pointer to character and is written as *buf i.e. a pointer to character (array). There are constructors and destructors of the class. There is a member function length that returns the length of the string of the calling object. Now we want an assignment operator for this class. Suppose we have a constructor that allows placing a string into the buffer. It can be written in the main program as under:
String s1 ( “This is a test” ) ;
Thus, an object of String has been created and initialized. The string “This is a test” has been placed in its buffer. Obviously, the buffer will be large enough to hold this string as defined in our constructor. We allocate the memory for the buffer by using new operator. What happens if we have another String object, let’s say s2, and want to write s2 = s1 ; Here we know that the buffer is nothing but a pointer to a memory location. If it is an array of characters, the name of the array is nothing but a pointer to the start of the memory location. If default assignment operator is used here, the value of one pointer i.e. buf of one object will be assigned to buf of the other object. It means there will be the same address in the both objects. Suppose we delete the object s1 , the destructor of this object will free the allocated memory while giving it back to the free store. Now the buf of s2 holds the address of memory, which actually has gone to free store, (by the destructor of s1). It is no longer allocated, and thus creates a problem. Such problems are faced often while using default assignment operator. To avoid such problems, we have to write our own assignment operator.
Before going on into the string assignment operator, let’s have a look on the addition operator, which we have defined for strings. There is a point in it to discuss. When we defined addition operator for strings, we talked about that what we have to do if we want to add (concatenate) a string into the other string. There we had a simple structure i.e. there is a string defined char buf with a space of 30 characters. If we have two string objects and the strings are full in the both objects. Then how these will be added? Now suppose for the moment that we are not doing memory allocation. We have a fixed string buffer in the memory. It is important that the addition operator should perform an error check. It should take length of first string , then the length of second string, before adding them up, and check whether it is greater than the length defined in the String (i.e. 30 in this case). If it is greater than that, we should provide it some logical behavior. If it is not greater, then it should add the strings. Thus, it is important that we should do proper error checking. Now in the assignment operator, the problem here is that the buf , that we have defined, should not point to the same memory location in two different objects of type String. Each object should have its own space and value in the memory for its string. So when we write a statement like s2 = s1 , we want to make sure that at assignment time, the addresses should not be assigned. But there should be proper space and the strings should be copied there. Let’s see how we can do this.
docsity.com
char *buf ; public: // constructors String(); String( const char *s ) { buf = new char [ 30 ]; strcpy (buf,s); } // display the string void display ( ) { cout << buf << endl ; } // getting the length of the string int length ()const { return strlen(buf); } // overloading assignment operator void operator = ( const String &other ); }; // ----------- Assignment operator void String::operator = ( const String &other ) { int length ; length = other.length(); delete buf; buf = new char [length + 1]; strcpy( buf, other.buf ); }
//the main program that uses the new String class with its assignment operator: main() { String myString( "here's my string" ); cout << “My string is = ” ; myString.display(); cout << '\n';
String yourString( "here's your string" ); cout << “Your string is = ” ; yourString.display(); cout << '\n';
yourString = myString; cout << “After assignment, your string is = ” ; yourString.display(); cout << '\n'; system ("pause");
docsity.com
Following is the output of the program. My string is = here's my string
Your string is = here's your string
After assignment, your string is = here's my string
The above example is regarding the strings. Yet in general, this example pertains to all classes in which we do memory manipulations. Whenever we use objects that allocate memory, it is important that an assignment operator (=) should be defined for it. Otherwise, the default operator will copy the values of addresses and pointers. The actual values and memory allocation will not be done by it.
Let’s go on and look what happens when we actually do this assignment? In the assignment of integers, say we have three integers i, j and k with some values. It is quiet legal to write as i = j ; By this ,we assign the value of j to i. After this we write k = i ; this assigns the value of i to k. In C, we can write the above two assignment statements in one line as follows
k = i = j ;
This line means first the value of j is assigned to i and that value of i is assigned to k. The mechanism that makes this work is that in C or C++ every expression itself has a value. This value allows these chained assignment statements to work. For example, when we write k = i = j ; then at first i = j is executed. The value at the left hand side of this assignment statement is the value of the expression that is returned to the part ‘ k =’ .This value is later assigned to k. Now take another example. Suppose we have the following statement:
k = i = ++j ;
In this statement, we use the pre-increment operator, as the increment operator (++) is written before j. This pre-increment operator will increment the value of j by 1 and this new value will be assigned to i. It is pertinent to note that this way ++j returns a value that is used in the statement. After this, the value of i is assigned to k. For integers, it is ok. Now, how can we make this mechanism work with our String objects. We have three String objects s1 , s2 and s3. Let’s say there is a value (a string ) in the buffer of s1. How can we write s3 = s2 = s1 ;. We have written s2 = s1 in the previous example in assignment operator. We notice that there is no return statement in the code of the assignment operator. It means that operator actually returns nothing as it has a void return type. If this function is not returning any thing then s3 = s2 = s ; cannot work. We make this work with the help of this pointer.
this Pointer Whenever an object calls a member function, the function implicitly gets a pointer from the calling object. That pointer is known as this pointer. ‘ this’ is a key word. We cannot use it as a variable name. ‘ this’ pointer is present in the function, referring to
docsity.com
being gotten (R.H.S.) are the same or not. So we can write the equal operator (operator=) as follows
void String::operator=( const String &other ) { if( this == &other ) return; delete buf; length = other.length; buf = new char[length + 1]; strcpy( buf, other.buf ); }
Here above, the statement if ( this == &other) checks that if the calling object (which is referred by this ) is the same as the object being called then do nothing and return as in this case it is a self assignment. By doing this little change in our assignment operator, it has become safe to use. So it is the first usage of this pointer that is a check against self assignment.
Returning this Pointer From a Function Now lets look at the second use of this pointer. We want to do the assignment as
s3 = s2 = s1 ;
In this statement the value of s2 = s1 is assigned to s3. So to do this it is necessary that the assignment operator should return a value. Thus, our assignment operator will expand so that it could return a value. The assignment operator, till by now copies the string on right hand side to the left hand side. This means s2 = s1 ; can be done by this. Now we want that this s2 should be assigned to s3 , which can be done only if s = s1 returns s2 (an object). So we need to return a String object. Here becomes the use of this pointer, we will write as
return *this ;
Here, in the assignment operator code this is referring to the calling object (i.e. s2 in this case). So when we write return *this ; it means return the calling object ( object on L.H.S.) as a value. Thus s3 gets the value of s2 by executing s3 = s2 where s2 is the value returned by the assignment operator by s2 = s1 ; Thus the complete assignment operator (i.e. operator= function) that returns a reference to an object will be written as under
String &String::operator=( const String &other ) { if( &other == this ) //if calling and passed objects are return *this; // same then do nothing and return delete buf; length = other.length; buf = new char[length + 1]; strcpy( buf, other.buf );
docsity.com
return *this; }
Now, here the first line shows that this operator= function returns a reference to an object of type String and this function takes a reference as an argument. The above version of operator= function takes a reference as an argument. First of all it checks it with the calling object to avoid self assignment. Then it deletes the buffer of calling object and creates a new buffer large enough to hold the argument object. And then at the last it returns the reference of the calling object by using this pointer. With this version of the assignment operator ( operator= function) we can chain together assignments of String objects like s3 = s2 = s1 ; Actually, we have been using this pointer in chained statements of cout. For example, we write cout << a << b << c ;
Where a , b , and c are any data type. It works in the way that the stream of cout i.e. << is left associative. It means first cout << a is executed and to further execute it, the second << should have cout on its left hand side. So here, in a way, in this operator overloading, a reference to the calling object that is cout is being returned to the stream insertion <<. Thus a reference of cout is returned, and (as a reference to cout is returned) the << sees a cout on left hand side and thus the next << b is executed and it returns a reference to cout and with this reference the next << c works. This all work is carried out by this pointer.
We have seen that value can be returned from a function with this pointer. We used it with assignment operator. Lets consider our previous example of Date class. In that class we defined increment operator, plus operator, plus equal operator, minus operator and minus equal operator. Suppose we have Date objects d1 , d2 and d. When we write like d2 = d1++ ; or d2 = d1 + 1 ; here we realize that the + operator and the ++ operator should return a value. Similarly, other operators should also return a value. Now let’s consider the code of Date class. Now we have rewritten these operators. The difference in code of these is not more than that now it returns a reference to an object of type Date. There are two changes in the previous code. First is in the declaration line where we now use & sign for the reference and we write it like
Date& Date::operator+=(int days)
We write this for the all operators (i.e. +, ++, - and -=). Then in the function definition we return the reference of the left hand side Date object ( i.e. calling object) by writing return *this ;
Now we can rewrite the operator+= of the Date class as follows. The declaration line in the class definition will be as
Date& operator+=(int days);
And the function definition will be rewritten as the following.
docsity.com
There is an example of it. Suppose we have stored date in a serial number form. So now it is not in the form of day, month and year but it is now a long integer. For example if we start from January 1, 1900 then 111900 will be the day one, second January will be the day 2, third January will be the day 3 and so on. Going on this way we reached in year 2000. There will be a serial number in year 2000. This will be a single long integer that represents the number of days since a particular date. Now if we have a Date object called d and an integer i. We can write d = i ; which means we want that i should go in the serial part of d. This means convert the integer i into date object and then the value of i should go into the serial number and then that object should be assigned to d. We can make this conversion of Date object. We do this in the constructor of the class. We pass the integer to the constructor, here convert it into a date, and the constructor then returns a Date object. On the other hand, if there is no constructor then we can write conversion function. We have used the conversion operator with cast. The way of casting is that we write the name of the cast (type to which we want to convert) before the name of variable. Thus if we have to write x = i ; where x is a float and i is an integer then we write it with conversion function as x = (float) i ; The (float) will be the conversion operator. Normally this conversion is done by default but sometimes we have to force it. Now we want to write a conversion operator, which converts an integer to a Date object. (here Date is not our old class, it’s a new one). We can write a conversion function. This function will be a member of the Date class. This function will return nothing, as it is a conversion function. The syntax of this function will be Date () that means now this is a conversion function.
In the body of this function we can write code of our own. Thus we can define the operator, and it will work like that we write within the parentheses the name of the conversion operator. The conversion functions are quiet interesting. They allow us to manipulate objects of different classes. For example, we have two classes one is a truck and other is a car. We want to convert the car to a truck. Here we can write a conversion function, which says take a car convert it into a truck and then assign it to an object of class truck.
Sample Program (conversion by constructor)
Lets take a look at an example in which the conversion functions are being used. There is a class Fraction. Lets talk why we called it Fraction class. Suppose we have an assignment
double x = 1/3 ;
When we store 1/3 in the computer memory, it will be stored as a double precision number. There is a double precision division of 1 by 3 and the answer 0.33333… is stored. Here the number of 3s depends upon the space in the memory for a double precision number. That value is assigned to a double variable. What happens if we multiply that double variable by 3, that means we write 3 * x ; where x was equal to 1/3 (0.33333…). The answer will be 0.99999…, whatever the number of digits was. The problem here is that the answer of 3 * 1 / 3 is 1 and not 0.99999. This problem occurs, when we want to represent the numbers exactly. The fraction class is the class in which we provide numerator and denominator to the object and it stores them separately. It will always keep them as an integer numerator and an integer denominator and we can do all kinds of arithmetic with it. Now if 1 and 3 are stored
docsity.com
separately as numerator and denominator, how we can add them to some other fraction. We have enough tools at our disposal. One of which is that we can overload the addition operator. We can add 1/3 and 2/5, the result of which is another fraction i.e. numerator and denominator. In this way we have no worries of round off errors, the truncation and conversions of int and floats.
Considering the Fraction class we can think of a constructor which takes an integer and converts it into a fraction. We do not want that as a fraction there should be some value divided by zero. So we define the default constructor that takes two integers, one for numerator and one for denominator. We provide a default value for the denominator that is 1. It means that now we can construct a fraction by passing it a single integer, in which case it will be represented as a fraction with the passed integer as a numerator and the default vale i.e. 1 as the denominator. If we have a fraction object f and we write f = 3 ; Then automatically this constructor (i.e. we defined) will be called which takes a single integer as an argument. An object of type fraction will be created and the assignment will be carried out. In a way, this is nothing more than a conversion operation. That is a conversion of an integer into a fraction. Thus a constructor that takes only one parameter is considered a conversion function; it specifies a conversion from the type of the parameter to the type of the class.
So we can write a conversion function or we can use a constructor of single argument for conversion operation. We cannot use both, we have to write one or the other. Be careful about this. We don’t use conversion functions often but sometimes it is useful to write them. The conversion operators are useful for defining an implicit conversion from the class to a class whose source code we don't have access to. For example, if we want a conversion from our class to a class that resides within a library, we cannot define a single-argument constructor for that class. Instead, we must use a conversion operator.
It makes our code easier and cleaner to maintain. It is important that pay more attention while defining a class. A well-defined class will make their use easy in the programming. Following is the code of the example stated above.
/* This program defines a class Fraction which stores numerator and denominator of a fractional number separately. It also overloads the addition operator for adding the fractional numbers so that exact results can be obtained. */
#include <stdlib.h> #include <math.h> #include <iostream.h>
// class definition class Fraction { public: Fraction(); Fraction( long num, long den );
docsity.com
// ----------- Greatest common factor // computed using iterative version of Euclid's algorithm long Fraction::gcf( long first, long second ) { int temp; first = labs( first ); second = labs( second ); while( second > 0 ) { temp = first % second; first = second; second = temp; } return first; }
//main program
{ Fraction a, b( 23, 11 ), c( 2, 3 ); a = b + c; a.display(); cout << '\n'; system("pause"); }
The output of the program is as follows 91/
Here is an example from the real world. What happens if we are dealing with currency? The banks deal with currency by using computer programs. These programs maintain the accounts by keeping the track of transactions and manipulating deposits and with drawls of money. Suppose the bank declares that this year the profit ratio is 3.76 %. Now if the program calculates the profit as 3.67 % of the balance, will it be exactly in rupees and paisas or in dollars and cents? Normally, all the currencies have two decimal digits after decimal point. Whenever we apply some kind of rates in percentage the result may become in three or four decimal places. The banks cannot afford that the result of 90 paisas added to 9 rupees and 10 paisas become 10 rupees and 1 paisa. They have to accurate arithmetic. So they do not rely on programs that use something like double precisions to represent currencies. They would have rather written in the program to treat it as a string. Thus 9 is a string, 10 is a string and the program should define string addition such that the result of addition of the strings. and .90 should be 1.00. Thus, there are many things that happen in real world that force us as the programmer to program the things differently. So we do not use native data types.
The COBOL, COmmon Business Oriented Language has a facility that we can represent the decimal numbers exactly. Internally it (the language) keeps them as strings in the memory. There is no artificial computer representation of numbers. Now a day, the languages provide us the facility by which we can define a data type
main()
docsity.com