C++ Under Dos
Harking back to the early days of computers, two people, working at Bell Labs had developed a programming language called 'C'. Compared to the languages in existence at that time, this language was a phenomenal leap ahead. No other language could match either the capability, or the flexibility, that C could offer. However, people soon learned that it was precisely this flexibility of C , that led to problems in debugging code, written in C. They yearned for something that contained all the features of C, along with a few additional features that would make code easier to debug. An example of what we are trying to say will be demonstrated in the following examples.
Consider the first example.
a1.c
main()
{
printf();
}
We have typed this program in BorlandC. As we may expect, we get no errors after compiling and linking. However, the output of this program appear to be some vague values.
a2.c
main()
{
printf(1000);
}
Consider the next C program that we have run. We realize that the compilation of this program, also, takes place without errors. When we run this program, we realize that the output, once again, is totally incomprehensible. All that we are trying to demonstrate are the minor lapses of C. Simple programs will compile without any problems. However, when we try to run them, we realize that the output we wish to obtain, simply did not materialize. For example, instead of printing 1000 when we run it, program a2.c just prints some junk. So, where was the problem? After all, if there is an error in our program, why isn't the compiler or the linker informing us about the problem? And if the program can't print what we want it to, why doesn't it just come back and tell us so. Aah! Haven't we just demonstrated that C, as we know it, is difficult to debug. No wonder that serious programmers in this world do not use C any longer.
One reason as to why we do not get errors in the C programs that we have just been working on, is due to the fact that the C compiler doesn't actually understand functions. And since it doesn't understand functions, it definitely cannot understand parameters that are passed to functions. Effectively, C just ignores all functions.
Let's go deeper into what happened in the programs that we just executed. We know that main() is always the first function to get called. Now, within the braces following main(), we give the name of the function along with the parameters we are going to pass to it. This statement is terminated, as usual, by a semicolon. The function we are calling in this program is printf( ). This function will be present beyond the braces that signal the end of the main() function. Any function that is outside main() doesn't really know of the parameters being passed to it. When the function is called, it will simply pick up the necessary parameters from the stack. Printf is a function that displays a string until it sees a 0, indicating the termination of the string
In the first program, our function printf will inquire about the location of the stack and start displaying whatever it finds there, until it encounters a 0. The second program will, however, specifically go to memory location 1000, and start printing whatever string is present from that location onwards.
Let us consider another program.
a3.c
main()
{
abc(10,20);
}
abc(int i,int j)
{
}
As usual, when we run this C program, we get no errors. As we have included no statement that will force our program to give us an output, no output is precisely what we are going to end up with. Now save this program with a .cpp extension. To be more accurate, save it as a3.cpp. A minor change, indeed, but one that is going to cause us innumerable headaches. Because on compilation of this program, we now end up with an error. Well, if C did not give us an error, why is it that the compilation of the same program completely stalled by the C++ compiler. Instead of tearing our heads in frustration, it would instead be safer to conclude that C++ just decided to act like the rest of the world. I've always maintained that the biggest problem with this world is that no one really trusts anyone else. I mean, why can't America just believe Saddam Hussein when he claims that Iraq has fully destroyed all it's nuclear weapon production plants? Why doesn't the Justice Department of the USA believe Microsoft when the latter claims that it isn't engaging in unfair trade practices? And why doesn't our C++ compiler just take our word? Why doesn't it just accept the fact that when we call function abc with two parameters, these two parameters that we are passing ,are exactly the same type as those that have been specified within our function abc?
When function abc is called, it will simply pick up the necessary parameters from the stack. As you can see, we have passed our function abc two integers. But the C++ compiler is adamant that we have to inform it, well in advance, as to precisely, what parameters we shall be passing to our function. Right now, it claims that it just doesn't know anything about the parameters of abc, which is why it simply terminates the compilation process midway, and gives us an error. This behaviour is very much unlike that of the C compiler, which doesn't really bother about what we pass to any function. To drive home this point, we make use of another C program.
a4.c
main()
{
abc(10);
}
abc(int i,int j)
{
}
Here, we only pass a single parameter to function abc which accepts two parameters. If we are expecting an error, we are definitely going to be dissapointed. We simply get a series of warnings. Have any of us ever really bothered about mere warnings from professors in college that we are falling behind in our studies. It was only when we flunked, that we looked back to see where we went wrong. The reason why C doesn't give us any errors is because of the same flexibility that we had earlier been talking about. Now save this program as a C++ program, and we shall realize that C++ isn't quite so lenient. We definitely get an error now.
At this point, let us go back to our previous C++ program a3.cpp, the one in which we passed two parameters to abc. A simple way to eliminate the error we got, would be simply to place the entire function abc, before main(). After all, the C++ compiler has simply to know all about function abc before it is called, and this is a simple way of fulfilling that criteria.
a5.cpp
abc(int i,int j)
{
}
main()
{
abc(10,20);
}
But what if we wish to have main() appear at the beginning of our program, to signify that main() is, after all, the function that is always called first. Or, what if we do not have the code of function abc, which means that there is no way by which we can place it before main(). In that case, before main(), we shall have to incorporate a statement that will give the compiler all the information it needs about our function abc. This declaration will include the name of the function as well as the type and number of parameters that are passed to the function. Such a statement is known as a function prototype. In actual fact, we also have to state the type of value, returned by the function. However the default is assumed to be an int. So, as long as the return value is an integer, we need not specify it.
a6.cpp
abc(int,int);
main()
{
abc(10,20);
}
abc(int i,int j)
{
}
Returning to our fourth C++ program, it will continue to give us errors, even if we resort to the two techniques discussed above. The compiler merely informs us that there are too few parameters in the call to function abc. The only way to eliminate this error is to actually have a function abc that accepts only one parameter, or pass two parameters to abc of datatypes as specified in the prototype. Here we have chosen the first option.
a4.cpp
abc(int);
main()
{
abc(10);
}
abc(int i)
{
}
Now consider the case when we have included something we use in almost each and every C program, namely printf(), within our own function abc.
a5.cpp
abc(int);
main()
{
abc(10);
}
abc(int i)
{
printf("hi");
}
On compiling this program, we get an unexpected error. At this point, we realize that printf is a essentially a function like any other. And like any other function we have to supply it's prototype, failing which, the C++ compiler will signal an error. Now the problem arises as to what the prototype of printf looks like, because, unlike function abc, we didn't write the code for the function printf(). We have no idea as to what the parameters accepted by printf() are. What we do know is that the people who wrote the function printf, placed it's prototype in a header file called stdio.h. All we have to do is include this header file in our program and the error vanishes.
a5.cpp
#include<stdio.h>
abc(int);
main()
{
abc(10);
}
abc(int i)
{
printf("hi");
}
All that we actually use in this entire header file is a single line of code that gives us the prototype of the function printf. Simply including the header file eliminates the necessity of bothering further about what parameters printf accepts, and so on. Instead, we could have just taken the single line that made up the prototype of printf, from the header file, and placed it at the beginning of our program. The program now executes without any further hassles, and says hi to us. Oh no! All that effort just for a simple hi ! Not worth it, if you ask me.
Consider the next program.
a6.cpp
main()
{
char *p;
p=100;
}
Compiling this program as a simple C program definitely gives us no errors. But merely giving it a .cpp extention, however will give rise to an error informing us that the conversion from int to char* is not possible. Once again, the strict error checking capabilities of C++ are evident to one and all. We cannot pass an integer in place of a pointer, or a long in place of a character, directly. In such cases, we have to take a more round-about route. A route known as casting. The term, itself might put you off, but actually, it simply means that we are ensuring that the terms to the right of an equal-to sign, ie '=', match the terms to the left of the same sign. All that we really have to is to place (char *), that is, the type of parameter p, within brackets, before anything we want to cast it to. This will, in no way, change any value to the right of the equal to sign. We can safely assure you that the value, 100, is not going to change to 101, or anything else, after the casting has been performed. Placing the cast just ensures that our program compiles without further problems.
C++ has extremely strict rules to ensure that what is on the type of variable to the right of the equal-to sign is the same as what's on it's right. Casting is a way that allows us to break these rules, enabling us to assign one variable type to a totally dissimilar variable type.
a6.cpp
main()
{
char *p;
p=(char *)100;
}
We now go one stage further. We had earlier stated that C didn't really know much about functions, and even less about their parameters. Also, that C++ treats functions in a manner totally different from that of C. Lets go back to a C program , which has one function that accepts two parameters. We now only compile this file without building it. At the end of this process, we get an Obj file. We use the 'type' command to print this file out, by saying 'type a7.obj'. If we now search through the maze of incomprehensible symbols printed out, we shall see the term _abc. Repeat the same process with a8.c, in which function abc has only one parameter. When we now compile and type the obj file we still see function abc in exactly the same form as in the earlier program, namely _abc.
a7.c a8.c
main() main()
{ {
abc(10,20); abc(10);
} }
a7.cpp a8.cpp
abc(int,int); abc(int);
main() main()
{ {
abc(10,20); abc(10);
} }
We now take a look at the C++ equivalents of the same programs. We compile a7.cpp and print it's Obj file. We now notice a slight change in the manner in which function abc appears. It appears as @abc$qii. So maybe, this is the way that C++ displays functions. Compile a8.cpp, and view the obj file. Surprise!Surprise! Our function is no longer @abc$qii, but simply @abc$qi. We thus observe that the single 'i' at the end of the function name actually refers to the fact that the function abc has an integer as a parameter. We now realize that the C++ has treated the same function in a different manner. The name of the function in the Obj file has been totally changed, after we have changed it's parameters. You may think that the merely the addition of a single character is no great deal, but try sending us E-Mail at vmukhii@giasbm01.vsnl.net.in instead of vmukhi@giasbm01.vsnl.net.in. It will definitely not reach us. The mere addition of an extra 'i', results in the name being totally changed. This property of changing the function name totally based on what the parameters are, is known as Name Mangling. Some people also call this same property, function overloading, where we can have more than one function of the same name, within a program, as long as the parameters being passed to it are not the same. Alternately, we could also call this property polymorphism. However, if you are from Microsoft or you are a great fan of the Bill Gates, you may call this the property of Decorative Names.
We could also test the same function by now passing two characters instead of integers. Once again the name of the function in the obj file changes, this time to @abc$qzczc, where 'zc' denotes that the parameter is a character. Similarly, having one character and one integer as parameters of abc result in the name being changed to @abc$qzci. We now conclude that C++ treats functions with the same function name, differently, if their parameters are not the same. We could take advantage of this feature of C++, to perform different tasks based on the number of parameters passed to a number of functions, all having the same name.
Consider the next program. If we save this program as C program, we shall get compilation error that say 'type mismatch in redeclaration of abc','extra parameter in call to abc' and so on. This is because C obviously cannot distinguish between two functions of the same name, having different parameters. C++ on the other hand, has no such problems, and the program executes with absolutely no hassles.
a9.cpp
#include<stdio.h>
abc(int);
abc(int, int);
main()
{
abc(10,20);
abc(100);
}
abc(int i,int j)
{
printf("%d..%d\n",i,j);
}
abc(int k)
{
printf("%d..\n",k);
}
Before we go on to the next topic, let us look at a very prominent feature of the C programming language, namely structures. It's almost impossible to describe just how useful, structures proved to be to us. Suppose we had a very rich family called the Ford family, that had six members: Tom, Dick, Harry, Liz, Mary and Jane. Let us assume that we need to know the monthly income of each and every member of the family. Which could mean that we are either from the IRS/Income Tax Department, or just common criminals. Either way, we would be very interested in keeping tabs on everyone's income. To do so, we could make use of a computer program. Since all these people are from one family, wouldn't it be convenient for us to be able to group them all under one heading. After all, their's might not be the only family we wish to loot. ( Oh boy! Did we just give ourselves away?) Suppose we were collecting information about a hundred people in town, maybe, we could have just created variables to access each of these hundred people, but it would definitely be easier to just group them according to the family they belonged to. In doing so, we also would be avoiding the possibility of a huge mess, later on, when we may be tracking an even larger number of people. To serve precisely this need, we make use of structures. We could create a structure that contained the required information about all the members of this family, and name this structure Ford. We could now make use of the 'Ford' structure to update ourselves about the income of any member of this family.
What we are trying to say is that structures make our lives easier. But first, let us look at an example of a structure.
sc1.cpp
#include<stdio.h>
struct zzz
{
int i,j;
};
main()
{
struct zzz a;
printf("Size = %d\n",sizeof(a));
printf("Location= %p\n",&a);
}
Here, zzz is a structure tag with two members, i and j. Within main( ), we have the statement 'struct zzz a;' By this statement, we are saying that 'a' is now a structure that looks like zzz. We can print the size of this structure as well as it's location in memory.
We may now access any variable within zzz through 'a'. This is shown in the next program.
Since variables i and j now belong to the family of 'a', to access them, we say a.i and a.j. Since we are using the C++ programming language, we may even avoid the use of the keyword struct, while declaring 'a'.
The statement 'zzz a;' accomplishes the same thing.
sc2.cpp
#include<stdio.h>
struct zzz
{
int i,j;
};
main()
{
zzz a;
printf("Address of a is %p\n",&a);
printf("Variables values are %d ..%d ", a.i,a.j);
printf("Variables addr are %p and %p ", &a.i,&a.j);
}
OUTPUT:
Address of a is 09F8:0FFC Variables values are 294..857 Variables addr are 09F8:0FFC and 09F8:0FFE
As the variables i and j have not been initialized, the values that are displayed may be just anything. But the addresses of one of the variables is the same as the address of structure in memory. Of course, we can also declare another structure, 'b', that looks like zzz. This structure would be allocated a totally different memory location, and can also access variables in a manner similar to that of 'a'. The address of 'b' will also be the same that of ' b.i '.
sc3.cpp
#include<stdio.h>
struct zzz
{
int i,j;
};
main()
{
zzz a;
printf("Address of a is %p\n",&a);
a.i=10; a.j=20;
printf("Variables %d...%d\n",a.i,a.j);
printf("Variables addr are %p and %p ", &a.i,&a.j);
zzz b;
printf("Address of b is %p\n",&b);
b.i=100; b.j=200;
printf("Variables %d...%d\n",b.i,b.j);
printf("Variables addr are %p and %p ", &b.i,&b.j);
}
OUTPUT:
Address of a is 1BF4:0FFC Variables 10...20 Variables addr are 1BF4:0FFC and 1BF4:0FFE Address of b is 1BF4:0FF8 Variables 100...200 Variables addr are 1BF4:0FF8 and 1BF4:0FFA
Now, we shall do something that we were not able to do while using 'C'. We shall actually incorporate a function within our structure zzz. Now, maybe, you might wonder as to why we need functions within our structures. To answer that question, lets just say that the use of functions within structures increases their utility a hundred fold. Now, even functions can be called in a manner similar to that of variables. However, while each variable within a structure may only be able to inform us about their value at that instant, a function could perform a variety of tasks , all at one go.
By that, we mean that we now do not have to first obtain the values of each variable from main(), before performing any desired task with these variables. Everything can now be done within the structure itself . For example, if we go back to our old Ford structure, we could now have a function that will display a list of only those people whose income exceeds $10,000 per month. In other words, we could now do some intelligent processing work within our structure.
sc4.cpp
#include<stdio.h>
struct zzz
{
int i,j;
abc()
{
printf("abc\n");
}
};
main()
{
zzz a;
printf("Addr =%p\n",&a);
printf("Variables %d...%d\n",a.i,a.j);
a.abc();
}
To further demonstrate what we can do with functions within structures, we have placed a single function print, within zzz, that we shall access from both the structures, a as well as b.
sc5.cpp
#include<stdio.h>
struct zzz
{
int i,j;
void print()
{
printf("%d...%d\n",i,j);
}
};
main()
{
zzz a;
a.i=10;a.j=20;
a.print();
zzz b;
b.i=100;b.j=200;
b.print();
a.print();
}
Now, when function print is called, it accepts only those values of members of that particular structure from where it has been called. So, obviously, when we call print from the structure b, it will only display the values that have been assigned to the variables of that structure, ie 100 and 200. The values of the variables that have been assigned by structure 'a', remain unchanged, at 10 and 20. This can be seen from the output of the last print statement.
Until this point, we have freely incorporated functions within structures. However, when we think of the term structure , we tend to refer to the conventional C definition of a structure, which refers to merely a group of variables. So, whenever we have to have functions within a structure in C++, we do not call it a structure any longer. We now give such a structure a new name, Class. To differentiate between all variables, structures and classes, we may use the following distinguishing points. A variable has one value at any point in time, while a structure is merely a collection of variables. A class, on the other hand, is a structure tag that contains code or functions within it.
We shall now replace the word structure by the word class, in our next program.
sc6.cpp
#include<stdio.h>
class zzz
{
int i;
int j;
void print()
{
printf("%d...%d\n",i,j);
}
};
main()
{
zzz a;
a.i=10;a.j=20;
a.print();
}
When we compile this program, however, we end up with quite a few errors. So, was our previous statement about replacing the word struct by the word class, wrong? Well, not quite. While we can make use of the word class, C++ now treats the variables and the functions within our renamed structure, in a slightly different manner. Earlier, we could access a member of the structure without any problems. However, the members of a class have specifically to be declared accessible to all, so that they can be used outside the class. This can be achieved by the statement, public :
sc7.cpp
#include<stdio.h>
class zzz
{
int i;
public:
int j;
void print()
{
printf("%d...%d\n",i,j);
}
};
main()
{
zzz a;
a.i=10;
a.j=20;
a.print();
}
When we now compile our program, we just get a single error to signify that the variable i, is not accessible to anyone. However, we may now access the variable j, or the function print(), without any problems. To remove all the errors during compilation, we simply place the 'public :' , before declaring any variables or functions that have to be accessed from outside the class.
We had earlier mentioned that 'a' was a structure that looked like the structure tag zzz. But, now that zzz is called a class, shouldn't it be appropriate to rename 'a', as well? So we no longer call 'a', a structure, but instead, refer to it as an object of class zzz. We could also describe 'a' as being an instance, or an occurance, of class zzz. Similar to structures, we can create several objects of the same class. Now consider the next program.
inh1.cpp
#include<stdio.h>
class zzz
{
public:
int i,j;
void abc()
{
printf("In abc zzz");
}
void print()
{
printf("%d...%d\n",i,j);
}
};
main()
{
zzz a;
a.i=10;a.j=20;
a.print();
a.abc();
yyy b;
}
When we compile the above program, we definitely get errors. After all, we are trying to create an object 'b' that looks like yyy, which in turn, does not exist. We first have to specifically make the statement 'class yyy{};', which will easily convince the compiler that yyy is a class. Only then can we create an object that looks like yyy.
inh2.cpp
#include<stdio.h>
class zzz
{
public:
int i,j;
void abc()
{
printf("In abc zzz");
}
void print()
{
printf("%d...%d\n",i,j);
}
};
class yyy
{
};
main()
{
zzz a;
a.i=10;a.j=20;
a.print();
a.abc();
yyy b;
b.i=100;
}
The original error that we had, disappears. Which means that object b has been created. We had, however added a single line of code, wherein, we attempted to assign some value to a variable i, using the object 'b'. We now encounter a new error saying that the variable i, is not a member of class yyy. This is, but obvious, as class yyy does not contain any code right now. This variable i, however, is a member of class zzz. So, is it possible for 'b' to ever be able to access variable i, even if it belongs to another class? To find out, read ahead.
inh3.cpp
#include<stdio.h>
class zzz
{
public:
int i,j;
void abc()
{
printf("In abc zzz");
}
void print()
{
printf("%d...%d\n",i,j);
}
};
class yyy:public zzz
{
};
main()
{
zzz a;
a.i=10;a.j=20;
a.print();
a.abc();
yyy b;
b.i=100;
b.j=200;
b.print();
}
When we compile this program, we might be surprised to get absolutely no errors. As far as we can see, class yyy still contains no code. But actually, this is not the case. When we use the statement 'class yyy : public zzz ', we are essentially deriving class yyy from class zzz. In other words, whatever code present within class zzz, is taken and pasted into class yyy. Class yyy may freely make use of a variable or function from any class, as long as we use :public, along with the name of that class. Once again, just as in structures, changing the values assigned to variables of object 'b', does not have any effect on those of object 'a'.
inh4.cpp
#include<stdio.h>
class zzz
{
public:
int i,j;
void abc()
{
printf("In abc zzz\n");
}
void print()
{
printf("from print %d...%d\n",i,j);
}
};
class yyy:public zzz
{
public:
void pqr()
{
printf("in pqr yyy\n");
}
};
main()
{
zzz a;
a.i=10;a.j=20;
a.print();
a.abc();
yyy b;
b.i=100;
b.j=200;
b.print();
b.abc();
b.pqr();
a.print();
}
OUTPUT:
from print10...20 In abc zzz from print100...200 In abc zzz in pqr yyy from print10...20
Here, we have shown that object 'b' can call any function from the class zzz, as well as all the functions in class yyy. Class zzz is called the base class, while class yyy is called the derived class. In this program, we have proved that the derived class now contains all it's own functions, as well as the functions, it obtained from the base class. But let's divert our attention back to the Ford family, in particular, Tom Ford. Being the eldest of the siblings, he inherited the Ford family fortune on the sad demise of their parents. But, since the lad had been working for quite sometime, he also had savings of his own. In other words, his total worth was now greater than that of his parents. Any derived class behaves in a manner similar to that of Tom Ford. In size, it is now much larger that the base class.
The code of the function print(),is automatically inserted into yyy, since yyy is derived from class zzz. It is the same case with the variables i and j. Because of this, we may safely initialize these variables from 'b', and rest, assured that only the values of b.i and b.j, and none other will be displayed, when the statement b.print() is executed.
While the derived class can use any function present in the base class, the same is not true in the reverse case. It is simply not possible for us to have the statement ' a.pqr(); ', in this particular program. Now let us see what happens when we have a function with the same name in the base class, as well as the derived class.
inh5.cpp
#include<stdio.h>
class zzz
{
public:
void abc()
{
printf("In abc zzz \n");
}
};
class yyy:public zzz
{
public:
void pqr()
{
printf("in pqr yyy \n");
}
void abc()
{
printf("In abc yyy \n");
}
};
main()
{
zzz a;
yyy b;
a.abc();
b.pqr();
b.abc();
}
The output of this program is :
In abc zzz In pqr yyy In abc yyy
In other words, if the same function is present in both, the base and the derived class, and we are calling this function from the object of the derived class, the function from the derived class is called. In this case, obviously, abc will be called from class yyy, and not from class zzz. Only when abc is not present in class yyy, will it be called from class zzz. So, what is the use of inheritance ? We have a number of development tools that are written using C++. The designers of these tools give us a variety of classes to ease our load. These classes contain complex code that automatically performs many useful tasks. We may derive our own class from such classes, and make use of all their code. In the case of complex tasks that are performed repetitively, this method goes a long way in reducing the amount of code that we have to write. We always derive a class from the original class, because we do not, under any circumstances, want to change the original class. We are, however, free to override any function present in the original class, simply by placing the name of that function within our class, and writing our own code within it.
Now let's look at how memory is allocated in C++, to objects. One unique feature of C++, is that anything and everything is either a class, or an object, which in turn is nothing but an instance of a class. When we have to create an instance of class zzz, don't we just use the statement ' zzz a; ' ?
When this statement is executed, an object 'a' is created that looks like class zzz. Now, look at the statement we use, when we want to have an integer, called i, in our program. We simply say ' int i; '.
Now, isn't this very similar to the statement in which we create an object that looked like class zzz ?
So, we can safely come to the conclusion that 'int' is actually a class in C++, and the statement ' int i; ' actually leads to the creation of an object of that class. This object is also allocated some memory.
Similarly, all other variable types are also classes, in reality.
Consider the statement shown below.
int *j;
Does this statement also lead to the creation of an object? Actually, no. All that we have done is declare that j is a pointer to class ' int '. An object will only be created if we add the code
' j = new int; ' , after the above statement. It's now time to take a look at our next program.
cc1.cpp
#include<stdio.h>
main()
{
int *j;
j=new int;
printf("addr %p\n",j);
j=new int;
printf("addr %p",j);
}
We are creating two objects in this program. Both these objects are not only called by the same name 'j', but they are also instances of the same class, int. In case you expected this program to show errors, you will definitely be disappointed. We are permitted to create two instances of a class in a program, that have the same name. What the keyword 'new' does, is first find out the size of the class, in this case, class ' int '. It then allocates enough memory for this class by calling malloc. After all this only, is an object called j created. If new is called twice, memory will have to be allocated twice.
We now turn our attention to doing something similar, but with a class of our own.
cc2.cpp
#include<stdio.h>
class zzz
{
public:
void abc()
{
printf("hi");
}
};
main()
{
zzz *a;
a=new zzz;
printf("%p \n",a);
a=new zzz;
printf("%p \n",a);
}
Here, 'a' is a pointer to class zzz. Other than that, the execution of the program proceeds in exactly the same way as the previous program. All this only proves that ' int ' is actually a class, and is treated exactly the same way, as our own class is treated. Now consider the case where we have a class containing a function of the same name, as shown in the next program.
cc3.cpp
#include<stdio.h>
class zzz
{
public:
int i,j;
zzz()
{
printf("In function zzz within class zzz\n");
}
print()
{
printf("i=%d....j=%d\n",i,j);
}
};
main()
{
zzz a;
a.print();
}
We certainly expect this program to call the function print(), on execution. What we hadn't bargained for is an output that appears as shown below.
In function zzz within class zzz
i=635...j=483
The function print may output any vague value, as we haven't initialized variables i and j. What amazes us, is the fact that function zzz has also been called. Nowhere in the above program have we made the statement' a.zzz(); '. If we haven't explicitly called a function from the class, how has function zzz been called ?
And can we call this function in the normal manner, by just saying ' a.zzz(); ' ?
cc4.cpp
#include<stdio.h>
class zzz
{
public:
int i,j;
zzz()
{
printf("In function zzz within class zzz\n");
}
print()
{
printf("i=%d....j=%d\n",i,j);
}
};
main()
{
zzz a;
a.zzz();
}
To our amazement, we get an error saying that zzz is not a member of class zzz. Can't the C++ compiler see that this function is present within class zzz. In fact, in the previous program itself, the very same function had been called. This is crazy! When we actually try to call this function, we just cannot call it, but when we don't call it, it get's called automatically!
Aah! Another unique feature of C++. As soon as we create the object of a class, a function within the class, having the same name as that of the class, will automatically be called. However, C++ doesn't take it very kindly, if we try to call this function explicitly. As far as it is concerned, this is an act of aggression, and an invasion of it's own turf. Permission to call a function that has the same name as that of the class it belongs to is denied to one and all. But, perhaps after relenting a bit, C++ gives us this function anyway, even if we are not really interested in calling it. As soon as the object is created, this function get called automatically.
Such a function is known as a constructor.
In most other respects, a constructor behaves like other functions in C++, and exhibits overloading. This will be demonstrated in the next program.
cc5.cpp
#include<stdio.h>
class zzz
{
public:
int i,j;
zzz()
{
printf("In constructor with no parameters\n");
}
zzz(int)
{
printf("In constructor with one parameter\n");
}
};
main()
{
zzz a;
zzz b(10);
}
OUTPUT:
In constructor with no parameters In constructor with one parameter
The two constructors get called, as expected. Thus, we can even have constructors with any number of parameters. In C++, which is a language that exhibits very strong type checking, correct return values are of utmost importance. So what are the return values of a constructor ? Let us experiment by placing various return values before the constructor, starting with ' int '.
cc6.cpp
#include<stdio.h>
class zzz
{
public:
int i,j;
int zzz()
{
printf("In constructor with no parameters\n");
}
};
main()
{
zzz a;
}
Error ! Error ! 'Constructor can't have a return type specification' , the C++ compiler informs us. What if we make the return value of this function, a ' void '. After all, the constructor does appear to return no value. No luck there, either. Same error. Basically, a function is called from main() conventionally, by giving the name of the function, and terminating the statement with a semicolon. It is this very statement, that accepts a return value. A constructor, however, is called automatically whenever an object is created. Which means that there is nothing that can accept a return value. That is why we get an error.
If we look back, we shall realize that, in our earlier programs, we didn't have a function of the same name as the class. So, we wonder, what exactly used to happen in those programs? Was an object being created, without a constructor being called ? That would be a violation of the rules of C++. Which is why, when we create an object, C++, on it's own, inserts a constructor without any parameters and containing no code, into the class. If that is the case, then maybe, we can just delete the constructor without parameters. After all, who doesn't like to accept anything given free ?
cc7.cpp
#include<stdio.h>
class zzz
{
public:
int i,j;
zzz(int)
{
printf("In constructor with one parameter\n");
}
};
main()
{
zzz a;
zzz b(10);
}
Unfortunately, C++ has come to the conclusion that, since we are rich enough to afford our own constructor with one parameter, we do not need it's charity. It is, now, not going to insert a free constructor without parameters. So, it now gives us an error saying that it cannot find a match for this constructor. We are therefore forced to insert this constructor, ourselves.
In an earlier program, our class contained variables that weren't initialized. Let us now initialize these variables, and print their values.
cc8.cpp
#include<stdio.h>
class zzz
{
public:
int i,j;
i=10;
j=20;
zzz()
{
printf("In Constructor\n");
}
print()
{
printf("i=%d....j=%d\n",i,j);
}
};
main()
{
zzz a;
a.print();
}
To our great horror, we get errors like 'Type name expected'
'Multiple declaration for i' 'Cannot initialize a class member here'
Of course, these errors are repeated for the second variable. The third error causes to ponder for a moment. If we cannot initialize these variables here, then maybe we should just try elsewhere. Such as, within the constructor.
cc9.cpp
#include<stdio.h>
class zzz
{
public:
int i,j;
zzz()
{
i=10;
j=20;
printf("In Constructor\n");
}
print()
{
printf("i=%d....j=%d\n",i,j);
}
};
main()
{
zzz a;
a.print();
}
OUTPUT:
In Constructor i=10....j=20
This program, more than any other, brings out the necessity of having constructors. C++ does not allow us to directly initialize a variable in a class, anywhere else. We have to initialize them only within a constructor.
th1.cpp
#include<stdio.h>
class zzz
{
public:
int i,j;
zzz()
{
printf("In constructor \n");
}
};
main()
{
zzz *a;
a=new zzz;
printf(" Address of a..%p",a);
}
In the above program, we have a class with a single constructor. It is only when the 'new' operator works on the class, that an object of the class is created. Along with the object being created, 'new' performs a few more tasks. It makes sure that the variables are placed in one section of memory, while the functions are placed in a different section. At the instant of the creation of the object, the constructor gets called automatically. Here, we have printed the address of where this object is placed in memory. Now, the object should be placed in memory, at exactly the same location, as one of the variables.
In the next stage, we have created one more object 'b'. We have also initialized the variables i and j, in the constructor. The last change we made, was to incorporate a function called print within our class, to display these variables.
th2.cpp
#include<stdio.h>
class zzz
{
public:
int i,j;
zzz()
{
i=0;
j=0;
printf("In constructor \n");
}
void print()
{
printf("i=%d....j=%d\n",i,j);
}
};
main()
{
zzz *a;
a=new zzz;
printf("%p...address of a\n",a);
a->print();
zzz *b;
b=new zzz;
printf("%p...address of b\n",b);
b->print();
}
Nothing much different happens in this program. The function, print, displays the values of i and j, which are 0, at all times. Of course, the objects, 'a' and 'b' will be assigned different memory addresses.
Let us now have a function that will change these values of i and j.
th3.cpp
#include<stdio.h>
class zzz
{
public:
int i,j;
zzz()
{
i=0;
j=0;
printf("In constructor \n");
}
void print()
{
printf("i=%d....j=%d\n",i,j);
}
void change(int x, int y)
{
i=x; j=y;
}
};
main()
{
zzz *a;
a=new zzz;
a->change(10,20);
zzz *b;
b=new zzz;
b->change(1,2);
a->print();
b->print();
}
The function, change(), will assign whatever values that have been passed to it to the variables i and j. Thus in our output, we shall see i=10..j=20, followed by i=1..j=2. These values have been displayed by the function, print(). But let's take a deeper look into what's really going on in this program. When any function, irrespective of whether it is in C or C++, gets called, it doesn't have the faintest idea as to who is calling it. All that the function does, is to take a look at the stack, and pick up the required parameters. If that is the case, then how does print() manage to display only the values that belong to the particular object that calls it?
We now do something that seems to be totally irrational. Within the constructor, we attempt to display the address of something called 'this'. We also display the addresses of both the objects.
th4.cpp
#include<stdio.h>
class zzz
{
public:
int i,j;
zzz()
{
i=0; j=0;
printf("In constructor this=%p \n",this);
}
void print()
{
printf("i=%d....j=%d\n",i,j);
}
void change(int x, int y)
{
i=x; j=y;
}
};
main()
{
zzz *a;
a=new zzz;
printf("address of a %p\n",a);
a->change(10,20);
a->print();
zzz *b;
b=new zzz;
printf("address of b %p\n",b);
b->change(1,2);
b->print();
}
We do not get any errors when we compile our program. That could only mean one thing. A variable called 'this' is actually exists. But, where have we created the variable in question?
As far as our program is concerned, we have only four options as to where we can create variables. We can do it within the round brackets following the name of the function, or within the curly braces that follow. We may also create the variable just after public: within the class, or declare it globally. It is quite evident that we have not used any of these options while creating the variable 'this'.
When we write the word 'this', to our surprise the colour of the word changes. A clear indication that the term 'this', is infact a reserved word that actually has some meaning in C++. A bigger surprise, however, awaits us. Let us look at the output of this program.
In constructor this=0B4D:0004 address of a 0B4D:0004 i=10....j=20 In constructor this=0B4E:0004 address of b 0B4E:0004 i=1....j=2
We notice that each time an object gets created, and the corresponding constructor gets called, the address of the object is exactly the same as that of the term 'this'. So what is 'this' ? Haven't we ever told you what a great language C++ is ? It's about time that we did. C++ creates a pointer to the current class and places it in the constructor, as the first parameter of the constructor. A real freebee. The designers of C++ however, were probably running short of names. So they named the pointer to the current class 'this'. The constructor of class zzz now looks like, internally.
zzz( zzz *this)
{
this->i=0; this->j=0;
printf("In constructor this=%p \n",this);
}
C++ can now easily make use of the pointer to access the variables passed by the object. While we cannot physically place ' zzz *this ' as a parameter, nothing prevents us from using this->i and this->j in place of variables i and j.
th5.cpp
#include<stdio.h>
class zzz
{
public:
int i,j;
zzz()
{
this->i=0;
this->j=0;
printf("In constructor this=%p \n",this);
}
void print()
{
printf("i=%d....j=%d\n",this->i,this->j);
}
void change(int x, int y)
{
i=x; j=y;
}
};
main()
{
zzz *a;
a=new zzz;
printf("address of a %p\n",a);
a->change(10,20);
zzz *b;
b=new zzz;
printf("address of b %p\n",b);
b->change(1,2);
a->print();
b->print();
}
In the function, print(), we have actually displayed the value of this->i and this->j, instead of merely displaying just i and j. We notice that there is absolutely no change in the output, as compared to the previous program. The explanation for this is quite simple. Whenever C++ calls a function, the function gets called with an extra parameter. The extra parameter is called the ' this ' pointer, which points to where the the variables are located in memory. Now, if C++ came across the variable i, standing all alone, it replaced it with the term ' this->i ', internally. Similarly, all other functions in a class also have the same extra parameter called the ' this ' pointer. In fact, when a function in C++ is called with, for example, two parameters, we assume that just these two parameters go on the stack, as shown in fig a.

In reality, an additional parameter also goes onto the stack. This additional parameter contains the address, where the object is located in memory. This is why the function, print, when called by the object 'a', will display only those values of the variables, initialized from that object only. In the figure, we have assumed that the object 'a' is at memory location 100, while
'b' is at 200. We also assumed that our stack is at 1000, in memory. We can observe that the contents of memory location 1000, are pointing to the location of the object 'a' in memory. This is also the point where variables i and j are stored.
It's time to look back at what we have learnt about constructors and inheritance. Here, we have a very simple program with two classes. We have named the constructors according to the name of their class as well as their parameters. For example, the constructors of class zzz have been called 'zzz void', 'zzz one', ' zzz two'... depending on whether they have they have no parameters, one parameter, two parameters, and so on. The second class, yyy, contains only a single constructor.
ci1.cpp
#include<stdio.h>
class zzz
{
public:
int i,j;
zzz()
{
i=0; j=0;
printf("In zzz void\n");
}
zzz(int x)
{
i=x; j=0;
printf("In zzz one\n");
}
zzz(int x, int y)
{
i=x; j=y;
printf("In zzz two\n");
}
print()
{
printf("From print i=%d...j=%d\n",i,j);
}
};
class yyy
{
public:
yyy()
{
printf("In yyy void\n");
}
};
main()
{
zzz a;
a.print();
zzz b(1);
b.print();
zzz c(2,3);
c.print();
yyy d;
}
The output of this simple program is as follows:
In zzz void From print i=0...j=0 In zzz one From print i=1...j=0 In zzz two From print i=2...j=3 In yyy void
The constructors are called in the expected sequence. At this point in time, we cannot make use of statements like 'yyy e(4);' or 'd.print();' , as the matching constructors or functions do not exist. We now make a minor change to our program, in the sense that we have now replaced 'class yyy' by' class yyy: public zzz', at the beginning of the second class. This seemingly minor change will, however, result in pretty major changes in the working of the entire program. It's not merely the fact that the object 'd' may now make use of all variables and functions present in class zzz. It is much more than that. Because, the statement 'yyy d;' does not result in the creation of just one object of class yyy, as in the earlier program. What this statement results in, is the creation of two objects. Proving this statement, is precisely what we shall be doing, in the next program.
ci2.cpp
#include<stdio.h>
class zzz
{
public:
int i,j;
zzz()
{
i=0; j=0;
printf("In zzz void\n");
}
print()
{
printf("From print i=%d...j=%d\n",i,j);
}
};
class yyy:public zzz
{
public:
yyy()
{
printf("In yyy void\n");
}
};
main()
{
yyy d;
d.print();
}
OUTPUT:
In zzz void In yyy void From print i=0...j=0
As we may observe, the output of this program shows that two void constructors have been called. We also observe that, in sequence, the base void constructor has been called before the void constructor of the derived class. Of course, only when an object of a class is created, can the constructor be called. Which only goes to prove that, when we execute the above program, two objects are created. Now, we shall try to obtain similar results using constructors with one parameter. In both classes, only the constructors with one parameter are retained, with everything else being deleted.
ci3.cpp
#include<stdio.h>
class zzz
{
public:
int i,j;
zzz(int x)
{
i=x; j=0;
printf("In zzz one\n");
}
print()
{
printf("From print i=%d...j=%d\n",i,j);
}
};
class yyy:public zzz
{
public:
yyy(int p)
{
printf("In yyy one\n");
}
};
main()
{
yyy d(5);
d.print();
}
Unfortunately, we now get an error saying that C++ cannot find the default constructor to initialize base class zzz. In other words, while the correct constructor with a single parameter of class yyy has been called, the constructor with one parameter in class zzz hasn't been called. So, why did this happen ? Is it because some other constructor is the default constructor, which has to be called from the base class ? The only way to find out, is to experiment. Let us place three constructors in class zzz.
ci4.cpp
#include<stdio.h>
class zzz
{
public:
int i,j;
zzz()
{
i=0; j=0;
printf("In zzz void\n");
}
zzz(int x)
{
i=x; j=0;
printf("In zzz one\n");
}
zzz(int x, int y)
{
i=x; j=y;
printf("In zzz two\n");
}
print()
{
printf("From print i=%d...j=%d\n",i,j);
}
};
class yyy:public zzz
{
public:
yyy(int p)
{
printf("In yyy one\n");
}
};
main()
{
yyy d(5);
d.print();
}
OUTPUT:
In zzz void In yyy one From print i=0...j=0
Before we can jump to conclusions, let us take one more example. Here, we have only the constructor with two parameters in class yyy.
ci5.cpp
#include<stdio.h>
class zzz
{
public:
int i, j;
zzz()
{
i=0; j=0;
printf("In zzz void\n");
}
zzz(int x)
{
i=x; j=0;
printf("In zzz one\n");
}
zzz(int x, int y)
{
i=x; j=y;
printf("In zzz two\n");
}
print()
{
printf("From print i=%d...j=%d\n",i,j);
}
};
class yyy:public zzz
{
public:
yyy(int p, int q)
{
i = p; j = q;
printf("In yyy two\n");
}
};
main()
{
yyy d(6,7);
d.print();
}
OUTPUT:
In zzz void In yyy two From print i=6...j=7
Once again, the first constructor to get called, is the zzz void constructor.Now, we can safely conclude that the default constructor, called from the base class, is always the constructor with no parameters. As you may have noticed, we have made an additional change, wherein we assigned the parameters of the 'yyy two' constructor to i and j, resulting in their values now being displayed as 6 and 7, respectively.
It's now time to look for answers as to why only the void constructor is called from the base class, and not the other constructors. When we derive class yyy from class zzz, at each and every constructor of yyy, C++ places information as to where to jump next. Consider the program that we just did. Class yyy actually appears, to C++, as shown below.
class yyy:public zzz
{
public:
yyy(int p, int q) : zzz()
{
i = p; j = q;
printf("In yyy two\n");
}
};
When C++ first creates an object of class yyy, it jumps to the appropriate constructor of class yyy, first. It also knows that since class yyy is derived from zzz, it has to jump to a constructor of class zzz, as well. In our previous programs, however, it finds no information at the constructor of yyy, as to where it should be going next. So, after the name of this constructor, it automatically goes and inserts a ' : ' followed by the name of the default constructor in the base class, zzz( ). This is the location, where C++ has to jump, next. When it reaches zzz( ), the statements within this constructor are executed. After this, C++ jumps back to the original constructor within the derived class, that is yyy(int p, int q), which is now executed. Therefore, in the output, we see that the void constructor of the base class is called before the constructor of the derived class. After all that jumping around, your head might be spinning around. In that case, take a break, because, when we come back, we've got even more jumping to do.
If C++ could tell the constructor of the derived class where to go, there's nothing that prevents us from doing the same. All we have to do is place the name of the desired constructor of the base class preceeded by a ' : ' , after the name of the constructor in the derived class. For example, if we now wish to go to the constructor with one parameter of the base class, from yyy(int p, int q), we merely replace
yyy(int p, int q) by yyy(int p, int q) : zzz( p).
ci6.cpp
#include<stdio.h>
class zzz
{
public:
int i,j;
zzz()
{
i=0; j=0;
printf("In zzz void\n");
}
zzz(int x)
{
i=x; j=0;
printf("In zzz one\n");
}
zzz(int x, int y)
{
i=x; j=y;
printf("In zzz two\n");
}
print()
{
printf("From print i=%d...j=%d\n",i,j);
}
};
class yyy : public zzz
{
public:
yyy(int p, int q) : zzz(p)
{ i=p; j=q;
printf("In yyy two\n");
}
};
main()
{
yyy d(8,9);
d.print();
}
OUTPUT:
In zzz one In yyy two From print i=8...j=9
In this program, we have managed to avoid calling the default constructor of the base class. Instead, the constructor of class zzz that we specified, has been called. After the name of the constructor of yyy, if we had, instead placed ' : zzz(2,3) ', obviously, the constructor of zzz that will be called, is the one with two parameters, zzz(int i, int j).
In case we get tired of working with only the yyy constructor having two parameters, we could shift to doing something similar with all the yyy constructors.
ci7.cpp
#include<stdio.h>
class zzz
{
public:
int i,j;
zzz()
{
i=0; j=0;
printf("In zzz void\n");
}
zzz(int x)
{
i=x; j=0;
printf("In zzz one\n");
}
zzz(int x, int y)
{
i=x; j=y;
printf("In zzz two\n");
}
print()
{
printf("From print i=%d...j=%d\n",i,j);
}
};
class yyy:public zzz
{
public:
yyy(): zzz(7)
{
printf("In yyy void\n");
}
yyy(int p):zzz(2,3)
{
i=p;
printf("In yyy one\n");
}
yyy(int p,int q)
{
i=p; j=q;
printf("In yyy two\n");
}
};
main()
{
yyy d;
d.print();
yyy e(8);
e.print();
yyy f(4,5);
f.print();
}
OUTPUT:
In zzz one In yyy void From print i=7...j=0 In zzz two In yyy one From print i=8...j=3 In zzz void In yyy two From print i=4...j=5
The output only confirms what we have being saying. As you may observe, everytime we try to create an object of a derived class, the constructor of the base class that we specified executes, first. Following this, C++ executes the constructor of the derived class. In case we hadn't specified the name of the constructor of the base class that has to be called, C++ will automatically call the default constructor, which is the void constructor, zzz().
A minor deviation from our current topic is now called for. We have a C program with a pointer to an int and a pointer to a char. Are we able to equate them to each other ?
ci8.c : ci8.cpp:
main() main()
{ {
int *i; int *i;
char *j; char *j;
i=j; i=j;
} }
When the C program compiles, it does not give us any errors. In other words, C allows us to equate pointers to two different datatypes. We do not, however, have the same kind of luck with C++. C++ will just stop compiling, and tell us that we cannot convert char* to int*. Makes sense to us, at least. After all, can we convert apples to oranges ? Even if the answer to that question is no, it would not be for lack of trying, on our part.
In the program that follows, we have two classes. We have merely created pointers to these two classes. We now want to know whether C++ allows us to equate the two pointers.
ci9.cpp
class zzz
{
public:
int i,j;
};
class yyy
{
public:
int k;
};
main()
{
zzz *a;
yyy *b;
a=b;
}
Error ! Error! C++ is quite clear about one thing. Here, ' a ' is a pointer to one class, while ' b ' is a pointer to a totally different class. We just cannot equate them. We therefore get an error as follows : Cannot convert yyy* to zzz* . If we have the line ' b = a; ' instead of ' a = b; ' , the error would be ' Cannot convert zzz* to yyy* '. This is not too different from trying to equate a pointer to an int with a pointer to a char. After all, even 'int' and 'char' are classes in C++. But what happens if we derive class yyy from class zzz ?
ci10.cpp
class zzz
{
public:
int i,j;
};
class yyy : public zzz
{
public:
int k;
};
main()
{
zzz *a;
yyy *b;
a=b;
}
Yeah ! The error has totally vanished. So we can, after all, equate pointers to different classes, if one class is derived from the other. If we could say ' a = b; ', we could also have ' b = a; ', which is what we shall try in the next program.
ci11.cpp
class zzz
{
public:
int i,j;
};
class yyy : public zzz
{
public:
int k;
};
main()
{
zzz *a;
yyy *b;
b=a;
}
To our utter surprise, we still get the error, ' Cannot convert zzz* to yyy* '. We now have to analyse exactly what went wrong with this program. When we have the line ' a = b; ', we have to understand that, ' a ' and ' b ' actually represent two classes. Therefore on the right hand side of this statement, we have a class yyy, while the left hand side represents class zzz. Since class yyy is derived from class zzz, ' b ' represents a combination of both class zzz as well as class yyy, while ' a ' represents just class zzz. In other words, ' b ' is the bigger class. This is as shown in the figure.
Thus the pointer to the right in the line ' a = b; ', points to a greater content than the pointer to the left of the equal-to sign. The pointer on the left will say that it only needs a ' zzz ' on the right hand side. If you, however, want to give it more than it really needs, it will, quite happily, let you know that it has no problems, what-so-ever. After all, if I expect a salary of only a thousand dollars per month, but you are willing to give me two thousand five hundred, would I be complaining ?
But if I estimated my own worth at two thousand five hundred dollars, and you are offering me a mere thousand, I don't think I would be interested. Similarly, we cannot give ' b ' anything less than what it expects. When we use ' b = a;', ' b ' demands nothing less than the combination of class zzz, as well as class yyy on the right hand side of the equal-to sign. But, in this statement, we are merely giving ' b ', a class zzz. It will definitely come back and say 'ERROR!'. Rules are rules. So, is this the end of the road. No, never. Didn't we mention something earlier about breaking the rules ? Something called casting.
ci12.cpp
class zzz
{
public:
int i,j;
};
class yyy : public zzz
{
public:
int k;
};
main()
{
zzz *a;
yyy *b;
b=(yyy *)a;
}
By casting, all that we have done is inform ' b ' that ' a ' is now of type 'pointer to class yyy'. We are not, however, changing ' a ' in any way. Since ' b ' had been expecting exactly such a thing to be present on the right hand side of the equal-to sign, this program compiles without any problems.
Let's summarise what we have done in the last four programs. A pointer to a derived class can only be on the right of an equal-to sign, while the pointer to the base class can only be on the left. After all, the rules state that a bigger class can be assigned to a smaller class, but a smaller class can't be assigned to a bigger class. The only way to break these rules is by casting.
Memoirs of a Variable
Consider the following C program
st1.c:
main()
{
int i;
for(i=0;i<=10;i++)
abc();
}
abc()
{
int k=5;
k++;
printf("%d\n",k);
}
OUTPUT:
6 6 6 6 6 6 6 6 6 6 6
In this program, when the function abc is called for the first time, in the for loop, k is initialised to 5. As k is incremented by one, the output is 6, for this iteration of the loop. The next time abc is called, k is once again initialised to 5. This results in the program displaying a stream of the number, 6. Such an output may, however, not be the kind of output that we wish to have in certain applications. There might be cases where we want our program to recall the previous value of a particular variable. In such cases, we make use of the keyword 'static'.
st2.c:
main()
{
int i;
for(i=0;i<=10;i++)
abc();
}
abc()
{
static int k=5;
k++;
printf("%d\n",k);
}
OUTPUT:
6 7 8 9 10 11 12 13 14 15 16
The only change in this program is that k has now been declared to be static. The first time function abc is called, k, as usual is initialised to 5. The value of k now becomes 6 when we say k++. This is the output, the first time function abc is called. However, now that the variable k has been declared to be static, the next time function abc is called, C does not look at the line ' k=5; ' at all. Instead, the last value of the variable, which is 6, is remembered. This value is now assigned to k. This value is incremented, and displayed as 7. Thus, when the entire program executes, we see the value of k increasing, each time it is displayed. Placing the term 'static' before a variable, ensures that the variable recollects the value it held, the last time it was accessed.
A static variable behaves like a global variable, with a minor difference. It doesn't have the visibility of a global variable. Had k been a global variable, it would have been visible within main() as well as with the function abc. However, a static variable is visible only within the function in which it has been declared.
It's probably time we got back to C++ programming. The program that follows, is similar to examples that we have done before.
st3.cpp:
#include<stdio.h>
class zzz
{
public:
int i;
zzz()
{
i=6;
printf("Addr of i in constr: %p..\n",&i);
}
void print()
{
printf("In print: %d....%p\n",i,&i);
}
};
main()
{
zzz a;
a.print();
zzz b;
b.print();
zzz c;
c.print();
}
OUTPUT:
Addr of i in constr: 0A02:0FFE.. In print: 6....0A02:0FFE Addr of i in constr: 0A02:0FFC.. In print: 6....0A02:0FFC Addr of i in constr: 0A02:0FFA.. In print: 6....0A02:0FFA
We have a class zzz, with a variable i, a single constructor, and a function, print(). The variable i has been initialised to 6 in the constructor of this class. When we create an object 'a' of this class, C++ will allocate some memory for this object, and also call the constructor. Within the constructor, we have printed the address of the variable i.
When we use terms such as a.print() or a.i, what we mean is that the variable i, or the function print(), belong solely to the object a. The line 'a.print();' in our program, displays the value as well as the address of the variable 'i'. Since it is the object 'a' that is calling print(), effectively, what is being displayed, is 'a.i', that is, the variable i, whose owner is the object 'a'.
In this program, we have created two additional objects of the class zzz. These are 'b' and 'c'. We notice that each time the constructor is called, a different address will be displayed for i. Furthermore, the address in the constructor will match the address that is displayed every time the function print() is called by the object. Thus, we may safely state that the variable i belongs to the object that is calling it. Although this variable has been declared as well as initialized within class zzz, it does not belong to the class. In order to further prove this point, we introduce a minor variation in our previous program.
st4.cpp:
#include<stdio.h>
class zzz
{
public:
int i;
zzz()
{
i=6;
printf("Addr of i in constr: %p..\n",&i);
}
void print()
{
printf("In print: %d....%p\n",i,&i);
}
};
main()
{
zzz a;
a.i++;
a.print();
zzz b;
b.print();
zzz c;
c.print();
}
OUTPUT:
Addr of i in constr: 111C:0FFE.. In print: 7....111C:0FFE Addr of i in constr: 111C:0FFC.. In print: 6....111C:0FFC Addr of i in constr: 111C:0FFA.. In print: 6....111C:0FFA
In the above program, we have added the line ' a.i++; '. Due to this, the value of i displayed is now 7, when ' a.print(); ' is executed. In the program, the function print is also called, by both, the object 'b', as well as the object 'c'. This function will display the same variable i, that has been declared within the class. However, as we stated before, even though i is present in the class zzz, it belongs exclusively to the object that is accessing it. The i of one object, has nothing to do with the i of another object, even though both objects may be of the same class. This is why, when print is called by 'b' and 'c', the output is 6, the same as in the previous program.
The whole thing reminds me of the tube of toothpaste, at home. Although within an apartment, it doesn't belong to the apartment, itself. It belongs to, and is used by people, who are members of the family that reside in the apartment.
Is it possible for a variable present within a class to actually belong to a class? If such a thing were to happen, the address of the variable would not change everytime an object to the class was created.
st5.cpp:
#include<stdio.h>
class zzz
{
public:
static int i;
zzz()
{
printf(" In constr i=%d",i);
}
};
main()
{
zzz a;
printf("%d\n",a.i);
}
The variable in the above program has been declared to be static. We have created an object of the class zzz called 'a', as we normally do. Within main(), we do not try to print i, standing all alone. Since 'i' is associated with the object of the class, we normally print 'a.i'.
When we try to run this program, we get a linker error saying 'Undefined symbol zzz::i ....'.
The problem with making a variable static, is that we have to initialize the variable outside the class. To denote that we are initializing the variable i, that only belongs to class zzz, we make use of the '::' operator. At the end of the class zzz in our program, we include the line ' int zzz::i= 10; '. We cannot just say ' zzz::i=10; ', even though we have already stated that i is an 'int' within class zzz. Doing so, would give us an error saying 'type name expected'.
st6.cpp:
#include<stdio.h>
class zzz
{
public:
static int i;
zzz()
{
i++;
printf(" In constr i=%d",i);
}
};
int zzz::i=10;
main()
{
zzz a;
printf("In main i=%d\n",a.i);
}
OUTPUT:
In constr i=11 In main i=11
The program now executes, without any problems. At this point, however, some doubts rake through our mind. We have initialized the variable from outside the class, but to do so, we didn't make use of the object of the class. In other words, we did not say 'a.i=10;'. In that case, within main(), do we need to create an object at all, to access the variable i ?
st7.cpp:
#include<stdio.h>
class zzz
{
public:
static int i;
};
int zzz::i=10;
main()
{
printf("In main i=%d\n",zzz::i);
}
OUTPUT:
In main i=10
We have not created an object at all, in this program. A constructor will, therefore, never be called. But we have still managed to access a variable within a class, once again, by saying zzz::i
We shall now create three objects of the class zzz, and everytime the constructor is called, we shall display the value as well as the address of the variable i. Also, within the constructor, we have incremented the value of i.
st8.cpp:
#include<stdio.h>
class zzz
{
public:
static int i;
zzz()
{
i++;
printf("In constr i= %d...Addr=%p\n",i,&i);
}
};
int zzz::i=10;
main()
{
zzz a;
printf("a.i=%d\n",a.i);
zzz b;
printf("b.i=%d\n",b.i);
zzz c;
printf("c.i=%d\n",c.i);
}
OUTPUT:
In constr i=11..Addr=10D7:0094 a.i=11 In constr i=12..Addr=10D7:0094 b.i=12 In constr i=13..Addr=10D7:0094 c.i=13
We are definitely in for a surprise. In fact, the output is something we totally did not expect. Everytime we create an object and thereby call the constructor, the value of i is not reinitialized to 10. Instead, i retains the value it last held. Since we are incrementing i everytime the constuctor is called, we see an ever increasing value of i displayed. An even greater surprise is that, the address of i displayed, each time, is always the same. We therefore conclude that, by declaring i to be a static variable, i no longer belongs to the object of the class, but to the class, itself. This variable now remembers the value it held when it was last accessed.
st9.cpp:
#include<stdio.h>
class zzz
{
public:
static int i;
zzz()
{
i++;
printf("In constr i= %d..%p\n",i,&i);
}
void print()
{
printf("In print i= %d..%p\n",i,&i);
}
};
int zzz::i=10;
main()
{
printf("zzz::i= %d\n",zzz::i);
zzz a;
a.print();
zzz b;
b.print();
zzz c;
c.print();
}
OUTPUT:
zzz::i= 10 In constr i= 11..10D9:0094 In print i= 11..10D9:0094 In constr i= 12..10D9:0094 In print i= 12..10D9:0094 In constr i= 13..10D9:0094 In print i= 13..10D9:0094
In this program, we have only confirmed what we have been saying about static variables. We observe that, once again, the address displayed is always the same.
Time to recap what we have learnt about static variables, so far. To access a static variable, we do not need to create an object. This is why, to access i from class zzz, we may simply make use of ' zzz::i '. In other words, we can get such a variable absolutely free from a class, without taking all the trouble to create an object of the class. In fact, instead of calling such variables static, we should be calling them free. A static variable will always be owned by a class, and not by the objects of the class.
We may have an application which needs to know what the value of a variable in a class was, when it was last accessed. Based on this value, various options to proceed ahead, may be chosen. This variable may now have a new value assigned to it. The next time we access this variable, we may do so from a different object, but we still need to know what the variable's last value was. To do so, we make the variable 'static'.
It's not only variables that can be defined to be static. We may also have functions that are declared to be static. The rules remain the same. We don't have to create objects to access these functions. In fact, whole classes may be declared static. The whole idea is that, whatever is declared to be static, can be access absolutely free.
Extern ? Not Defined Here.
Time and time again, we fall back to revise our basics of the C programming language. It's only when these are strong, can we actually learn C++. Consider the following C program.
e1.c:
main()
{
printf("%p\n",&i);
abc();
}
Obviously, when we compile the above program, we shall get a compiler error saying 'undefined external symbol i...'. We shall now include the line 'extern int i;' at the start of the program, before main(). In other words, globally.
e2.c:
extern int i;
main()
{
printf("main..%p\n",&i);
abc();
}
We now merely compile this program, but do not link it. The compiler will not give us any errors. We cannot possibly link it because the function abc is not present within the progam. When we write the word 'extern' before a variable, we are saying that this variable has not been ceated here. Instead, it has been created somewhere else. So, somewhere else, is where we should be going.
e3.c:
int i;
abc()
{
printf("abc %p",&i);
}
This program contains the function abc and a variable i, but no main().Once again, we only compile the program, but do not build it. We get no errors. We now click on the word 'Project' in the toolbar at the top of our screen. We then select 'Open Project', and write the name of the project we wish to open. In this case, we have chosen 'e.prj'. At the bottom of our screen, a small window opens up, giving us the names of the files present in the project. Right now, there are none. So, we once again, click on Project. We now select 'AddItem' from the options. Here, we first write e2.c and click on 'add' , after which we write e3.c in the textbox, and once again click on 'add'. We now click on 'done' to indicate that our project is complete. When we now select Compile in the toolbar and then build, we get a file called e.exe. When we execute this function, the output is as follows.
main 10CA:0380 abc 10CA:0380..
As we can see, the addresses displayed by the two programs are exactly the same. This confirms that i had been defined in e3.c, and the same variable is also being used by e2.c because we had declared it to be 'extern'.
Virtually, All Your's
Throughout the ages, philosophers and great thinkers have always had one bad habit. They said things that no one understood. Many of them suggested that we are living in a virtual world, a world built around our own illusions. We never really understood exactly what they meant by this, but C++ has taken us right up to the edge of reality, using virtual functions.
Whenever we start a new topic, we always begin with a very simple program, often similar to one we had done earlier. We waste absolutely no time, and immediately get on to the first program in this topic.
vr1.cpp:
#include<stdio.h>
class zzz
{
public:
void abc(){printf("zzz abc\n");}
void pqr(){printf("zzz pqr\n");}
void xyz(){printf("zzz xyz\n");}
} ;
class yyy
{
public:
void abc(){printf("yyy abc\n");}
void pqr(){printf("yyy pqr\n");}
void aaa(){printf("yyy aaa\n");}
};
main()
{
zzz *a;
a=new zzz;
a->abc(); a->pqr(); a->xyz();
yyy *b;
b=new yyy;
b->abc(); b->pqr(); b->aaa();
}
OUTPUT:
zzz abc zzz pqr zzz xyz yyy abc yyy pqr yyy aaa
The explanation of this program is simple enough. We have two classes, each containing three functions. The function, xyz, is present only in class zzz, while the function, aaa, is present only in class yyy. The other two functions, abc and pqr, are present in both classes. Here, 'a' has been declared to be a pointer to class zzz, and 'b' is a pointer to class yyy. By saying ' a=new zzz;' and ' b=new yyy; ', we have created objects 'a' and 'b' of classes zzz and yyy, respectively. The sequence in which the functions are called in this program, is as shown in the output. We cannot say b->xyz(), or a->aaa(), because 'a' can only access those functions that are present in class zzz, while 'b' can only access those functions present in class yyy. Both 'a' and 'b' are two distinct objects. If we now derive class yyy from class zzz, we may use 'b' to access any function within zzz.
We now consider a case when we declare d to be a pointer to class zzz. Now, after deriving class yyy from class zzz, we say ' d = new yyy ', as opposed to saying ' d = new zzz '. It's time to investigate how this affects our program.
vr2.cpp:
#include<stdio.h>
class zzz
{
public:
void abc(){printf("zzz abc\n");}
void pqr(){printf("zzz pqr\n");}
void xyz(){printf("zzz xyz\n");}
} ;
class yyy : public zzz
{
public:
void abc(){printf("yyy abc\n");}
void pqr(){printf("yyy pqr\n");}
void aaa(){printf("yyy aaa\n");}
};
main()
{
zzz *d;
d=new yyy;
d->abc();
d->pqr();
d->xyz();
}
OUTPUT:
zzz abc zzz pqr zzz xyz
Class yyy is derived from class zzz. Therefore, the statement ' d = new yyy; ' is valid, in spite of the fact that 'd' is a pointer to class zzz. From the output, we find that abc and pqr are called from class zzz, and not from class yyy. In fact, all the functions that we have tried to call so far, belong to class zzz. What happens when we try to call a function that is present only in class yyy ?
vr3.cpp:
#include<stdio.h>
class zzz
{
public:
void abc(){printf("zzz abc\n");}
void pqr(){printf("zzz pqr\n");}
void xyz(){printf("zzz xyz\n");}
} ;
class yyy : public zzz
{
public:
void abc(){printf("yyy abc\n");}
void pqr(){printf("yyy pqr\n");}
void aaa(){printf("yyy aaa\n");}
};
main()
{
zzz *d;
d=new yyy;
d->abc();
d->pqr();
d->xyz();
d->aaa();
}
Error! Function aaa is not a member of class zzz. The same philosophers that caused us so many problems before, now come to our rescue. They always said that man should learn from his mistakes. Which is why we are not too disappointed with such a small error. We realise that with 'd', we are only able to access the functions that are present in class zzz. The right hand side of the statement ' d = new yyy; '
will return a pointer to both, class zzz as well as class yyy, as yyy is derived from zzz.

However, 'd' has been defined to be a pointer to class zzz only. So, despite the fact that the functions of both zzz as well as yyy are present in memory location which 'd' points to, the compiler will not allow 'd' to access any function that is in class yyy. Whenever we try to call a function using 'd', 'd' will immediately check what 'd' points to. Since it points to class zzz, only those functions in zzz can be called.
We would not have had any of these problems, had 'd' just been declared a pointer to class yyy. It would have then been able to access the functions of both yyy as well as zzz. But in our program, this is not the case.
In the next program, we have made only a single change, from vr2.cpp, in the sense that we have added the word 'virtual' in front of the function abc(), in class zzz. As soon as we write this word, it's colour changes on the screen, indicating that it is a reserved word, one that C++ recognises. The effect of this one word can be witnessed in the next program.
vr4.cpp:
#include<stdio.h>
class zzz
{
public:
virtual void abc(){printf("zzz abc\n");}
void pqr(){printf("zzz pqr\n");}
void xyz(){printf("zzz xyz\n");}
} ;
class yyy : public zzz
{
public:
void abc(){printf("yyy abc\n");}
void pqr(){printf("yyy pqr\n");}
void aaa(){printf("yyy aaa\n");}
};
main()
{
zzz *d;
d=new yyy;
d->abc();
d->pqr();
d->xyz();
}
OUTPUT:
yyy abc zzz pqr zzz xyz
Is it time to junk all that we said about C++ not allowing 'd' to call any function that is not present in the class, it has been defined to point to? Because, in our output, we see that abc has now been called from class yyy, the derived class.
Here, 'd' is a pointer to class zzz. That is why, when we say 'd->abc();' , 'd' will first go to the function abc() in class zzz. If abc() in class zzz is not virtual, as in vr2.cpp, this is the function that is called. But if it is virtual, 'd' will look at what it was initialised to. In our program, by the statement
' d = new yyy; ' , we have initialised 'd' to be an object of class yyy. Because it was initialised to yyy,
'd' will call function abc() that is in class yyy. Putting the term virtual in front of abc in class yyy will not make any change in the resulting output of this program. As long as abc in the base class is virtual, abc will still be called from class yyy. But what happens if abc is virtual only in yyy, and not in zzz ?
vr5.cpp:
#include<stdio.h>
class zzz
{
public:
void abc(){printf("zzz abc\n");}
virtual void pqr(){printf("zzz pqr\n");}
virtual void xyz(){printf("zzz xyz\n");}
} ;
class yyy : public zzz
{
public:
virtual void abc(){printf("yyy abc\n");}
void pqr(){printf("yyy pqr\n");}
void aaa(){printf("yyy aaa\n");}
};
main()
{
zzz *d;
d=new yyy;
d->abc();
d->pqr();
d->xyz();
}
OUTPUT:
zzz abc yyy pqr zzz xyz
As we mentioned earlier, 'd' first looks at what it has been defined as. Since 'd' has been defined as a pointer to class zzz, it will first go there to look for function abc(). Since the term is not present in front of abc, the function is called from here itself. Putting 'virtual' in front of abc in class yyy has absolutely no effect.
We have also made functions pqr and xyz virtual in class zzz. In the case of pqr, as in program vr4.cpp, it is called from class yyy. However, the function xyz is not present in class yyy. So even if it wanted to, C++ cannot call function xyz from class yyy. It simply moves on to the next best alternative and calls xyz from class zzz.
We have to realise that we cannot have statements like
yyy *e; e = new zzz;
We have to remember our earlier theory of smaller and bigger classes. Since the derived class contains all it's own functions, as well as the functions present in the base class, the derived class has to be larger. As we have defined 'e' to be a pointer to class yyy, if 'e' has to be equated to something present on the right of an equal-to sign, it demands a yyy, or something derived from yyy. As we are merely giving 'e', a zzz on the right hand side of the equal-to sign, in the second statement, it will definitely give us an error
In our next program, we define 'p' to be pointer to class zzz, as we have done in the previous examples. We, however, now define an additional class, xxx, that is derived from class yyy, and initialise 'p' to be an object of class xxx.
vr6.cpp:
#include<stdio.h>
class zzz
{
public:
void abc(){printf("zzz abc\n");}
void pqr(){printf("zzz pqr\n");}
void xyz(){printf("zzz xyz\n");}
} ;
class yyy : public zzz
{
public:
void abc(){printf("yyy abc\n");}
void pqr(){printf("yyy pqr\n");}
void aaa(){printf("yyy aaa\n");}
};
class xxx:public yyy
{
public:
void abc(){printf("xxx abc\n");}
void bbb(){printf("xxx xxx aaa\n");}
};bbb\n");}
void aaa(){printf("
main()
{
zzz *f;
f=new xxx;
f->abc();
f->pqr();
f->xyz();
}
OUTPUT:
zzz abc zzz pqr zzz xyz
In this program, 'p' has been initialised to be an object of class xxx. Therefore, in memory,
'p' points to class xxx, class yyy as well as class zzz.

But, since 'p' has been defined as a pointer to class zzz, it does not look beyond zzz. In fact, it cannot access any function from either class xxx or yyy. It can access only those functions that are in class zzz. These rules have already been discussed earlier. If we want 'p' to access any function present in either class xxx or class yyy, we have to make that function virtual in class zzz.
vr7.cpp:
#include<stdio.h>
class zzz
{
public:
virtual void abc(){printf("zzz abc\n");}
virtual void pqr(){printf("zzz pqr\n");}
virtual void xyz(){printf("zzz xyz\n");}
} ;
class yyy : public zzz
{
public:
void abc(){printf("yyy abc\n");}
void pqr(){printf("yyy pqr\n");}
void aaa(){printf("yyy aaa\n");}
};
class xxx:public yyy
{
public:
void abc(){printf("xxx abc\n");}
void bbb(){printf("xxx bbb\n");}
void aaa(){printf("xxx aaa\n");}
};
main()
{
zzz *f;
f=new xxx;
f->abc();
f->pqr();
f->xyz();
}
OUTPUT:
xxx abc yyy pqr zzz xyz
Here, functions abc, pqr, and xyz are all called from different classes. This, in no way, means that there has been any change in the rules that govern the calling of functions.
Function abc is present in all three classes. As 'p' is a pointer to class zzz, it will first look at class zzz. Since class abc is virtual, 'p' then looks at what it has been initialised to, that is, class xxx. This is where function abc will be called from. In the case of function pqr, when 'p' finds that it is virtual in class zzz, it then looks at class xxx. But this function is not present in class xxx. C++ then settles for the function, in the class yyy, that is, the class from which xxx is derived. In the case of function xyz, it is not present anywhere, except in class zzz. So this is the only place where it can be called from.
We thus notice that, if a function is virtual in the base class, in all the derived classes, it automatically becomes virtual. Also, the last occurance of the virtual function is being called, when we go from the base class, downwards. If we make all the functions in class yyy, virtual, there is absolutely no change in the output of the above program.
The complexities in all the above programs arise, because we defined a pointer to a smaller class, but created an object to a larger class.
In subsequent programs, we no longer define a pointer to class zzz, but instead, define a pointer to class yyy. As in the last program, we then initialise it to be an object of class xxx.
vr8.cpp:
#include<stdio.h>
class zzz
{
public:
void abc(){printf("zzz abc\n");}
void pqr(){printf("zzz pqr\n");}
void xyz(){printf("zzz xyz\n");}
} ;
class yyy : public zzz
{
public:
void abc(){printf("yyy abc\n");}
void pqr(){printf("yyy pqr\n");}
void aaa(){printf("yyy aaa\n");}
};
class xxx:public yyy
{
public:
void abc(){printf("xxx abc\n");}
void bbb(){printf("xxx bbb\n");}
void aaa(){printf("xxx aaa\n");}
};
main()
{
yyy *f;
f=new xxx;
f->abc();
f->pqr();
f->xyz();
f->aaa();
}
OUTPUT:
yyy abc yyy pqr zzz xyz yyy aaa
We are now able to call all the functions that are present in class yyy, as well as in class zzz. This is because class yyy is derived from class zzz, and we can call an additional function, xyz. As usual, these are all called from the class that 'f' is defined to point to, that is, class yyy. This is not the case for the function xyz. This function, being present in class zzz, is called from there.
We shall now make all the functions in class zzz 'virtual'.
vr9.cpp:
#include<stdio.h>
class zzz
{
public:
virtual void abc(){printf("zzz abc\n");}
virtual void pqr(){printf("zzz pqr\n");}
virtual void xyz(){printf("zzz xyz\n");}
} ;
class yyy : public zzz
{
public:
void abc(){printf("yyy abc\n");}
void pqr(){printf("yyy pqr\n");}
void aaa(){printf("yyy aaa\n");}
};
class xxx:public yyy
{
public:
void abc(){printf("xxx abc\n");}
void bbb(){printf("xxx bbb\n");}
void aaa(){printf("xxx aaa\n");}
};
main()
{
yyy *f;
f=new xxx;
f->abc();
f->pqr();
f->xyz();
f->aaa();
}
OUTPUT:
xxx abc yyy pqr zzz xyz yyy aaa
If we make a function virtual in the base class, it automatically becomes virtual in the derived class. Which is why function abc will be called from xxx. Also, only the last occurance of the function is called, when we go down, starting from the base class. This is why pqr gets called from class yyy. As xyz is present only in class zzz, this is where it is called from. We haven't, however, made function aaa, virtual. Therefore, 'f' will just look at what it has been initialised to, and pick up function aaa from class yyy. In the case of xyz, there is no other alternative, other than calling it from class zzz.
vr10.cpp:
#include<stdio.h>
class zzz
{
public:
void abc(){printf("zzz abc\n");}
void pqr(){printf("zzz pqr\n");}
void xyz(){printf("zzz xyz\n");}
} ;
class yyy : public zzz
{
public:
void abc(){printf("yyy abc\n");}
void pqr(){printf("yyy pqr\n");}
virtual void aaa(){printf("yyy aaa\n");}
};
class xxx:public yyy
{
public:
virtual void abc(){printf("xxx abc\n");}
void bbb(){printf("xxx bbb\n");}
void aaa(){printf("xxx aaa\n");}
};
main()
{
yyy *f;
f=new xxx;
f->abc();
f->pqr();
f->xyz();
f->aaa();
}
OUTPUT:
yyy abc yyy pqr zzz xyz xxx aaa
Once again, 'f' will first look at what it has been defined to point to. Since it points to class yyy, it calls function abc() from there. The fact that abc is virtual in class xxx, or that 'f' is actually an object of the class xxx, makes no difference. However, since aaa() is virtual in class yyy, 'f' looks at what class it has been initialised to. Since 'f' has been initialised to be an object of class xxx, function aaa is called from this class.
Virtual functions thus apply only when we have a pointer to a smaller class, but have initialised it to be an object of a larger class. Only the functions that are present in the class to which, the pointer has been defined to point to, can be called. However if the pointer sees the term 'virtual' in front of the function, it looks at the class, to which it has been initialised. In other words, it looks at which class, it is an object of. It will then call the function from this class. If this function is not present in the class to which it has to be initilised to, it call the function from the class it has been derived from.
When programmers speak, they often make use of terms that hardly make sense to us. They often stress the need to introduce modularity in the code we write. What this means, in simple language, is that they want us to break up the applications we write, into a number of programs, so that our final code is easy to debug. Breaking up programs is especially useful when it comes to adding more features in our application. Earlier, we had a simple project in C. We shall now do something similar in C++. C++ needs the prototype of every function that we call. For the sake of convenience, we put all these prototypes into a single file called the header file. In such a header file, we do not put any code. Ideally, even if we add a hundred header files into our project, the size of our project should not change, as they contain absolutely no code.
zzz.h:
#include<stdio.h>
class zzz
{
public:
void abc();
void pqr();
};
class yyy:public zzz
{
public:
void abc();
void pqr();
};
class xxx:public yyy
{
public:
void abc() ;
void pqr();
};
void aaa();
We first create a header file with three class prototypes. As before, we have derived class yyy from class zzz, and class xxx from class yyy. The functions, abc and pqr, are present in each and every class. In addition, we also have the prototype of a function, aaa(), within the header file.
We have given the actual code of these functions, abc and pqr, in a file called zzz.cpp. We, of course, have to distinguish between the function abc that is present in class zzz and the function of the same name present in class yyy or class xxx. If we wish to refer to a function belonging to a class, from outside the class, we use the double colon operator ' :: '. Thus the function, abc(), within class zzz, is referred to as zzz::abc(). By the look of it, the person who wrote C++ was either very fond of colons, or had run out of things to connect a function to it's class. All that we have done, is place a single line of code within each function. This will display which function is being called. At the top of this program, we have included a header file, but we haven't used the angle brackets, '<' and '>', as we usually do. Instead, we have made use of double quotes. By saying #include "zzz.h", we mean that zzz.h is a header file in our current directory.
zzz.cpp:
#include "zzz.h"
void zzz::abc()
{
printf("In zzz abc\n");
}
void zzz::pqr()
{
printf("In zzz pqr\n");
}
void yyy::abc()
{
printf("In yyy abc\n");
}
void yyy::pqr()
{
printf("In yyy pqr\n");
}
void xxx::abc()
{
printf("In xxx abc\n");
}
void xxx::pqr()
{
printf("In xxx pqr\n");
}
In the next file, z1.cpp, we have said that 'a' is a pointer to a class zzz. However, we have defined 'a' to be extern. This means that the actual definition of 'a' has been done in some other file. The file, z1.cpp, is the file that that contains the function, main(). We have initialised a to be an object of class zzz. We have also called a function, aaa(). The code of the function is not present in this file.
z1.cpp:
#include "zzz.h"
extern zzz *a;
main()
{
a= new zzz;
aaa();
}
The last file we have, is called z2.cpp. In this file we have the actual definiition of 'a', as well as the code of the function aaa(). The function aaa() simply contains a call to the function abc() by 'a'.
z2.cpp:
#include "zzz.h"
zzz *a;
void aaa()
{
a->abc();
}
We open a project, and add three files, zzz.cpp, z1.cpp and z2.cpp to our project, z.prj. The detailed explanation on how to go about this process has been explained in a previous example. In short, we click on Project, select OpenProject and give the name of the project. Once again, click on Project, then on AddItem, and give the names of our files, one by one. We finally click on 'Done'. We do not add the header file zzz.h into our project. The compilation and linking of the project will give us no errors.
Before we execute this seemingly complicated project, let us pause a moment to review what we have just written about. In the logical sequence of execution, main() will be called first. Now, main() knows that 'a' is a pointer to a class called zzz. We have now initialised 'a' to be an object of class zzz by saying ' a = new zzz '. We also have a call to a function aaa(). If we look at the code of function aaa(), within z2.cpp, we can see that it contains a call to the function abc(), by 'a'. As 'a' is a pointer to class zzz, function abc will be called from this class. Thus the output of our project will be as shown below.
OUTPUT:
In zzz abc
We now make a minor change to only one of our programs, namely z1.cpp. Here, we initialise 'a' to be an object of class yyy, instead of class zzz. Since class yyy is derived from class zzz, we are allowed to do such a thing.
z1.cpp:
#include "zzz.h"
extern zzz *a;
main()
{
a= new yyy;
aaa();
}
We save this file and recompile our project. We realise that there is absolutely no change in the output of our program. Function abc will still get called from class zzz. Because 'a' is defined as a pointer to class zzz, function abc will be called from this class only. The object 'a' only looks at what it points to. Thus, even if we initialise 'a' to class xxx, when this project executes, function abc is still called from class zzz.
But what if we place the term 'virtual' in front of the prototype of the function abc, in the header file, keeping 'a' initialised to yyy ?
zzz.h:
#include<stdio.h>
class zzz
{
public:
virtual void abc();
void pqr();
};
class yyy:public zzz
{
public:
void abc();
void pqr();
};
class xxx:public yyy
{
public:
void abc() ;
void pqr();
};
void aaa();
OUTPUT:
In yyy abc
When 'a' has to call a function, it first looks at what it has been defined to point to, namely class zzz. It goes out here directly, but finds that function abc is a virtual function. So, 'a' now looks at what it has been initialised to, ie class yyy. This is where it will call function abc from.
Let us modify file z1.cpp again. This time, we initialise 'a' to class xxx. Function abc continues to be virtual in the header file zzz.h.
z1.cpp:
#include "zzz.h"
extern zzz *a;
main()
{
a= new xxx;
aaa();
}
When a function is virtual in class zzz, functions of the same name will be virtual in all classes that are derived from class zzz. Thus, function abc is virtual in classes yyy and xxx, as well. Here 'a' will call abc from the class it has been initialised to. Therefore, when we execute this project, we see that function abc is called from class xxx.
A thought might have crossed our mind. Why haven't we made abc virtual in file zzz.cpp, where the code for the function is placed ? The only way to erase a doubt is to check whether such a thing is possible or not. So, we remove the term 'virtual' in the header file, and place it in front of 'void zzz::abc()' in zzz.cpp.
When we compile our program, we get an error telling us that storage class 'virtual' is not allowed in zzz.cpp. In other words, we have to make abc virtual in the prototype. The function abc cannot be made virtual when the code of the function has been written in a separate file.
Consider the file zzz.cpp. It has absolutely no idea as to what 'a' has been initialised to. Have we said 'a = new zzz' or have we said 'a = new yyy' ? The initialisation has been done in a different C++ file, belonging to the same project. Each C++ file is a separate entity, in itself. One C++ file also has no idea as to what is in another C++ file. In fact, although we can see every instruction, in consequtive statements of a file, the C++ compiler cannot. The C++ compiler doesn't even know what took place in the previous line of a single file. So then, how can we expect it to know what happens in a totally different file. Yet everything works!!! How does this happen ? This will be revealed as we go ahead.
We now go back to our basics. We have a class with three integers and three functions.
th1.cpp:
#include <stdio.h>
class zzz
{
public:
int i,j,k;
void abc()
{
printf("In zzz abc");
}
void pqr()
{
printf("In zzz pqr");
}
void xyz()
{
printf("In zzz xyz");
}
};
main()
{
printf("%d\n", sizeof(zzz));
printf("%p\n",zzz::abc);
printf("%p\n",zzz::pqr);
printf("%p\n",zzz::xyz);
}
OUTPUT:
6 11A7:0069 11A7:007A 11A7:008B
If we look at what the sizeof(zzz) returns, we find that the functions in class zzz haven't been taken into consideration at all.
Sizeof( ) is essentially a function borrowed from C. Suppose there is one integer in a class, the sizeof( ) function will return a value of 2. If we add one more integer in the class, the sizeof() function will tell us that the size of the class is 4. But what if there were no variables in the class ? Wouldn't the sizeof( ) function return 0 ? However , the concept of a class having a size of zero does not exist. This is why the size displayed is 1. Now, if we add a function to class that has two integers, by how much will the value returned by the sizeof( ) function change ? We notice that the addition of a function, or even a hundred functions does not change the value returned by sizeof( ), at all.
Since there are three ints in the class, and each int occupies two memory locations, the size displayed is 6.. The three functions will be stored in separate areas of memory. As before, zzz :: abc refers to the function abc that belongs to the class zzz.
We now create an object 'a' of the class zzz, and display the address at which it starts. We also display the address at which the first variable, i, starts in memory.
th2.cpp:
#include <stdio.h>
class zzz
{
public:
int i,j,k;
void abc()
{ printf("In zzz abc");}
void pqr()
{ printf("In zzz pqr");}
void xyz()
{ printf("In zzz xyz");}
};
main()
{
zzz *a;
a = new zzz;
printf("Size %d\n", sizeof(zzz));
printf("Addr of a %p\n",a);
printf("Addr of a->i %p\n",&a->i);
printf("Addr of abc %p\n",zzz::abc);
printf("Addr of pqr %p\n",zzz::pqr);
printf("Addr of xyz %p\n",zzz::xyz);
}
OUTPUT:
Size 6 Addr of a 11F8:0FFA Addr of a->i 11F8:0FFA Addr of abc 11A7:0092 Addr of pqr 11A7:00A3 Addr of xyz 11A7:00B4
As we can see, the address of where the object starts in memory is the same as address of a->i. The functions will always be stored in a different area of memory. We now make a small change to the above program. In front of the function, abc, in class zzz, we place the term 'virtual'. We do not add any more variables to the class, and within main(), we make absolutely no changes.
th3.cpp:
#include <stdio.h>
class zzz
{
public:
int i,j,k;
virtual void abc()
{ printf("In zzz abc");}
void pqr()
{ printf("In zzz pqr");}
void xyz()
{ printf("In zzz xyz");}
};
main()
{
zzz *a;
a = new zzz;
printf("Size %d\n", sizeof(zzz));
printf("Addr of a %p\n",a);
printf("Addr of a->i %p\n",&a->i);
printf("Addr of abc %p\n",zzz::abc);
printf("Addr of pqr %p\n",zzz::pqr);
printf("Addr of xyz %p\n",zzz::xyz);
}
OUTPUT:
Size 8 Addr of a 11FA:0FF8 Addr of a->i 11FA:0FFA Addr of abc 11A7:00A8 Addr of pqr 11A7:00B3 Addr of xyz 11A7:00C4
When we look at what the sizeof() function returned to us, we notice that the size of class has changed. Have we added any variable in our class ? We definitely haven't. In that case, how has the size of class zzz suddenly become 8. Class zzz clearly takes up two more memory locations.
A look at the next two lines reveal an even bigger surprise. The address of a->i is no longer the same as the address where object 'a' resides in memory. We could try displaying the addresses of a->j and a->k, to see if one of these addresses match the address of 'a'. We shall find that they do not. Doesn't this create a conflict with whatever we have learnt about classes, until now ?
Making a single function virtual caused the increase in size of the class zzz by two. Therefore, making the functions pqr and xyz virtual, as well, should now increase the size of class zzz to occupy an additional 4 bytes of memory, logically speaking. Let us see if this happens, in the next program.
th4.cpp:
#include <stdio.h>
class zzz
{
public:
int i,j,k;
virtual void abc()
{ printf("In zzz abc");}
virtual void pqr()
{ printf("In zzz pqr");}
virtual void xyz()
{ printf("In zzz xyz");}
};
main()
{
zzz *a;
a = new zzz;
printf("Size %d\n", sizeof(zzz));
printf("Addr of a %p\n",a);
printf("Addr of a->i %p\n",&a->i);
printf("Addr of abc %p\n",zzz::abc);
printf("Addr of pqr %p\n",zzz::pqr);
printf("Addr of xyz %p\n",zzz::xyz);
}
OUTPUT:
Size 8 Addr of a 11FF:0FF8 Addr of a->i 11FF:0FFA Addr of abc 11A7:00A8 Addr of pqr 11A7:00C4 Addr of xyz 11A7:00E1
As we can see, only the first time we make a function in class zzz virtual, the size of the class increases by two. Making an additional function of the class virtual, does not further change the size of class zzz.
To explain all the programs we have just done, let's consider the programs where the functions are not virtual. Let's assume that C++ places the variables at a memory location of 500. In other words, the addresses of i, j and k are 500, 502 and 504. The location of i will also be the address of the object 'a' in memory. Let us also assume that the three functions are stored at 600, 620 and 640. This is shown in the figure below.


NO FUNCTIONS VIRTUAL IN CLASS
However, if we make one of the functions virtual, the address of the i will no longer start at 500. Instead, the address of i is now 502. In other words, something has been added to our list of variables. If we probe deeper, we shall find that it is some kind of a pointer. The contents of this pointer will be some memory location, in our case, 550 . We now realise that if we look at the contents of this memory location, we find the address of the first function, namely abc.


ALL FUNCTIONS VIRTUAL IN CLASS
When even one function in the class is virtual, and we say new, an array of pointers is created somewhere in memory. The first location in the array will contain the starting address of the first virtual function in memory. Only the addresses of the virtual functions are stored in this array. If all three functions in the class are virtual, this array will contain the address 600, 620 and 640 at locations 550, 554 and 558. But if only functions pqr and xyz are virtual, then the array will contain 620 and 640 at memory locations 550 and 554, respectively.
At the end of the class, all the functions are placed at some position in memory. Who is responsible for creating this array of pointers to the functions ? It is definitely not created in the first statement within main(). All that this statement does, is define a pointer 'a', to class zzz. But as soon as we say 'new zzz;', this array is created, and the starting location of this array is placed in a two byte pointer, just before the location where the variables start in memory. We do not even need to initialise 'a', for the array to be created. In other words, new is responsible for the creation of this array. Object 'a' comes into picture only when we have to call a function in the class.
Since the contents of this array points only to the virtual functions in the class, the array is known as a Virtual Table.
Let us take the case of a function being called. Let us assume that we have to call function pqr. We shall do this by saying ' a->pqr(); '. For the time being, let's assume that function pqr is not virtual. When the program is compiled, the functions will be left as they are, in the resultant obj file. Information as to where the functions are placed, will be given to the linker by the compiler. When the linker takes over, it simply gets the code of the function pqr, and places it in the appropriate location. The function is now ready for execution.
But if function pqr is virtual, the C++ compiler will first ask for the location where the object 'a' starts in memory. In the figure above, we have given this location as 500. This is also the location of the two-byte pointer, that contains the starting address of an array of pointers. The C++ compiler obtains this address by saying ' * 500 '. The answer it gets is 550, the start of the Virtual Table. It then jumps to this address. The C++ compiler further finds out whether the function pqr is the first, second or the third virtual function. Since pqr is the second virtual function, it skips the first four bytes and jumps to location 554. It obtains the contents of this memory location. It turns out that, at this location, the number 620 is present. This is the address of the function pqr. The code of this function will now get executed at runtime.
th5.cpp:
#include <stdio.h>
class zzz
{
public:
int i,j,k;
virtual void abc()
{ printf("In zzz abc");}
virtual void pqr()
{ printf("In zzz pqr");}
virtual void xyz()
{ printf("In zzz xyz");}
};
main()
{
zzz *a;
a = new zzz;
a->pqr();
}
OUTPUT:
In zzz pqr
This concept of introducing a virtual table as an imtermediate stage slows down the entire execution process, because now the function has to be accessed in a very round-about manner. The addresses of a function, however, remains the same, whether it is virtual or not.
We now consider the case of a class derived from class zzz.
th6.cpp:
#include <stdio.h>
class zzz
{
public:
int i,j,k;
virtual void abc()
{ printf("In zzz abc");}
virtual void pqr()
{ printf("In zzz pqr");}
virtual void xyz()
{ printf("In zzz xyz");}
};
class yyy:public zzz
{
public:
virtual void abc()
{ printf("In yyy abc");}
virtual void pqr()
{ printf("In yyy pqr");}
virtual void aaa()
{ printf("In yyy aaa");}
};
main()
{
zzz *a = new zzz;
a->abc();
}
The explanation of this program has already been given, earlier. We have defined 'a' as a pointer to zzz, when we said ' zzz *a', and it has also initialised to zzz, when we equated 'a' to 'new zzz'. Therefore, function abc gets called from class zzz only. But, suppose we rewrite main(), so that 'a' is initialised to class yyy, keeping the rest of the program same, what difference does this make ?
main()
{
zzz *a = new yyy;
a->abc();
}
Function abc is virtual in class zzz. As class yyy is derived from class zzz, abc automatically becomes virtual in class yyy. It does not matter what we define 'a' to. As soon as we say 'new yyy',an array of pointers is created in memory. The addresses that go into this array are of the functions of the class to which we have said 'new' to. Thus, the virtual table contains the addresses of virtual functions of class yyy. The sequence in which these functions are placed in the array, however, depend on class zzz. This is because class zzz is the base class. This means that the ordering of the functions in the virtual table is decided by the base class, but the functions that get called, are the functions belonging to the derived class. Because the ordering is decided by the base class, then functions that appear first in the virtual table are the functions in class zzz, namely functions abc, pqr and xyz. Following this, any virtual functions that are only present in class yyy will be placed, for example function aaa. Even though the virtual functions of class zzz are the ones placed in the virtual table, when any of those functions have to be called, they are called from class yyy. When function abc is called, it absolutely doesn't matter whether 'a' is defined as a pointer to class zzz or to class yyy. The virtual function, abc, will always be called from the class, to which we have said 'new', that is, class yyy in this example.



Once again, take a look at the second line in main(), that is 'zzz *a = new yyy;'. We repeat our earlier statement, that it really doesn't matter what's on the left-hand side of the equal-to sign. If we have said 'new yyy', then C++ will not check to see whether 'a' is a pointer to class zzz or to class yyy. C++ will check to see whether it's rules are being met or not. It makes sure that we don't have a larger class on the left-hand side and a smaller class on the right-hand side. It will, as far as possible, call the function from class yyy, because class yyy is the class on the right-hand side of the equal-to sign. However, if a function is not present in class yyy, it will be called from class zzz. This is what happens when the function xyz() has to be called. A check is done only if we have to call the function aaa(). This function is not present in class zzz, the class which 'a' has been defined as a pointer to. In this case, trying to call function aaa() will result in an error.
However, if we define 'a' as a pointer to class yyy, by saying ' yyy *a = new yyy; ' we have absolutely no problems. We can call any of the four functions in the figure above. The way function pqr is called is exactly as described for the previous figure.
But let's go back to defining 'a' to be a pointer to class zzz, but initialising it to class yyy. The function abc will be called from the class we have initialised 'a' to. Thus, we are calling code from a higher class, using a pointer to a smaller class. This is a true representation of polymorphic behaviour.
Is it possible for us to make a function without any code, in a class, equal to zero ? Let us take the case of an ordinary C++ program, with a single function, abc, to test this out.
vz1.cpp:
class zzz
{
public:
void abc( )=0;
};
main( )
{
zzz *a;
}
I suppose, we all may have expected errors from this program. After all, who's ever heard of a function being made equal to zero ? We do get a single error saying 'Non-virtual function zzz::abc declared pure'. Even if we don't understand what this actually means, we can infer that we aren't able to equate function abc to zero. It's quite useless trying to initialise 'a' in main( ), with the statement ' a = new zzz; ' . We only get an additional error saying that we cannot create an instance of abstract class zzz, another meaningless error. An instance of a class refers to an object of the class. Thus we cannot create an object of our class zzz.
But what if we make the function virtual, and than make it equal to zero ?
vz2.cpp:
class zzz
{
public:
virtual void abc( )=0;
};
main( )
{
zzz *a;
}
To our delight, it doesn't give us any errors. But before we jump for joy, let's remind ourselves that we have only created a pointer to class zzz. An object of class zzz hasn't yet been created. This object is created when we say new zzz.
vz3.cpp:
class zzz
{
public:
virtual void abc( )=0;
};
main( )
{
zzz *a;
a = new zzz;
}
Well, our luck had to run out sometime. We still get an error. An error that tells us we cannot create an object of class zzz.
vz4.cpp:
class zzz
{
public:
virtual void abc( )=0;
};
class yyy:public zzz
{
public:
}
main( )
{
yyy *a;
a = new yyy;
}
The above program seems to be a totally useless exercise. We have derived a class yyy from class zzz. We can hardly expect to create an object of the derived class, yyy, when we couldn't couldn't create an object of the base class, zzz.
In our next program, we have added the name of the function, abc( ), along with a pair of braces within class yyy. In other words, we have the entire code of function abc( ) in class yyy.
vz5.cpp:
class zzz
{
public:
virtual void abc( )=0;
};
class yyy:public zzz
{
public:
void abc( ){ }
}
main( )
{
yyy *a;
a = new yyy;
}
To our surprise, this program gives us absolutely no errors. If you ask me, the entire sequence of programs make no sense. We couldn't create an object of class zzz, a class that contains a virtual function abc( ), which we equated to zero. We couldn't create an object of class yyy, which was derived from class zzz, either. But the moment we added the code of function abc, we get no errors.
This could only mean one thing. When we have made a virtual function abc( ), without code, equal to zero, in class zzz, we cannot create an object of that class. Now, when we derive a class yyy, from this class zzz, the entire code of the function, abc, has to be added to class yyy. By entire code, we mean that the function should have the name, the parameters, as well as the return values of the function, as in the base class. If class zzz had to have a number of functions that were virtual as well as equal to zero, we would have to add the code of all these functions in class yyy. Then only, can we create an object of the class yyy.
If we had virtual functions in class zzz, either equated to zero, or not equated to zero, a Virtual Table gets created. Now if we derive another class, yyy, from class zzz, all the functions automatically come into class yyy, as long as the virtual functions in class zzz weren't equated to zero. But if class zzz contained a virtual function equal to zero, we are forced to include the code of that function in class yyy. Thus, making a virtual function equal to zero in a class, guarantees us that any class derived from this class will have to implement this function. The ordering of the Virtual Table will always be decided by the base class. In other words, class zzz will decide the order in which all virtual functions are called, in class yyy. This doesn't change, irrespective of whether the virtual functions are equal to zero, or not equal to zero, in class zzz.
We finally consider the case of a class xxx, which has been derived from class yyy, which is, in turn, derived from class zzz. Both, class zzz as well as class yyy, have one virtual function equal to zero, each.
vz6.cpp:
class zzz
{
public:
virtual void abc()=0;
};
class yyy:public zzz
{
public:
virtual void pqr()=0;
} ;
class xxx:public yyy
{
public:
void abc(){}
void pqr(){}
};
main()
{
xxx *a;
a=new xxx;
}
We are now forced to have the code of both, function pqr as well as function abc, in class xxx. Class xxx is actually derived from class zzz and class yyy. If there are any virtual functions that are equated to zero in any of these two classes, then we have to have the code of these functions in class xxx.
Reference Operators
We are firm believers in the fact that, in order to learn new technologies, we need to get our basics rights. Let us take the case where we have to pass a number of parameters to a function. For the moment, we pass only two parameters to a function, abc. Within the function abc, we have an additional variable, k. We use this variable to temporarily store the value of i. We then assign the value of j to i, and the value of k to j. In other words, we have exchanged the values of i and j, in the function, abc.
ref1.cpp:
#include<stdio.h>
void abc(int ,int );
main()
{
int i,j;
i=10;
j=20;
abc( i, j );
printf("i=%d..j=%d \n",i,j);
}
void abc(int i, int j)
{
int k;
k=i;
i=j;
j=k;
}
OUTPUT:
i=10..j=20
Within main(), the variables i and j have the values, 10 and 20, respectively. In the line ' abc( i, j ); ', these values of i and j are put on the stack. The value of j goes on the stack first, followed by the value of i. This is as shown in the figure below. Only then, is the function abc is called.

When function abc is called, it picks up the values in reverse order, that is, i is picked up first, followed by j. Within function abc, the values of i and j are interchanged, with the help of k. So now, the value of i is 20, and that of j is10. This change is visible in the values on the stack. This change in i and j is however, restricted to the function abc. If we display the addresses of i and j in the function abc, we shall see that they are different from the addresses of i and j in main(). In no way do changes made to i and j in abc() affect the values of i and j within main(). This is because at the end of the statement 'abc();' within main(), the stack is restored to point to 104 by main(), as soon as the ';' is encountered. By this, we mean that if the stack moved four locations down at the beginning of the function call, main() moves it four locations up after the function has been executed. When the function printf() is called, the values of i and j that go on the stack, are those values that are within main(). This is why, printf will display the values of i and j as 10 and 20.
Suppose we want any change made to variables within a function to be visible within main(), we have to make use of pointers. Instead of putting the values of the variables on the stack, we put their addresses on the stack. This is shown in the next program.
ref2.cpp:
#include<stdio.h>
void abc(int *,int *);
main()
{
int i,j;
i=10;
j=20;
abc(&i,&j);
printf("i=%d...j=%d \n",i,j);
}
void abc(int *i, int *j)
{
int k;
k=*i;
*i=*j;
*j=k;
}
OUTPUT:
i=20...j=10
Within main(), we now have the line ' abc(int &i, int &j); '. Therefore, when the function abc is called from main, the stack is as shown in the figure. The addresses of the variables, i and j, are obtained from the previous figure.
The function will accept the addresses of the two variables from the stack. As in the previous program, we have an intermediate variable, k, which we use when exchanging the values of i and j. However, everytime we have to access the values of the two variables, we have to use *i and *j. When we change the value of *i, we are effectively changing the value of the variable that is at memory location 50. Thus any change in the function is reflected within main().
If we wish to utilise these variables a number of times in our function, we not only have to make quite a few changes from the previous program, ref1.cpp, but in general, we also create a lot of confusion.
Is there a method by which we could have any changes to the values of variables within a function, visible within main(), but without making major changes to the code within the function?
Let us look at the next example.
ref3.cpp:
#include<stdio.h>
void abc(int &,int &);
main()
{
int i,j;
i=10;
j=20;
abc(i,j);
printf("i=%d...j=%d \n",i,j);
}
void abc(int &i, int &j)
{
int k;
k=i;
i=j;
j=k;
}
OUTPUT:
i=20...j=10
Consider the above program and compare it with our first program, ref1.cpp. We have changed the prototype of function abc at the top of this program. The variables that we now pass are 'int &' instead of just 'int'. The actual function name has to follow the same pattern as the prototype. We have made corresponding changes here as well. We haven't, however, made any changes in the code of the function abc. From main( ), the function abc is called in the same way, as in ref1.cpp.
As in that program, we also exchanged the values of i and j, without using *i and *j. We find that printf() in main() displays the new values of i and j.
We changed the prototype of the function, so that the parameters of the function are 'int &' instead of only 'int'. When we call function abc, we retain the statement ' abc(a,b); '. But what goes on the stack are not the values of i and j. Because of the prototype, what actually goes on the stack are the addresses of i and j. The function abc now gets these addresses. But the code of this function does not have pointers to access the values at these addresses. C++ therefore goes and writes *i and *j, where ever it sees i and j in function abc. We arrive at the conclusion that when you see & in the prototype and name of the function, pretend that you haven't seen the &, and write the code of the function accordingly. This programs now works in exactly the same way as ref2.cpp.