Learn the basics of Visual C# programming. Easy to learn and read tutorial for beginners.
Basic lessons for beginning visual arts teachers, or for regular classroom teachers who would like to incorporate more art in their classrooms. All lesson materials are for educational purposes ...
cacacaFull description
Dave Weckl BookFull description
Descripción: Dave Weckl Book
percusion - drums
A brief article focusing on seven basic asanas or poses to do--to ground your practice and get back to, while attempting more complex asanas. The seven poses are described briefly and a few…Full description
Dave Weckl Book
Basics
short notes on law of tortsFull description
Introduction to C++ Programming...
USGS code to convert SEGY to ASCII
Descripción: Learn C++ In A DAY The Ultimate Crash Course to Learning the Basics of C++
Introduction to C++ Programming...Descripción completa
Full description
revista ctoDescripción completa
solutionDescrição completa
Introduction to C++ Programming...
Conquering C++ This is the first part of C++ (basics of programming and OOP)
Contents - PART I Basic C++ 1.) Introduction • • •
a.) Evolution of Programming Languages
•
b.) Binary System & Memories c.) Terms used in programming
•
2.) Simple C++ Programming • • •
• •
6.) More Data Types
Your very first C++ program Compilers and how to run a program Data Types of variables o Integer o Floating Types o Double o Character o Boolean type o Data type range o Determining the range o More on Binary numbers Simple C++ programs Identifiers,Keywords and File Nomenclature,Constants,Comments
Scope of variables, Storage Classes (auto, static, extern, register) Type Qualifiers (const and volatile), Variable Initialization, Arrays Character Arrays Multi Dimensional Arrays, Initializing Multi Dimensional Arrays, Passing Arrays to functions Structures, Nesting of Structures Enumerated Data Types, Unions, Passing structures to functions
• • • • • • • • •
Address of operator, Declaration and use of pointer Arithmetic operation on pointers, Pointers and Arrays Pass by value, Pass by reference (C and C++ style) and reference variables More on references Returning pointers from functions Memory Allocation (new and delete) and Memory Heap Pointers to functions, Pointer Declarations Pointers to Structures, Pointer to Pointer (multiple indirection) Pointer to constants and constant pointers, Void pointers Pointers to characters, Pointers Demystified (2-D arrays and pointers) Passing multidimensional arrays to functions, Functions with varying arguments
4.) Controlling Program Flow •
•
•
Loops (Iterations) o For loop and its variations o While, Do-while loop Decision statement o If...elseif..., Nested If, Conditional Operator o Switch Case Loop Control Statements o Break, Continue, Goto, Return
•
• •
•
• • •
•
• •
5.) Functions •
8.) Classes and Objects
Declaration, call and definition, Parameters and arguments Default Arguments, Returning values from a function, Returning void, The int main( ) function An illustration of int main( ) Types of functions, Using Library functions (rand, srand and time), Function overloading, An example using functions Recursion (calling oneself!), Inline Functions
• •
•
•
Object Oriented Programming Concept and OOP Languages Class with example Constructors, Constructor with Parameters, Overloaded Constructors, Default Constructors and Arrays of Objects Scope Resolution Operator, Destructor, Objects and Functions (passing and returning objects), Initialializing an object using an object A Practical Use of Classes Data Encapsulation...Who are we hiding data from? More objects and classes, Private member functions (helper functions) Friend Function and Friend Classes, Static Class Members, Constant Objects (and the 'mutable' qualifier) Copy Constructor, this pointer, pointer to objects, objects in memory Initializer list, explicit keyword, declaration and definition, return value optimization
Workshop (for chapters 7 and 8) • •
Test Yourself-III More Questions (extra programs)
If you are done with the basics go on to
Advanced C++ (PART - II)
3
Programming Languages
The earlier first section was Binary numbers. Some people said that I ought to reduce the math in this tutorial and I felt that perhaps I should start off differently. And so I've altered the sequence of sections in this first unit.
Recap of important computer concepts: When we talk of computers, we refer to ‘hardware’ and ‘software’. All the physical components like monitor, keyboard, mouse, modem, printer etc. fall under the hardware category. We need computers to do something useful for us but computers aren’t smart enough to know what we want them to do (at least not yet!). We need to explicitly tell the computer our requirements in the form of instructions. A set of instructions for doing something useful forms a ‘program’. And we refer to these programs as software (i.e. unlike hardware, software is something that we can’t touch and feel). Major hardware parts in a computer: 1.
Input and Output devices: Computer can receive input from input devices (like a keyboard, mouse or a scanner). Output devices are used by the computer to send out information to the user; example: monitor, printer, plotter etc.
2.
Arithmetic and Logical Unit (ALU): As the name implies this unit is responsible for all arithmetic calculations. It is part of the central processing unit.
3.
Memory: When our computer wants to work on some information, it will require some place where it can store data; where it can store the instructions; where it can store intermediate results of operations etc. Memory serves this purpose and there are different types of memory (for different requirements): Primary memory: This memory can be quickly accessed by the computer (in technical jargon we say
that this memory has low access time; i.e. time taken to access data in primary memory is less). Generally, instructions and data needed by the computer immediately for processing are placed in primary memory. Data in primary memory is not permanent. Each time we restart the computer, the memory would get refreshed. Secondary memory/ storage: This is the place where we store all our data. It’s a long-term storage device (like a hard-disk). Access times are higher but secondary memory is cheaper than primary memory.
Introduction
4 4.
CPU (Central Processing Unit): We’ve seen computer parts to get input, store information, display output and also to perform
calculations. But there needs to be someone at a higher level to control the individual units; someone to decide when to capture the input, when to send output information to the monitor etc. The CPU takes up this responsibility and is termed the ‘brain’ of the computer. The CPU is also called the processor (a microprocessor chip).
The language computers understand: We have a lot of languages in this world but all computers can understand only binary language. The vocabulary is very simple and small; it consists of only 2 things: 0 and 1. This is all that a computer understands. (Humans are comfortable with the decimal system - consisting of the numbers 0 to 9). We’ll look at the binary system later in this chapter. Remember: A computer finally needs everything in binary language (instructions and data).
Programming Languages: Programs are written to tell the computer to do something useful for us. It might be as simple a task as adding two numbers or as complex as transferring data between 2 computers in a network. There are several reasons why we need programs. Imagine searching through a stack of papers to search for some telephone bill. Imagine a company maintaining its accounts (expenses and income on a day-to-day basis) - if this was done using the traditional file and paper method, someone would have to keep entering all the details in sheets of paper and then stack them into files. If, in future, someone wants to find out when a particular product was sold they would have to manually read through each
Introduction
5 and every paper in each and every file. Computers can be programmed to perform such tasks and they will do it faster. Basically, the computer is very good in performing repetitive tasks. One point to note: man created computers and man programs computers to perform certain tasks. A program is a set of instructions that can be executed by the computer. The computer will obediently follow whatever the programmer instructs it to do (as long as it understands the instructions). The downside to this is that the computer does not have any special intelligence; it is only as intelligent as it is programmed to be. For instance, a robot can be programmed to walk. If it is not programmed to detect obstacles, the robot will simply bang into obstacles because it does not have the intelligence to recognize and avoid obstacles. If the programmer provides such provisions, then the robot will be able to handle such scenarios. With many software programs already existing, you may wonder why do we need more software; why not just buy and use what already exists? Software is usually not custom designed for the requirements of a particular company and you may not even have software for your own special requirement. For example, dentists never used to have any software specifically for their use but now you can see a range of dental software (the dentist can maintain details of each of his patients’ tooth in the computer). Programs needn’t be as complicated as dental software; you may want to have a little software to calculate your CGPA or to calculate your rank in class. To perform such tasks a programmer has to write a program (or a set of instructions). These instructions are written in a specific programming language and the programmer can chose to write the instruction in any one of the available languages.
Evolution of Programming Languages: Any programming language can be categorized into one of the following categories: •
High Level
•
Middle Level
•
Low Level (machine and assembly languages)
Middle level languages are sometimes clubbed together under the category of high-level languages. Machine Level Languages: A computer has a processor and if we want the computer to do something then we need to direct instructions at the processor. The problem is that computers can understand only binary language (i.e. the language which comprises of only 1s and 0s). All instructions to the processor should be in binary and so a programmer can write programs using a series of 1s and 0s. Every
Introduction
6 processor will understand only some instructions (this depends on how the processor was designed). An instruction to increment a variable might be: 110110100011101 Imagine having 100 such instructions in a program! The advantage is that no conversion (or translation) is needed because the computer can understand the 1s and 0s. This is called machine level language and this was used at the time computers came into existence. The drawbacks are quite clear. By using machine language writing programs is very tedious and also prone to error (even if one bit is wrong, the entire program functionality could get altered). Trying to identify the error will also be a very tedious job (imagine reading a series of 1’s and 0’s trying to locate the mistake). Assembly Languages: Since it is very difficult for us to write instructions in binary language, assembly languages were developed. Instead of using a set of 1s and 0s for a particular instruction, the programmer could use some short abbreviations (called ‘mnemonics’) to write the program (for example: ADD is a mnemonic to add two numbers). Every processor has an instruction set that describes the various commands that the processor can understand. A normal instruction set will have instructions like JMP (jump), ADD (for addition) and so on. A programmer will write the program using these instructions and an assembler will convert the mnemonics into binary form so that the processor can understand it. The problem is that assembly level languages are specific to each processor. For example the 8085 (the simplest microprocessor) has an instruction set different from the 8086 microprocessor. Thus you would have to rewrite the program for each microprocessor. Another problem with assembly level programming is that the programmer needs to know details about the processor’s architecture such as the internal registers where values can be stored, how many internal registers are available for use etc. Low level languages are very close to the hardware but difficult for writing programs. High Level Languages: Writing larger programs in assembly language is quite difficult and hence, high-level languages were developed (like BASIC). These languages were not close to the actual computer hardware but were very close to the English language. They tried to simplify the process of programming. In these languages the programmer needn’t worry about internal registers and processor architecture; instructions could be typed in almost normal English. The English-like instructions had to be converted to machine language using a compiler. One simple example of an English like command in COBOL is:
Introduction
7 ADD incentive TO basic GIVING salary. The instruction is quite self-explanatory (Add ‘incentive’ and ‘basic’ and store the result in ‘salary’). COBOL is also a high-level language. As programs grew larger (for example: maintaining a record of all employees, accounts maintenance etc.), even high level languages had certain drawbacks. These languages are also referred to as unstructured form of programming. The reason they are unstructured is because when a large program is written, it becomes very difficult to analyze the program at a later date (by someone else or even by the original programmer himself). There are many reasons for this; one is the use of statements like GOTO in high-level languages. In any program some tasks will either be performed repetitively or the programmer might want the program to execute a different set of instructions if some condition is satisfied. For example in a division program, in case the denominator is 0 then the program should not divide the two numbers; instead it should display an error message. Only if the denominator is not 0 should the program divide the two numbers. This kind of programming is referred to as program flow control. In a larger program there could be various other possibilities and all this was dealt with by using the GOTO statement. If you’ve used BASIC you will be aware that for every instruction that you type, you have to specify a line number (example 10,20,30 and so on). GOTO will be followed by some line number (indicating the instruction that has to be executed next. Ex: 1020 GOTO 50 means that now you want the program flow to go to line number 50). The problem with this is that in a large program it will become very difficult for anyone to understand the program logic. Imagine reading through 100 lines and then finding one GOTO statement to the 200th line. Then on the 205th line there could be another GOTO statement directing you to the 150th line. This should make it clear as to what is meant by the term ‘unstructured’. This is a major problem with high level languages. Another fact is that high level languages are very far away from the actual hardware. Examples of high-level languages are BASIC, Fortran (Formula Translation), COBOL (Common Business Oriented Language) and LIST (List processing). Thus to take advantage of high level and low level languages, middle level languages like C and C++ were developed. They were easy to use and tried to retain the advantages of low level languages (i.e. being closer to the architecture).
Introduction
8
C and C++ Dennis Ritchie developed C and it was quite popular. An interesting feature in C is the use of functions. The programmer could write a function for checking whether a number is odd or even and store it in a separate file. In the program, whenever it is needed to check for even numbers, the programmer could simply call that function instead of rewriting the whole code. Usually a set of commonly used functions would be stored in a separate file and that file can be included in the current project by simply using the #include syntax. Thus the current program will be able to access all functions available in ‘filename’. Programs written in C were more structured compared to high level languages and another feature was the ability to create your own data types like structures. For instance if you want to create an address book program, you will need to store information like name and telephone number. The name is a string of characters while the telephone number is an integer number. Using structures you can combine both into one unit. Similarly there are many more advantages of using C. Though C seemed to be ideal, it was not effective when the programs became even more complex (or larger). One of the problems was the use of many functions (developed by various users) which led to a clash of variable names. Though C is much more efficient than BASIC, a new concept called Object Oriented Programming seemed better than C. OOP was the basis of C++ (which was initially called ‘C with classes’). C++ was developed by Bjarne Strastroup. In object oriented programming, the programmer can solve problems by breaking them down into real-life objects (it presented the programmer with an opportunity to mirror real life). What is an object? This topic is dealt with extensively in the chapter on ‘Objects and Classes’ but a brief introduction is provided here. Consider the category of cars. All cars have some common features (for example all cars have four wheels, an engine, some body colour, seats etc.). Are all cars the same? Of course not. A Fiat and a Ford aren’t the same but they are called as cars in general. In this example cars will form a class and Ford (or Fiat) will be an object.
Introduction
9 For those people who know C programming, it would be useful to know the differences between C and C++. Basically C++ includes everything present in C but the use of some C features is deprecated in C++. •
C does not have classes and objects (C does not support OOP)
•
Structures in C cannot have functions.
•
C does not have namespaces (namespaces are used to avoid name collisions).
•
The I/O functions are entirely different in C and C++ (ex: printf( ), scanf( ) etc. are part of the C language).
•
You cannot overload a function in C (i.e. you cannot have 2 functions with the same name in C).
•
Better dynamic memory management operators are available in C++.
•
C does not have reference variables (in C++ reference variables are used in functions).
•
In C constants are defined as macros (in C++ we can make use of ‘const’ to declare a constant).
•
Inline functions are not available in C.
Let’s recap the evolution of programming languages: initially programs were written in terms of 1s and 0s (machine language). The drawback was that the process was very tedious and highly error-prone. Assembly language was developed to write programs easily (short abbreviations were used instead of 1s and 0s). To make it even simpler for programmers, high level languages were developed (instructions were more similar to regular English). As the complexity of programs increased, these languages were found to be inadequate (because they were unstructured). C was developed but even that was not capable of dealing with complex or larger programs. This led to the development of C++. Note: Sometimes languages are divided into low level and high level only. In such a classification, C/C++ will come under high level languages.
Introduction
10 Why do you need to learn C++? There are many people who ask the question, "why should I learn C++? What use is it in my field?" It is a well-known fact that computers are used in all areas today. Programming is useful wherever computers are used because it provides you the flexibility of creating a program that suits your requirements. Even research work can be simulated on your computer if you have programming knowledge. Construction and programming may appear to be miles apart but even a civil engineer could use C++ programming. Consider the case of constructing a physical structure (like a pillar) in which the civil engineer has to decide on the diameter of the rods and the number of rods to be used. There are 2 variables in this case: 1. The number of rods needed (let’s denote it as ‘n’) and 2. The diameter of each rod (let’s call it as ‘d’) The civil engineer might have to make a decision like: "Is it cost-effective for me to have 10 rods of 5cm diameter or is it better to have 8 rods of 6cm diameter?" This is just one of the simple questions he may have in his mind. There are a few related questions: "What is the best combination of number of rods, their diameters and will that combination be able to handle the maximum stress?" Usually equations are developed for each of the factors involved. It would be much easier if the civil engineer could simply run a program and find out what is the best combination instead of manually trying out random values and arriving at a solution. This is where programming knowledge would benefit the engineer. Any person can write a good program if he has adequate knowledge about the domain (domain refers to the area for which the software is developed. In this case it is construction). Since the civil engineer has the best domain knowledge he would be able to write a program to suit his requirements if he knew programming. Programming is applicable to almost every field- banking (for maintaining all account details as well as the transactions), educational institutions (for maintaining a database of the students), supermarkets (used for billing), libraries (to locate and search for books), medicine, electrical engineering (programs have been developed for simulating circuits) etc.
Introduction
11
Binary Numbering System
The following 2 sections on binary numbering system and memory are optional but recommended. If you’ve taken a course in electronics you probably already know about this and can use the material provided here as a refresher. Having knowledge of the binary system and computer memory will help in understanding some features in programming.
The heart of the computer is the microprocessor, which is also referred to as the processor. The microprocessor is the main part of the computer’s CPU (central processing unit). A processor consists of millions of electronic switches; a switch can be either in ON or OFF state. Thus there are only two distinct states possible in these devices. In our realworld calculations we make use of the decimal system (which has 10 distinct states/numbers: 0 to 9). Counting up to ten is easy for humans but would be quite difficult for computers. It is easier to create a device capable of sensing 2 states than one capable of sensing 10 states (this also helps reduce on errors). Computers make use of the binary system (i.e. they store data in binary format and also perform calculations in binary format). The binary system (binary meaning two) has just two numbers: 1 and 0 (which correspond to the ON and OFF state respectively – this is analogous to a switch which can either be in ON state or in OFF state). When we have different systems (binary, decimal etc.), there ought to be a way of converting data from one system to the other. In the decimal system the number 247 stands for 7*100 + 4*101 + 2*102 (add it up and the result will be 247; i.e. in each place we can have one of the 10 digits and to find the actual place value we have to multiply the digit by the corresponding power of 10). For example: 247 = (2 x 102) + (4 x 101) + (7 x 100) = 200 + 40 + 7 1258 = (1 x 103) + (2 x 102) + (5 x 101) + (8 x 100) = 1000 + 200 + 50 + 8 Note: In C++ and most other computer languages, * is used as the multiplication operator. The same concept holds good for a binary number but since only two states are possible, they should be multiplied by powers of 2. Remember: A binary digit is called a bit.
Introduction
12 So, what is the value of 1101? Multiply each position by its corresponding power of 2 (but remember, you have to start from 20 and not from 21). The value for 1101 is 13 as illustrated in the figure below:
An alternate method to obtain the value is illustrated below (but the underlying concept is the same as above):
It is easy to obtain the values which are written above each bit (27=128, 26=64 and so on). Write these values on top and then write the binary number within the squares. To find the equivalent decimal value, add up the values above the square (if the number in the square is 1). If a number is denoted as 1101, then this stands for the lower (or last) four bits of the binary number (the upper bits are set to 0). Hence 1101 will come under the values 8, 4, 2 and 1. Now, wherever there is a 1, just add the value above it (8+4+1=13). Thus 13 is the decimal equivalent of 1101 (in binary format). To distinguish between decimal and binary we usually represent the system used (decimal or binary) by subscripting the base of the system (10 is the base for the decimal system while 2 is the base for the binary system). Hence (13)10 = (1101)2
Introduction
13 Hence (13)10 = (1101)2
Computers store information in the form of bits and 8 bits make a byte. But memory capacity is expressed as multiples of 210 bytes (which is equal to 1024 bytes). 1024 bytes is called a Kilobyte. You may wonder why it is 1024 and not 1000 bytes. The answer lies in the binary system. Keeping uniformity with the binary system, 210=1024 and not 1000 (the idea is to maintain conformity with the binary system). Beware: The bit in position 7 in fig 1.1 is actually the 8th bit of the number (the numbering of bit starts from 0 and not 1). The bit in the highest position is called as the most significant bit. In an 8 bit number, the 7th bit position is called the most significant bit (MSB) and the 0th bit is known as the least significant bit (or LSB). This is because the MSB in this case has a value of 128 (28) while the LSB has a value of just 1.
Introduction
14
Computer Memory We know that computers can operate only on bits (0s and 1s). Thus any data that has to be processed by the computer should be converted into 0s and 1s. Let us suppose that we want to create a text file containing a single word “Hello”. This file has to be stored physically in the computer’s memory so that we can read the file anytime in the future. For the time being forget about the file-storage part. Let’s just concentrate on how the word “hello” is stored in the computer’s memory. Computers can only store binary information; so how will the computer know which number corresponds to which alphabet? Obviously we cannot map a single bit to a character. So instead of bits we’ll consider a byte (8 bits). Now we can represent 256 characters. To perform map a character to a byte we’ll need to use some coding mechanism. For this purpose the ASCII (American Standard Code for Information Interchange) is used. In this coding system, every alphabet has an equivalent decimal value. When the computer uses ASCII, it cannot directly use the decimal value and it will convert this into an 8-bit binary number (in other words, into a byte) and store it in memory. The following table shows part of the ASCII code.
Character A B a b
Equivalent decimal value 65 66 97 98
Binary value 0100 0001 0100 0010 0110 0001 0110 0010
In this way each character is mapped to a numeric value. If we type the word hello, then it is converted into bytes (5 bytes- one for each character) based on the ASCII chart and is stored in memory. So ‘hello’ occupies 5 bytes or 40 bits in memory. Note: It is very important to know the binary system if you want to use the bitwise operators available in C++. The concept of memory is useful while learning pointers. A question arises, “where are the individual bits stored in memory?” Each individual bit is stored in an electronic device (the electronic device is technically called a flip-flop; which is something like a switch). A single flip-flop can store one bit. Consider the fig. below:
Introduction
15
As mentioned earlier we deal in terms of bytes rather than bits. The figure shows a 4-byte memory (which means it can hold 32 bits – each cell can store one bit). All information is stored in memory and the computer needs some method to access this data (i.e. there should be some way of distinguishing between the different memory locations). Memory addresses serve this purpose. Each bit can be individually accessed and has a unique memory address. To access the first byte, the computer will attempt to read the byte stored at memory address 1. If memories didn’t have addresses then the computer would not know from where it has to read data (or where it has to store data). An analogy to memory address is the postal address system used in real-life. A city will have a number of houses and each house has a unique address. Just imagine the situation if we didn’t have any postal address (we wouldn’t be able to locate any house in the city!). One difference is that a memory address can house only bits and nothing else. Memory address representations are not as simple as shown above. In the above case we’ve considered a memory that has capacity to store just 4 bytes. Memories usually contain kilobytes or gigabytes of space. As the amount of memory available increases, so does the size of the address (the address number might be in the range of millions). Thus instead of using the decimal system for addresses, the hexadecimal numbering system is used. Hexa means 16 and the distinct numbers in this system are 0 to 9 followed by A, B, C, D, E and F where A= 10 in decimal, B= 11 in decimal and F= 15 in decimal. Counting in hexadecimal system will be 0…9, A, B…F, 10,11,12,13…19,1A, 1B, 1C…and so on. It is quite clear that 10 in hexadecimal does not equal 10 in decimal. Instead, (0F)16 = (15)10, (10)16 = (16)10 and (11)16 = (17)10 Four bits form a ‘nibble’. Eight bits form a ‘byte’ and four bytes of memory are known as a ‘word’. There is some significance attached to a ‘word’ which we shall deal with later.
Introduction
16 Another term related to memory is ‘register’. A register consists of a set of flip-flops (flip-flops are electronic devices that can store one bit) for storing information. An 8-bit register can store 8 bits. Every processor has a set of internal registers (i.e. these registers are present within the processor and not in an external device like a hard disk). These registers (which are limited in number depending on the processor) are used by the processor for performing its calculations and computations. They usually contain the data which the processor is currently using. The size of the register (i.e. whether the registers will be 16-bit, 32-bit or 64-bit registers also depends on the processor). The common computers today use 32-bit registers (or 4-byte registers). The size of a register determines the ‘word-size’ of a computer. A computer is more comfortable (and prefers) working with data that is of the word-size (if the word-size is 4 bytes then the computer will be efficient in computations involving blocks of 4 byte data). You’ll understand this when we get into data types in the subsequent chapters. The different types of memory are: 1. Secondary storage (for example the hard disk, floppy disks, magnetic disks, CD-ROM) where one can store information for long periods of time (i.e. data is retained in memory irrespective of whether the system is running or not). 2. The RAM (random access memory) is used by the computer to store data needed by programs that are currently running. RAM is used for the main (primary) memory of the computer (all programs which are executed need to be present in the main memory). The RAM will lose whatever is stored in memory once the computer is switched off. 3.
ROM (read only memory): This contains instructions for booting up the system and performing other start-up operations. We cannot write to this memory.
4.
The internal registers within the processor- these are used by the computer for performing its internal operations. The compiler will decide what has to be stored in which register when it converts our high-level code into low-level language. As such we won’t be able to use these registers in our C++ code (unless we write assembly code).
Remember: Secondary storage (also called auxiliary memory) is not directly accessible by the CPU. But RAM is directly accessible (thus secondary memory is much slower than the primary memory). For a program to execute it needs to be present in the main memory. Which memory has lowest access time (or which memory can the CPU access quickly)? The internal registers can be accessed quickly and the secondary storage devices take much longer to access. Computers also make use of a cache-memory. Cache memory is a highspeed memory which stores recently accessed data (cache access is faster than main memory access).
Introduction
17
Related concept: In general cache means storing frequently accessed data temporarily in a place where it can be accessed quickly. Web browsers tend to cache web pages you visit frequently on the hard disk. Generally when we type in a website address, the browser needs to query the website and request for the page; which is a time consuming process. When the browser displays this webpage, it internally caches (stores a copy) this webpage on our hard disk also. The next time we time the same website address, the browser will directly read out from the hard disk rather than query the website (reading from hard disk is faster than accessing a web server). Remember: Computers store information using the binary system, addresses are represented in the hexadecimal system and in real-life we use the decimal system. A 3-bit number can be used to form 8 different combinations (including the 000 combination).
Introduction
Binary
Decimal Equivalent
000
0
001
1
010
2
011
3
100
4
101
5
110
6
111
7
18 If 3 bits are used then the maximum possible decimal number that can be represented is 7 (not 8 because the first number is 0). Similarly if an 8-bit number can be used to represent up to (2^8) different values (0 to 255 in the decimal system). A binary number can be either signed or unsigned. When a number is unsigned (we don’t bother about the sign), it means that the number is always positive. If a number is signed, then it could be positive or negative. +33 is a signed number (with a positive sign). In real life, we can use + or – to indicate whether a number is positive or negative but in computers only 1s and 0s can be used. Every decimal number has a binary equivalent. The binary equivalent for the decimal number 8 is 00001000. If this is a signed number then +8 would be written as 00001000 and –8 would be denoted by 10001000. Notice the difference between the two. The 7th bit (or the most significant bit) is set to 1 to indicate that the number is negative. Assume the number 127. For +127 you will write: 01111111 For –127 you will write: 11111111 For 255 you will write: ? Well, the value for 255 cannot be written using a signed 8-bit number (because the 7th bit is reserved for the sign). If the unsigned representation was used then 255 can be represented as 11111111 (in this case the MSB signifies a value and is not concerned about the sign of the number). What is the point to note here? By using a signed representation the maximum value that can be represented is reduced. An 8 bit unsigned binary number can be used to represent values from 0 to 255 (11111111 will mean 255). On the other hand, if the 8 bit binary number is a signed number then it can represent from –127 to +127 only (again a total of 255 values but the maximum value that can be represented is only 127). Beware: Signed representation in binary format will be explained in detail later. Computers store negative numbers in 2s complement rather than storing them directly as shown above. Remember: In signed numbers the MSB (in the binary representation) is used to indicate the sign of the number.
Introduction
19
Terms used in C++ •
Compiler : Before providing a technical explanation a compiler in layman’s language is a
program which will read through the C++ program code (code refers to the program written by the programmer) line by line. The compiler will read each and every character that has been typed in the program code. After reading the entire code, the compiler will convert the code into machine level format (i.e. 0s and 1s). The compiler is very dedicated and hard working since it will meticulously read each and every character. Technically speaking, the microprocessor in a computer can understand only binary numbers (only 0s and 1s). It is not possible for us to write programs in binary numbers. Compilers will convert the coding for a program into machine understandable form (i.e. it will convert the instructions we type into machine understandable form). The instructions we type (in something similar to English), is known as the source code. The compiler converts the source code into the object code (which the microprocessor understands and we cannot understand). There is a detailed discussion on this topic in the chapter “Advanced Topics”. If you are new to programming you might wonder as to where you should type your C++ program? First of all you should have a C++ compiler. There are many compilers available for free on the Net and they can be downloaded from the Net. After downloading the compiler, create a new C++ source file and type the C++ code in the compiler. Later on in this book, we shall see how to run a C++ program in the compiler. A few compilers available on the Net are : 1. 2. 3. 4.
Borland C++ compiler : www.borland.com/bcppbuilder/freecompiler/ Bloodshed Dev C++ compiler : www.bloodshed.net/devcpp.html DJGPP C++ compiler For Linux/Unix the GNU C++ compiler is available in standard installations of the operating system.
Introduction
20 Most compilers nowadays provide an Integrated Development Environment (IDE), i.e. they have an editor, a compiler, a linker and some extra tools (like a debugger). In schools and colleges the most commonly used compiler is Turbo C++. Most of the programs written in this book are compatible with Turbo C++. It is better to use Visual C++ because advanced features like namespaces and exception handling are not available in older versions of Turbo C++. All compilers will support the basic features of C++. If you are using Linux/Unix then you will be having the GNU C++ compiler in your Linux distribution CD (this compiler is freeware but it doesn’t have a graphical user interface- it can only be used from the command line; refer to the Appendix section).
•
Keywords :
Keyword is a word that the compiler already knows, i.e. when the compiler sees a keyword somewhere in the program it knows what to do automatically. For example, when the compiler encounters the keyword ‘int’, it knows that ‘int’ stands for an integer. Or if the compiler reads a ‘break’, then it knows that it should break out of the current loop.
•
Variable and Constant :
As the name suggests, a variable is something whose value can be changed throughout the program. It is not fixed. On the other hand, a constant is one whose value remains the same (constant) throughout the program.
•
Operators and Operands:
Operator is something that performs operation on a variable. For example + is an operator that performs the addition operation. The terms on which the operator operates are known as the operands. In the expression x+y x and y are known as the operands and + is the operator.
Introduction
21 •
Expression:
Expression performs an operation and consists of operators and operands (or variables). a>b This is an expression in which a and b are the operands. > is the operator.
•
Binary and Unary:
Binary means ‘two’ and ‘unary’ means one. A binary operator operates on two operands (or two values) whereas a unary operator operates on just one operand.
•
Definition and Declaration :
In C++ these two terms are used frequently. Declaration of a variable means telling the compiler that this variable is an integer, or this variable is a character. A declaration will inform the compiler about the type of a variable. Defining a variable means allocation of memory space by the compiler for that particular variable. In the case of variables, a single statement performs both declaration and definition. For example: the statement int x; declares and defines ‘x’ as an integer. Remember that declaration is just declaring the type of something (telling the compiler what data type it belongs to) but definition means allotting space to it. It may seem as if both are one and the same; in many cases they are but in a few places they are not (these cases will be dealt with later).
•
Initialization :
Initialization refers to the first (initial) assignment of a value to a variable. x = 50; This initializes the value of x to 50. Always remember that the value to the right of the equal sign will be assigned (or stored) to the value on the left of the equal sign. For example: a = b; This means that value of b is assigned to a.
Introduction
22 •
Parentheses, braces, angle and square brackets :
There are different type of brackets: • • • •
Parentheses are just the normal brackets ( ). Braces are { }. Angular brackets are < >. Square brackets are [ ].
All of these brackets are used in C++ but each one is used for different purposes.
•
Character:
Characters include alphabets, numbers and other special symbols as well (like *, /, - etc…). Usually one tends to think of characters as only alphabets; but remember a character could be anything (an alphabet, a number, a special symbol on your keyboard or even a blank white space is a character).
•
Syntax : Every language has its own ‘syntax’. By the term syntax we refer to the
structure of a language and use of correct language. English has its own set of rules while French has slightly different rules. The vocabularies of both the languages are also different. Programming is also another language (that’s it is called programming language). Just like any other language it has its own set of rules that must be complied with. This is known as the syntax. Only if the programmer adheres to the syntax will the compiler understand the instructions.
•
Errors: This is a very common term in programming languages. In fact it is very
common to hear someone say, "Syntax Error". Error means a mistake and syntax error means that there is a mistake in the syntax. The compiler reads the entire program before execution. While reading the program, if the compiler discovers that it can’t understand
Introduction
23 some instruction it will display an error message. Till the error is cleared, the program cannot be executed. The compiler will give a syntax error if the syntax has not been adhered to properly. Errors generated by the compiler are known as compile-time errors. There are other types of errors as well. The compiler is not intelligent to detect all types of errors. For instance if the programmer make a logical error, the compiler will never know it. Suppose a program is written for adding two numbers and in the coding, instead of the + operator, the – operator has been used. The compiler will not recognize any error during compilation and the program will execute (but the programmer will not get the desired output). This is a logical error and as a programmer you have to be very careful while coding. The compiler is not brilliant enough to know what is in the programmer’s mind!
•
Executable file and linkers:
Executing refers to the process of running a program. We’ve talked about object file, but can we execute object files directly? The compiler produces an object code but this object code generally depends on some other external files. For example: if you are using complex mathematical functions, you might make use of functions which have been defined by some other programmer. That programmer would have created a library which you can include in your source code (instead of creating those functions in your program). A library is a collection of object files. Thus your program will actually depend on the other object files for some of the complex mathematical functions which you’ve used. Someone now has to link up all these object files to produce an executable file. This work is done by the ‘linker’. Most modern compilers have a linker included in them. Linkers are discussed in detail later. Remember: Each C++ source code needs to be compiled separately to produce an object code (ten source codes mean that we’ll have ten object codes). Finally the linker has to combine these ten object codes to produce one single executable module. •
Debugging:
is the process of troubleshooting a program (of course it’s done when the program doesn’t work up to your expectation!). Debugging is the process of identifying bugs in the code. A bug is a programming term for an ‘error’.
Introduction
24
Some extra details: An interesting point (about Kilo, Mega etc.): You might have read that 1Kilobyte = 1024 bytes and perhaps you thought about it as well, “Shouldn’t 1Kilobyte = 1000 bytes?” Let’s start with our familiar decimal system. In the decimal system, 1 Kilo unit = 1000 units. For example: • • •
This is fine as far as the decimal system is concerned. Here the base used is 10 and we express everything in powers of 10. Computers speak in binary language and the base used here is 2. Let’s extend the concept of ‘kilo’ to the binary system. Can we represent the value 1000 as a power of 2? Let’s try: Power 20 21 22 23 24 25 26 27 28 29 210
Value 0 2 4 8 16 32 64 128 256 512 1024
Oops! We’ve just exceeded 1000 in 210. In the binary system, the closest we come to 1000 is 1024. Thus 1 Kilobyte = 1024 bytes (but for rough calculations we assume it as 1000 bytes).
Introduction
25
Recap of the First Unit: ¤
Computers only understand binary language (1s and 0s).
¤
8 bits form a byte.
¤
The different types of storage are: internal CPU registers, RAM, ROM and secondary storage.
¤
RAM is used as main memory and data is present only as long as the computer is switched on.
¤
Main memory is directly accessible by the computer whereas secondary memory is not.
¤
During execution programs should be present in the main memory.
¤
Word-size of a machine depends on the size of the processor’s internal registers.
¤
Machine level language is written in 1s and 0s.
¤
Assembly level languages make use of mnemonics (mnemonics are abbreviated English words used to represent instructions).
¤
High level languages like BASIC are in simple English but they are far away from the real hardware of the system.
¤
C has the advantages of both high level and low level languages. But it doesn’t support Object Oriented Programming (OOP).
¤ ¤
¤
C++ is actually C with classes (i.e. it supports OOP). A compiler is required to convert the source code (i.e. the instructions we type in English) into the object code (i.e. the machine language which the computer understands). 1 kilobyte = 1024 bytes (and not 1000 bytes).
Introduction
Your very first C++ Program
26
All the example programs in this book have been tested in Turbo C++ compiler (the programs were also tested in Visual C++ 6.0). They should run on other C++ compilers as well. Let us start with a basic C++ program. This will give you an idea about the general structure of a C++ program. Let’s write a program in which the user can enter a character and the program will display the character that was typed: // My first C++ program # include int main( ) { char letter; cout << "Enter any letter" ; cin >>letter; cout << "The letter you entered is : " <
Explanation for the above program: z
// My first C++ program: The first line in the above program forms the ‘comments’. In C++, any line that is typed after the two slashes ( // ) is known as comments. The compiler does not read comments. Comments can be used anywhere in a program and you can write anything that you want in the comments. Comments are useful when a programmer (or someone else) reads the source code in the future. Proper comments will make it easier for anyone to understand the program logic. When you write a very long program, you will appreciate the use of comments.
z
# include , it will physically include the iostream header file in your source code (i.e. the entire file called iostream.h file will be pasted in your source code). This is one of the standard header files which you’ll use in almost all of your C++ programs.
z
int main( ) : Every C++ program has to have one ‘main’ function. Functions will be explained later. Data Types & Variables
Remember that the compiler will execute whatever comes within the ‘main’ function. Hence the instructions that have to be executed should be written within the ‘main’ function. Just as the name implies, ‘main’ is the main part of the C++ program and this is the entry point for a C++ program.
27
z
{ : Functions are defined within a block of code. Everything within the opening and closing braces is considered a block of code. ‘main’ is a function and hence we define this function within braces. This is as good as telling the compiler, “The ‘main’ function starts here”.
z
char letter; : ‘letter’ is the name of a variable. Instead of the name ‘letter’ we could also use any other name. ‘char’ defines ‘letter’ as a character variable. Therefore, the variable ‘letter’ will accept the value of only one character.
z
cout << "Enter any letter" ; : This is followed by the << operator (known as the insertion operator). Following this operator, anything typed within double quotes will be displayed on the screen. Remember that cout and << go together. ‘cout’ is known to the compiler already (since it is defined in the iostream header file which has been included). So when the compiler comes across cout<<, it knows what to do.
z
cin >>letter;: cin is the opposite of cout. cin>> is used to obtain the value for a variable from the user. (>> is known as the extraction operator). Input is usually obtained from the keyboard.
z
return 0; : This statement tells the compiler that the ‘main’ function returns zero to the operating system (return values will be discussed in the chapter on functions).
z
}: The closing brace is used to tell the compiler that ‘this is the end of the function’. In our case, it is the end of the ‘main’ function and this indicates the end of the program as well.
You might have noticed in the above program that every statement is terminated with a semi-colon (;). That is exactly the use of the semi-colon. It is used to tell the compiler that one instruction line is over. If you don’t put semi-colons in your program, you will get errors while compiling. A variable is used for temporary storage of some data in a program. In the above example, ‘letter’ was a variable. Every variable belongs to a particular type (referred to as data type). The different data types in C++ are discussed later. In the above program: char letter; declares ‘letter’ as a character variable (a character is one of the basic data types in C++). When the compiler encounters this statement, it will allocate some memory space for this variable (i.e. any value which is assigned to the variable ‘letter’ will be stored in this allocated memory location). When the required memory space has been allocated we say that the variable has been defined (in this case a single statement will declare and define the variable ‘letter’).
Some points to remember: When we want to display something on the screen we will code it as: cout<>variable-name; Data Types & Variables
Beware of the direction of the >> and << operators. In the statement cout<
28
information flows from the variable into ‘cout’ (which means it goes to the monitor display). When we say cin>>variable; information (the value of the variable) flows from ‘cin’ (which would be the keyboard) into the variable (i.e. the value is stored in the variable). Information will flow in the direction of the arrows (<< or >>). ‘cout’ is linked to the standard display device (i.e. the monitor) while ‘cin’ is linked to the standard input device (i.e. the keyboard). Hence, you can’t use cout>>variable; This will cause an error while compiling. ‘cout’ and ‘cin’ are already pre-defined and so you can use them directly in your programs. 'iostream.h’ is a header file that is used to perform basic input and output operation (or general I/O like using ‘cin’ and ‘cout’). Remember: C++ is a case-sensitive language, which means that the compiler will not consider ‘letter’, ‘LETTER’ and ‘Letter’ as the same. Data Types & Variables
How to run your first program in the29 compiler? Saving and compiling the program: There are many C++ compilers available in the market. Some of them are freeware (meaning that they are free to use) while others have to be paid for. Turbo C++ compiler might be the simplest to use (but it is not freeware). Simply choose "New File" and type out the program coding. Suppose you are using some other compiler, click on File and choose "New". Some compilers may have the option of creating a C++ source file while other compilers may require a new project to be created. Whatever the method you will ultimately come to the step of creating a C++ source file. After typing the code in the compiler, save the file by giving it some name. The "Save As" option will appear under the "File" menu. Give a name (for example: first). Now the file is saved as first.cpp. All C++ source files are saved in *.cpp format. Just like *.doc represents a Word document file, *.cpp denotes a C++ (C Plus Plus) source file. In the compiler program there will be an option called ‘Compile’ in the menu bar. Select the compile option and the compiler will do its work. It will compile the program (or in other words, it will read whatever has been typed) and in case there are any errors, the compiler will point out the line where the error was detected. Check whether the program has been typed exactly as given earlier. Even if a semi-colon is missing, it will lead to errors. If the compiler says no errors (or if the message "compiled successfully" appears), then you can go to the next stage: building the *.exe file. *.exe file extension stands for executable files. A *.cpp file cannot be run directly on the computer. This has to be converted into a *.exe file and to do so select the "Make or Build exe" option in the compiler. The file ‘first.exe’ will be created. Now the program can be executed from the DOS prompt by typing ‘first’. But instead of running the program every time from DOS, it will be convenient to run the program from the compiler itself and check the output. In the compiler there will be another option called "Run". Just click on this and the program will run from the compiler itself (you needn’t switch back and forth between DOS and the compiler screen). The figure should give you a rough idea as to how the executable file is created from the C++ source code.
Data Types & Variables
30
The source code is the program that you type. Source code is converted into object code by the compiler. The linker will link your object code with other object codes. This process of creating a C++ program is discussed in the last chapter.
Modification that might be needed in first.cpp (depending on your compiler): A little problem might be encountered while running your program from the compiler (this problem will exist in Turbo C++ compiler). While running the program from the DOS prompt, this problem will not occur. What’s the problem? When first.cpp is executed from the compiler the program will ask the user to enter a character. Once a character has been entered, the program will return to the compiler screen. You won't see your output! It might appear as if there is some problem with the program. What happens is that the program displays the character on the screen, immediately terminates the program and returns to the compiler screen. This happens so fast that you can’t see the output being displayed. Data Types & Variables
Modify your program as shown below (if you are using Turbo C++):
31
// Your first program modified: first.cpp # include # include int main( ) { char letter; cout<< "Enter any letter" ; cin>>letter; cout<< "The letter you entered is : " <
Another header file called conio.h has been added. The function getch( ) is used at the end of the program. getch ( ) is defined in the conio.h header file. getch( ) stands for ‘Get Character’. When getch ( ) is executed, the program will wait till the user types a character (any character). Only after a character is typed will the program move to the next instruction. In the above program, there is no statement other than return 0; after getch ( ). Hence program flow is as follows: The program asks the user to enter a letter. The user enters a letter. Then the program displays the typed letter on the screen. The program will not quit at this point since there is a statement getch ( ). It will wait till the user types another character. When the user presses a character the program will quit. Type the above program, save and run it from the compiler. Some compilers have named the function getch( ) as _getch( ). This function also requires the conio.h header. Remember: conio.h is not available with all compilers and getch( ) function is not a standard C++ function. Some compilers provide it but most do not. In other compilers if a similar problem is encountered while displaying the result try using getchar( ); instead of getch( ); getchar( ) function doesn’t require the conio.h header file. If the compiler does not recognize getchar( ), then instead of adding any of the in-built functions, just add another line as follows: cin>>letter; at the end of the program just before return 0; Data Types & Variables
The program flow will be the same as described earlier.
32
The latest compilers, like VC++ (Microsoft Visual C++ compiler) do not have any of the above problems even if the program is run from the compiler. VC++ will always ask the user to press a character to terminate the program. Another alternative is to use a function called ‘system’, which is defined, in the header file: stdlib.h. z
system("PAUSE");
can be used to pause the program (i.e. execution of the program will continue only when the user presses a key). z
system("CLS");
can be used to clear the display screen. To use these two functions you have to type #include header file in your source code.&The system( ) function actually executes a DOS command. (Try giving the commands Data Types Variables ‘cls’ and ‘pause’ in your DOS prompt).
Data Types
33
The following topics are covered in this section: z
Introduction
z
Integer
z
Floating Type
z
Double
z
Character
z
Boolean
z
Data Type Ranges and determining the ranges
z
More on Binary Numbers
Every piece of data has to belong to some basic category. Consider a simple example in real life: every number has to be of a particular type. The number 5 is a natural number (or it can be called as a whole number). 6.5 is a real number (it has a decimal point). Similarly, in programming we have what are called as data types. When a variable is declared, the programmer has to specify which data type it belongs to. Only then will the compiler know how many bytes it should allocate for that particular variable. Or in other words, each data type occupies a different memory size and if a variable is declared as belonging to one particular data type it cannot be assigned a different data type value. In simpler terms, suppose the variable ‘x’ is declared such that it can hold only whole numbers; then it cannot (and should not) be assigned some alphabet. There are two categories of data types: fundamental data types and user-defined data types. The second category of data types will be dealt with later. The fundamental (or built-in or primitive) data types are: z z z z z
Integer Floating Point Character Double Bool
The first three data types: integer, floating point and character are used frequently.
Integer (int): An integer can contain only digits (numbers) from 0 to 9. Examples of integers are: z z z z
0 10 345 6789
Data Types & Variables
z z
-23 -600
34
It includes positive and negative numbers but the numbers have to be whole numbers. It does accept the decimal point. Hence the following numbers are not integer data types: z z z
3.5 4.8 0.23
These numbers come under the second category (floating point type) and not under integers. If the program has to accept such values from the user do not declare the variable as an integer. If a variable is declared as an integer and the user enters a value of 2.3, the program will assign 2 as the value for that integer variable. Similarly, if the user enters 3.2, the program will assign 3 to the integer variable. Remember: Once a variable is declared as an integer, it will only store whole numbers (if the user types a value with the decimal point, the program will ignore everything that is typed after the decimal point). How to declare a variable as belonging to the type integer? The syntax is: int variable-name; Each data type occupies a certain amount of memory space. An integer will occupy 2 bytes of memory (which means 16 bits). From this it is possible to calculate the maximum and minimum values that an integer can store. 2^16 = 65536 (hence 65536 different combinations of 16 bits are possible). Divide this by 2 because integers (by default) range from negative to positive values. We have a 0 in between and so subtract one from this to get 32,767. Hence an integer can take values from –32,768 up to +32,767 (a total of 65536 different values). A natural question springs to mind, "What would happen if a value greater than 32,767 is entered?" Since this value cannot be accommodated within the allocated two bytes, the program will alter the value. It’s not exactly altering the value; it will basically change your value into something different. The user might enter 123456 as the integer value but the program will store it as –7623 or something like that. Whenever you use variables ensure that you have declared them as belonging to the correct data type. This restriction on maximum range might seem to be a problem. In C++ ‘qualifiers’ can be used to vary the range of fundamental data types. Qualifiers are only supplements to the basic data types and they cannot be used separately on their own. They work only with a basic (or fundamental) data type. The 4 qualifiers available in C++ are: 1. 2. 3. 4.
Short Long Signed Unsigned
Signed and unsigned integers were discussed in the first chapter. When an integer is specified as signed, then automatically the most significant bit of the number is used as a sign bit (to denote the sign of the number). Hence it can be used if the programmer needs positive and negative number values for the variable. By declaring a variable as an integer, by default you can specify both positive and negative values. By default an integer is a signed integer. In other words, int variable-name; is the same as signed int variable-name; In the second form, ‘signed’ is the qualifier and it is used to explicitly state that the variable is a signed integer. For an unsigned integer the syntax will be: unsigned int variable-name;
Data Types & Variables
An unsigned integer can hold a value up to 65,535 (a signed integer can hold only up to 32,767). Of course, in an unsigned integer you 35 cannot assign a negative value. The range is from 0 to 65,535. To go beyond 65,535 and make use of both positive and negative values as well, the qualifier long should be used. long int variable-name; Long integers occupy 4 bytes of memory (32 bits). Remember, long int actually means signed long int (you can give positive and negative values). If you specify unsigned long int variable-name; you can only assign positive values to the variable. Thus, two qualifiers can be used together with a basic data type. What about the ‘short’ qualifier? Short integer is the same as a signed integer. It occupies two bytes and has the same range of positive and negative values as the normal integer case. int x; is usually the same as short int x; Compilers (depending on the operating system) will assume ‘int’ as a ‘long int’ or a ‘short int’. VC++ (since it works in the Windows OS) will default to ‘long int’ if you specify a variable as type ‘int’ (i.e. it will allocate 4 bytes to an ‘int’ variable). Turbo C++ (which is a DOS based compiler) will default to ‘short int’ when you specify a variable as type ‘int’. Thus the statement: int var; will allocate ‘var’ 4 bytes if you are using VC++ but the same statement will allocate 2 bytes if you are using Turbo C++ compiler. Programmers sometimes prefer to explicitly state what type of integer they want to use by making use of the ‘short’ and ‘long’ qualifiers. ‘short int’ always occupies only 2 bytes (irrespective of whether the OS is Windows or DOS) while a ‘long int’ always occupies 4 bytes. Two qualifiers can be used together, but do not try using: short long int variable-name; This will cause a compile-time error. So be careful with what qualifiers you use. And remember that the default for int is equivalent to short signed integer.
Floating Types (float): Floating type data include integers as well as numbers with a decimal point. It can also have an exponent. Exponent means 10 to the power of some integer value (whole number). 20000 = 2 x 10^4 = 2e4 = 2E4. If you specify decimal numbers, floating point data type will store up to a precision of 6 digits after the decimal point. Suppose 0.1234567 is assigned to a floating-point variable, the actual value stored would be 0.123457 (it will round up to the sixth digit after the decimal place). Valid floating-point numbers are: z z z z
0.1276 1.23 1.0 10.2
Data Types & Variables
z
2e5 (this will be typed in your code as 2e5)
36
Do not use an exponent with a decimal point. For example: 2e2.2 is an invalid floating point because the exponent has to be an integer. Floating point numbers use 4 bytes of memory and has a much greater range than integers because of the use of exponents. They can have values up to 10^38 (in positive and negative direction). The same qualifiers used for an integer can be applied to floating point numbers as well. To declare a floating variable, the syntax is: float variable-name;
Double (double): This is similar to the floating-point data type but it has an even greater range extending up to 10308. The syntax to declare a variable of type double is: double variable-name; Beware: Visual C++ (VC++) usually uses its default as ‘double’ instead of ‘float’. Suppose we type: float x=31.54; you will get a warning message saying that a ‘double’ (i.e. 31.54) is being converted into a floating point. It is just to warn you that you are using a ‘float’ and not a ‘double’. (Even if there are warnings, there won’t be any problem in running your program).
Character (char): A character uses just one byte of memory. It can store any character present on the keyboard (includes alphabets and numbers). It can take numbers from 0 to 9 only. The following are valid characters: z z z z z z z
A B 3 a : ‘ /
If the number 13 is entered as the value for a character, the program will only store 1 (i.e it will store the first character that it encounters and will discard the rest). A character is stored in one byte (as a binary number). Thus whatever the user enters is converted into a binary number using some character set to perform this conversion. Mostly all computers make use of the ASCII (American Standard Code for Information Interchange). For example, according to the ASCII coding, the letter ‘A’ has a decimal value of 65 and the letter ‘a’ has a value of 97. There is another form of coding called the EBCDIC (Extended Binary Coded Decimal Information Code) which was developed by IBM and used in IBM computers. However, ASCII remains the most widely used code in all computers. The table at the end of the book gives a listing of the ASCII values and their equivalent characters. The syntax to declare a variable which can hold a character is: char variable-name;
Boolean Type (bool) Data Types & Variables
This data type will only accept two values: true or false. In C++, ‘true’ and ‘false’ are keywords. Actually a value of true corresponds 37 to 1 (or a non-zero value) and a value of false corresponds to 0.
#include int main( ) { bool check; check = true; cout<
//output will be 1 (since ‘check’ is true)
Remember: The size of data types given above is a general case. Data type sizes depend on the operating system. So it may vary from system to system.
Data Type Ranges Bytes Name
(Compiler dependent)
Description
Range (depends on number of bytes)
char
1
Character or 8 bit integer.
signed: -128 to 127 unsigned: 0 to 255
bool
1
Boolean type. It takes only two values.
true or false
short
2
16 bit integer.
long
4
int
2/4
float
4
double
8
32 bit integer. Integer. Length depends on the size of a ‘word’ used by the Operating system. In MSDOS a word is 2 bytes and so an integer is also 2 bytes. Floating point number. double precision floating point number.
signed: -32768 to 32767 unsigned: 0 to 65535 signed:-2147483648 to 2147483647
Depends on whether 2/4 bytes.
3.4e + / - 38 (7 digits) 1.7e + / - 308
Remember: ‘short int’ always occupies only 2 bytes (irrespective of whether the OS is Windows or DOS) while a ‘long int’ always occupies 4 bytes. Determining the range: You might be wondering why the ranges are from -128 to 127 or from -32768 to 32767? Why not from –128 to 128? We’ll take the case of signed characters to understand the range. A character occupies one byte (8 bits). In a signed character the MSB (i.e. the 7th bit is used to denote the sign; if the MSB is 1 then the number is negative else it is positive). The binary number: 0111 1111 represents +127 in decimal value (we’ve seen about conversion from binary to decimal numbers in the first chapter). The number 0000 0000 represents 0 in decimal. But what about 1000 0000? Since the MSB denotes a negative number, does this stand for –0? Since there is no point in having a –0 and a +0, computers will take 1000 0000 as –128. 0000 0000 is taken to be zero. Thus in the negative side we have a least value of –128 possible while in the positive side we can have a maximum of only +127.
Data Types & Variables
Another point to note is that negative numbers in computers are usually stored in 2’s complement format. For example: 1000 0001 38 actually stands for –1 but if this number is stored in 2’s complement format then it stands for –127. Similarly 1000 0010 would appear to be –2 but is actually –126. To understand 2’s complement you should know about 1’s complement. Let us say we have a signed binary number: 1000 0001. The 1’s complement of this number is 1111 1110 (i.e. to find the 1’s complement of a binary number just change the 1s to 0s and 0s to 1s but do not change the sign bit). To find the 2’s complement just add 1 to the 1’s complement. Hence the 2’s complement of 1000 0001 is 1111 1111 (or –127). In the same way the 2’s complement of 1111 1111 (-127) is 1000 0001 (or – 1). Thus the computer instead of storing 1111 1111 (for –127) will store the 2’s complement of the number (i.e. it will store 1000 0001). Remember: When converting a number to its 2’s complement leave it’s sign bit unchanged. And numbers are stored in 2’s complement format only if they are negative. Positive numbers are stored in the normal format.
More on Binary Numbers: Everything in the computer is stored in binary format. Even if we ask the computer to store an octal number or a hexadecimal number in a variable, it will still be stored in binary format (we’ll see this later) because computers only understand 0s and 1s. How does a computer perform calculations? Yes, it has to do it in binary format. Let’s take an example of binary addition: 0
First of all, what 2 decimal numbers are we adding? Convert them to decimal and you’ll get: 106 and 52. When the computer has to add 106 and 52, it would in effect be adding the 2 binary numbers as shown above. Binary arithmetic is simple: 0+0=0 1+0=1 0+1=1 1 + 1 = 0 and a carry of 1
Use this and try out the addition of 106 and 52. Voila! You’ll get the answer of 158. Proceeding further, we need to investigate as to how negative numbers are really stored in computers. We discussed earlier that if +10 is represented as 0000 1010 then -10 would be represented as 1000 1010 (since the MSB is considered as the sign bit). Right? Let’s check it out. Data Types & Variables
39
If we add -10 to 10 then we should get the answer as zero. 0
And the answer is? -20. So, where did we go wrong? One question you might have is why did we add the sign bit also? The computer isn’t smart enough (or rather it doesn’t have the circuitry) to separate the sign bit from the rest of the number. The 2 numbers that you want to add, are fed as input to an adder circuit which will blindly add up both the numbers and give the result. To overcome this problem, we can make use of 2s complement. For example: 1000 0001 actually stands for –1 but if this number is stored in 2’s complement format then it stands for –127. Similarly 1000 0010 would appear to be –2 but is actually –126. To understand 2’s complement you should know about 1’s complement. Let us say we have a signed binary number: 1
0
0
0
0
0
0
1
1
1
0
The 1’s complement of this number is 1
1
1
1
1
To find the 1’s complement of a binary number just change the 1s to 0s and 0s to 1s but do not change the sign bit. Next, to find the 2’s complement just add 1 to the 1’s complement. 1
Hence the 2’s complement of 1000 0001 is 1111 1111 (or –127). In the same way the 2’s complement of 1111 1111 (-127) is 1000 0001 (or –1). Thus the computer instead of storing 1111 1111 (for –127) will store the 2’s complement of the number (i.e. it will store 1000 0001). Why? Let’s go back to our initial problem of adding +10 and -10. Now let’s assume that the computer stores -10 in 2’s complement format. The addition will now be: Data Types & Variables
Voila! The answer is 0. But you may ask ‘what about the carry of 1?’. Well, since it is an extra bit, it overflows (which means it is lost and we needn’t worry about it). I’m not going to get into more details of 2’s complement since this should be sufficient for learning C++. To learn more on this subject, you can check out books on digital systems or computer system architecture. Note: When you perform binary addition, 1 + 1 is equal to 0 and a carry of 1 (because in the binary system we only have 2 states: 1 and 0; so we can’t have 1 + 1 = 2 in binary). Remember: When converting a number to its 2’s complement its sign bit is unchanged. Numbers are stored in 2’s complement format only if they are negative. Positive numbers are stored in the normal format. Data Types & Variables
A few simple programs are explained in this section to help you get a feel of C++. First, let’s write a program to calculate the area of a circle. The variables needed in this program are the radius of the circle (which can be obtained as input from the user) and the constant ‘pi’ (whose value can be provided in the code itself). // To Calculate the Area of a Circle # include int main( ) { float PI = 3.14; // variables can be initialized during declaration int rad; cout<< "Enter the radius" ; cin>>rad; cout<< "Area of the circle is "<< PI * rad * rad; return 0; } The preprocessor has only one directive and it will include the iostream.h header file into the source code. The compiler will start reading the code from the main ( ) function onwards. Remember: Whatever is typed within the main ( ) function will be executed. The main ( ) function is used as the entry point for a C++ program. PI is a variable name and is declared as a float quantity (because the value of PI has a decimal point). At the point of declaration, PI is initialized to a value of 3.14. This means that whenever PI is used in the program, the compiler will use 3.14 instead of PI. The line: cout<<"Enter the radius"; will cause Enter the radius to be displayed on the screen. This is because "Enter the radius" is typed within double quotes following ‘cout’ and the insertion operator. Anything between double quotes, along with cout<< will be displayed on the screen just as it appears within the double quote. The value entered by the user will be stored in the variable ‘rad’. Then the statement "Area of the circle is " will be displayed on the screen. The compiler will calculate the value of ‘PI * rad * rad’ and display it at the end (* is the multiplication operator in C++). The output for the above program is: Enter the radius 9 Data Types & Variables
Area of the circle is 254.14
42
Bold indicates that the user entered the value. In this case 9 was entered as the radius. Suppose we type cout<< "rad"; the output will be just the word rad The value of the variable ‘rad’ will not be displayed. Remember: When you want to display some variable’s value on the screen, DO NOT ENCLOSE IT IN DOUBLE QUOTES; just mention the name of the variable after the insertion operator.
Initializing variables: It is a good idea to initialize variables at the time of declaration. Even if you are unsure of the value you can still initialize it to 0. If you are wondering why, just consider the example below: #include int main( ) { int correct, choice; cout<<"\nEnter your guess of the lucky number: "; cin>>choice; if (choice= =correct) { cout<<"\nCongrags. You are correct!"; } else { cout<<"\nSorry. Wrong guess"; } cout<
The problem is that in the program we haven’t assigned a value to the variable ‘correct’ and neither have we 43 initialized it. Thus this variable has an unknown value (also called garbage value). There are 2 ways to initialize variables: int correct = 25; or int correct(25); The second form is called functional notation and we generally don’t use this to initialize in-built data types (like integers, characters, double etc.). This form is used to initialize user defined data types (which we’ll discuss in the chapter on Classes).
Another example program: //Can you guess what this program is for and how it works? #include int main( ) { char check; int i; cout<<"Enter the character that you want to convert to ASCII : "; cin>>check; i = check; cout<<"The ASCII value for "<
as an integer, the number will be displayed as it is (without going through the ASCII coding process).
44
Try it: Write a program that will do the reverse process (i.e. type a number and the program has to display the corresponding character). Data Types & Variables
45
Identifiers, File Nomenclature, Keywords, Constants, Comments The following topics are covered in this section: Identifiers File Nomenclature Keywords Constants Comments
Identifiers: Identifiers are very important in C++ and are used in all programs. What is an identifier? In the previous program ‘check’ was declared as a character variable. The identifier in this case is ‘check’. Basically identifiers are names that are given to variables, constants or functions. In the previous program there are two identifiers: ‘check’ and ‘i’. C++ permits the use of a huge variety of names for identifiers like: test Test int_test var1 var123 c_b_f _var Almost any name you can think of can be used as an identifier but there are some restrictions. An identifier should not start with a number. The first character of an identifier should be an alphabet or an underscore ( _ ). After the first character, numbers can be used in the identifier. Remember the following: Never start an identifier with anything other than a letter or an underscore. It is better to use a letter than starting with underscores. Do not use keywords as identifiers (ex: do not name an identifier as int). Uppercase and Lowercase identifiers are different (Check is different from check). Be careful when using characters that look similar to each other. For example ‘1’ (the number one) and ‘l’ (the lowercase alphabet L) look alike. Use names that are easy to understand (if you are storing the salary of a person, name the variable as ‘salary’ instead of declaring it as ‘x’. Do not use very long names. Do not name many variables with similar names (avoid using identifiers like: count, counter, counting etc.).
Data Types & Variables
46
Keywords: Keywords are reserved words in C++ programming. They should not be used as identifiers and all keywords should be in lower case. The 63 keywords are tabulated below. Asm
Auto
bool
break
case
Catch
char
Class
Const
const_cast
continue
default
Delete
do
Double
Dynamic_cast
else
enum
explicit
Export
extern
False
Float
For
friend
goto
If
inline
Int
Long
mutable
namespace
new
Operator
private
Protected
Public
register
reinterpret_cast
return
Short
signed
Sizeof
Static
static_cast
struct
switch
Template
this
Throw
True
Try
typedef
typeid
typename
union
Unsigned
Using
virtual
void
volatile
wchar_t
while
File Naming Convention: A few points regarding file nomenclature are: In MSDOS a filename cannot have more than 8 characters whereas in Windows there is no such restriction. Use meaningful names (depending on what the program performs). Avoid using underscores in the file names. Be careful when using characters that look similar to each other. For example ‘1’ (the number one) and ‘l’ (the lowercase alphabet L) look alike.
Data Types & Variables
47 File Type
Extension used
C++ source file
*.cpp
C++/C header file
*.h
C source file
*.c
Executable file
*.exe
Object Code
*.obj
Text file
*.txt
Word document
*.doc
Image/graphics file
*.jpg/ *.gif/ *.bmp
Constants: Constants are used in expressions and their values cannot be changed. They are used in the program code without using any variable name to refer to them. For example consider the statements: int x = 5; float y = 3.33; In this, 5 is an integer constant and 3.33 is a floating type constant. You cannot change 5 or 3.33 but you can assign these values to variables (the variables in the above code fragment are ‘x’ and ‘y’). Constants can be classified based on their data type as follows: Numeric type constants (includes integers, floating-point etc.) Character Constants String Constants Numeric Constants: Numeric constants consist of a series of digits. Integer constants can be written in different number systems: hexadecimal (base 16), octal (base 8) or in decimal (base 10). A decimal integer constant is any normal whole number (can consist of digits from 0 to 9):
Data Types & Variables
48 2,34, 100, 900, 1456 etc. An octal integer constant can contain digits from 0 to 7 only and it should start with a zero (so that the computer will know it is an octal number). For example: 023, 0567, 0214 etc. A hexadecimal integer constant should start with 0x or 0X and can consist of digits from 0 to 9 and A to F (uppercase and lowercase letters are allowed). For example: 0x10, 0x1FF etc. Floating point constants will have the decimal point in the number. Character and String Constants: Character constants are single characters enclosed within two single quotes (or between two apostrophes). For example: ‘a’ ‘b’ ‘x’ ‘1’ ‘A’ ‘*’ A single character enclosed within single quotes is a character constant. All character constants will have an integer value (determined from the ASCII table). A C++ statement: char ch = ‘B’; will assign the character constant ‘B’ to the character variable ch. String constants consist of a series of characters enclosed within double quotes. For example: "hello" "This is a string" "x" Even "x" is a string because it is enclosed in double quotes. "x" is different from ‘x’ (this is a character constant). The reason is because "x" actually consists of ‘x’ and ‘\0’ (the null character which will be
Data Types & Variables
49 explained later). Constants are also called as ‘literal’. You might come across the terms character literal, integer literal, string literal etc. Beware: The constants described in this section are different from variables using the ‘const’ keyword (this is discussed in Unit 6).
Comments: Comments are not meant for the compiler to read. They are meant to make the program easier to understand for humans who might read the coding later. It is a good practice to write comments (even for small programs). What should be written in the comments? First of all, a couple of lines about the aim of the program should be mentioned. You should also mention the name of the programmer and the date when the program has been modified. Comments should be updated every time you modify the code. As the program gets larger, it is advisable to write comments for the various functions that are used in the program. For instance, if a function is used to calculate the sum of numbers, then this could be mentioned in the comments. Basically you are permitted to write anything within comments because it will not affect the program. There are two methods used to denote comments: Single line commenting (//) Multiple line commenting (/*…..*/) For single line comments use double slashes (//) followed by the comments (but it should be on the same line as the double slash). Example: //this is a single line comment Multiple line commenting permits you to write line after line of comments. It will be as follows: /* Multiple line commenting… …this is also part of the comment… ……comment continued…… */ Everything in between /* and */ will be commented. By using multiple line commenting the programmer needn’t use double slashes for each and every line.
Recap:
Data Types & Variables
50 The file iostream.h provides the general basic input and output functions. ‘cout’ is used to display data on the screen while ‘cin’ is used to obtain inputs from the user. Every variable/function in C++ has to have a name. This name is called an identifier. Keywords are specially reserved words in C++. They cannot be used as identifiers. There are two data types in C++: fundamental and user-defined. Variables are named memory locations (their name is provided by the programmer) where values can be stored and changed frequently in the program. Constants are usually used to assign values to the variables. All data types have different sizes and are used for specific purposes. The type of a variable determines what it can store. Comments are written for easy understanding of the program (they are not processed by the compiler).
Data Types & Variables
51
C++ Operators - I The following topics are covered in this section: • •
Arithmetic Operators Escape Sequences
1. Arithmetic Operators These operators are used to perform basic mathematical operations like addition, subtraction, division and multiplication. Operator Symbol
Operation performed
+
Addition
-
Subtraction
*
Multiplication
/
Division
%
Modulo operator (Remainder after division)
The modulo operator is not used to calculate the percentage. It is used to find the remainder after integer division. For example: 6 % 4 = 2 (because dividing 6 by 4 produces a remainder of 2). The modulo operator can operate only on integers. Since the modulo operator is used to determine the remainder after division, it is also called the remainder operator. Arithmetic operators are binary operators since they need two quantities (or operands) to perform an operation.
Operators - I
52 #include int main( ) { int num1, num2; cout<<"Enter the two numbers : "; cin>>num1>>num2; cout<<"The product is : "<>num1>>num2; This is a method of obtaining multiple inputs using a single statement. The above statement is equivalent to writing: cin>>num1; cin>>num2; The two numbers, when entered by the user, can be separated by a space or by a new-line (i.e. the first number is typed and then after pressing the ‘enter’ key the second number is typed). When you run the program you would get the following on your screen: Enter the two numbers : 8 4 The product is : 32The sum is : 12The difference is : 4The quotient is : 2The remainder is : 0 Something is not right in this output; the results are correct but the display is on a single line. To display the output in an organized manner, the program should print each output on a new line. For this purpose of formatting the output C++ provides us with ‘escape sequence’.
Operators - I
53
Escape Sequences/ Backslash Character Constants If you remember, whatever you type within double quotes following cout<<, will be printed as it is on the screen. There is a problem in case you want to print a new line, or you want to use tabs (because whatever you type within double quotes will be displayed directly on the screen). To solve this problem, escape sequences were developed. Just as the name implies, these escape sequence characters are used to escape from the normal sequence of events. An escape sequence always begins with a backslash ( \ ). For a new line, the escape sequence is \n (n for new line). If you want to push the tab setting then \t should be used (t for tab). The modified program for doing simple arithmetic operations is as follows: #include int main( ) { int num1, num2; cout<<"Enter the two numbers : "; cin>>num1>>num2; cout<<"\n The product is : "<
There are a few other useful escape sequence characters as well: Operators - I
54 Escape Sequence
Meaning
\n
new line
\t
horizontal tab
\a
bell sound
\\
Backslash
\?
question mark (?)
\"
double quotation
\’
Apostrophe
\0
Null character (used in strings)
Though escape sequences consist of 2 characters they represent only a single character (for example: \? represents the question mark character). Escape sequences are character constants and are denoted as ‘\\’, ‘\?’, ‘\0’ etc. Beware: ‘\0’ is not equivalent to zero. ‘\0’ represents a null character (used in strings). Try it: Use the above escape sequences in a C++ program to see their effects. The \a escape sequence will literally make a sound of a bell. Remember: Escape sequences always start with a backslash ( \ ) and have to be followed by valid characters (like n, a, t etc…). If you happen to use some other character (like w), the compiler will display a warning message (the program will run and usually the compiler will ignore the character or display the character on screen). Coming back to the program with arithmetic operators; what would be the output if 5 and 2 are entered as the two numbers? What will the quotient and remainder be? The result will give a quotient of 2 and a remainder of 1. But isn’t 5/2 = 2.5? The answer to this lies in the declaration of the variables ‘num1’ and ‘num2’. Since these two variables are declared as integers, all the results of arithmetic operations will also be in the integer format. Suppose ‘num1’ and ‘num2’ are declared as floating point data type, then the result of division would be 2.5 instead of 2. In this case, what do you think will be the remainder? Will it be zero always? Remember: The modulo (%) operator can operate only on two integers and not on any other data type. If the remainder operator is used on floating point numbers, the compiler will produce an error message. The other arithmetic operators can be used on integers as well as floating-point numbers.
Operators - I
55
C++ Operators - II The following topics are covered in this section: • •
Assignment Operators Type Conversion
2. Assignment Operators The assignment operator assigns the value on its right-hand side to whatever is present on the left-hand side. The ‘equal to’ symbol ( = ) is the C++ assignment operator. For example, the statement x = 2; assigns the value on the right-hand side of the ‘equal to’ sign to the term on the left (i.e. the value 2 is assigned to the variable x). The term which appears on the left-hand side of the assignment operator (usually a variable) is called the ‘target’. You will always assign a value to some variable but not to a constant. 2 = x;
//Error
is a wrong statement because you cannot assign the value of x to 2 (2 is an integer constant and you cannot change its value). The value on the right side of the assignment operator can be an expression as shown below (or a function that returns a value): x = num1 + num2; y = 5*6; Compilers often use the terminology ‘lvalue’ (or Lvalue) and ‘rvalue’ especially in error messages. The ‘lvalue’ refers to the operand on the left-hand side of the assignment operator while the ‘rvalue’ refers to the operand on the right-hand side of the assignment operator. The lvalue can be a variable or a pointer but it should not be a constant. The rvalue can be a constant, variable, an expression or a function call. Beware: Lvalue can be used as rvalue (i.e. on the right-hand side of an assignment) but rvalue cannot be used as Lvalue.
Operators
56 C++ permits multiple assignments (i.e. more than one variable can be assigned a value using one statement). For example let x, y and z be integers, then x = y = z = 3; is an example of multiple assignment. The process of assignment will be from right to left; 3 is assigned to ‘z’ and the value of ‘z’ (which is now 3) is assigned to ‘y’ and y’s value is assigned to ‘z’. The above multiple assignment is equivalent to the following set of statements: z = 3; y = z; x = y; and all the three variables x,y and z are assigned the value of 3. Is this valid? x = y+1 = 3; This sort of a statement is not valid in C++. You cannot use arithmetic operators in between multiple assignments (the compiler will not know how to solve this statement).
Type Conversion Each data type has a different range because of the different sizes that they occupy. A character is allotted 1 byte while an integer might be allotted 2/4 bytes. What will happen if we assign an integer to a character or a double to an integer? Assigning a variable of one data type to a variable of another data type is known as type conversion. When assigning between numeric types, whatever is on the right hand side of the assignment will be assigned to the variable on the left. If a variable of a smaller type is assigned to a variable of a larger type, no information will be lost on conversion (because a smaller type can be accommodated into a larger type). For example a character (which occupies only 1 byte) can be converted into an integer without any loss of information (because an integer occupies more bytes). However if you attempt to assign an integer to a character you will lose the higher order bits of the integer (only the lower 8 bits of the integer will be retained in the character). Let us assume that an integer occupies two bytes and a character occupies one byte. unsigned int a=510; unsigned char x; x=a;
Operators
57
As can be seen from the figure, the upper 8 bits are lost due to the type conversion and the value of the character will be only 254. The following program illustrates a few type conversions: #include int main( ) { char ch; double db; float f; short int i; db=55e4; ch = i = f = db; cout<
Operators
58
C++ Operators - III The following topics are covered in this section: • •
3. Assignment Arithmetic Operators/Shorthand Operator (+ =, - =, * =, / =) Assignment arithmetic operator is a combination of an arithmetic operator and the assignment operator. Consider the example: X + = 3; The above statement is the same as: X = X+3; i.e. the value of X is incremented (or increased) by 3 and the new incremented value is assigned to X. So if X had a value of 2 before this expression, then X will be 5 after this expression is executed. Operator
Operation performed
a+=b
a=a+b
a-=b
a=a–b
a*=b
a=a*b
a/=b
a=a/b
a%=b
a=a%b
Operators
59
4. Relational Operators ( < , > , = = , ! = , >= , <= ) Relational operators are also binary operators (since they operate on two operands). They are used for comparing two values and the result of the comparison is either true (value 1) or false (value 0). Some examples are given below: 5>4 will return a value of True (1) 2>3 will return a value of False (0) In programs that you write, comparisons will usually be made between one variable and a constant or between two variables. For example: x>y z>10 > means ‘greater than’ while >= stands for ‘greater than or equal to’. x>=y will yield a true value even if x = y whereas x>y will yield a value of false when x = y. Be clear as to what relation you want to test when using these operators. Suppose you want to test whether two variables are equal, you have to make use of the equality operator. The equality operator is denoted by = = (double equal to signs). Remember: Many beginners in programming use the equality operator and assignment operator interchangeably. The assignment operator is a single ‘equal to’ sign and it is meant only for assigning values to variables. The equality operator (a double ‘equal to’ sign) is used to check whether two values are equal. Relational Operator
Operation Performed
Result of Operation
x>y
Is x greater than y?
True/False
x
Is x less than y?
True/False
x>=y
Is x greater than or equal to y?
True/False
x<=y
Is x less than or equal to y?
True/False
x==y
Is x equal to y?
True/False
x!=y
Is x not equal to y?
True/False
Operators
60 We’ll write a simple program for comparing two numbers and displaying the appropriate result. #include int main( ) { float num1, num2; cout<<"Enter the two numbers : "; cin>>num1>>num2; if (num1>num2) { cout<<"The number "<
Operators
61 num1= = num2 within the ‘if’ condition, the following mistake is made: if (num1 = num2) // Logical Error { cout<<"The number "<
Operators
62 In the following program, we have coded to display the result of num>5. Do you think it is valid?
#include int main ( ) { int num; cout<< "Enter the number"; cin>>num; cout<<(num>5); //Legal? return 0; }
Always remember that the result of a comparison yields a value of TRUE (1) or FALSE (0). Hence the above program is perfectly correct. In case you enter a value that is greater than 5 you will get the output as 1 else you will get 0. 0 is considered as false and all other values are considered to be true.
Operators
63
C++ Operators - IV The following topics are covered in this section: • •
Logical Operators Unary Operators
5. Logical Operators - AND ( && ) OR ( || ) NOT (!) These operators are used to combine two or more expressions. The way in which they combine the expression differs depending on the operation used. They are used when we need to test multiple conditions. For example you may write a program that has to check whether the marks scored by a student is greater than 70 and less than 80. If it is so then you will want the program to display a ‘B’ grade. To check whether the average mark is greater than 70 you have to use one expression and to check whether the average is less than 80 you should use another expression. Thus in simple English your statement will be: If (average mark is greater than 70 AND average mark is less than 80) Print "B grade" AND: it combines two conditional expressions and evaluates to true only if both the conditions are true. First Condition
Second condition
Result of AND operation
False
False
False
False
True
False
True
False
False
True
True
True
If the first condition and the second condition are both true then the result of the AND operation will also be true. Example:
Operators
64 // To check whether the given number is even # include int main ( ) { int num; cout<< "Enter the number"; cin>>num; if ( (num!=0) && ((num%2)= =0) ) // Two conditions have to be true { cout<<"\n Even Number"; } return 0; } In this program we need to check for two conditions (the number entered should not be zero and the number when divided by 2 should not produce a remainder). Only if both these conditions are satisfied should the program display that the number is even. The AND operator is used to combine the two conditions that are to be tested and if both are true then the message is displayed. OR: operator combines two conditions and evaluates to true if any one of the conditions is fulfilled (i.e. only one of the conditions need to be true). It is designated by using two parallel bars/pipes ( | | ). First Condition
Second condition
Result of OR operation
False
False
False
False
True
True
True
False
True
True
True
True
The ‘AND’ and ‘OR’ operators can be used on a sequence of conditions (i.e. at a time you can check for multiple conditions). For example the following code is legal: if ( (x>y) && (y>5) && (z>y) && (x>4) ) { //body of the ‘if’ condition… }
Operators
65 In this case only if all the four conditions are true will the body of the ‘if condition’ be executed. NOT: NOT is a unary operator. Unlike ‘AND’ and ‘OR’, NOT operates only on one operand. The logical value of the operand is reversed. If the operand is true, then after the NOT operation it will be become false. You might be thinking that the NOT operator can operate only on 1 and 0. Actually, any number greater than 0 will be considered as a true value. Hence the following would give: • • •
!5 will give 0 !0 will produce 1 !1 is equal to 0. Condition
Result of NOT operation
False
True
True
False
Basically, any number other than zero is considered as true. ‘Not’ of any number (other than 0) will give you FALSE (or zero). Check out the following program that illustrates the NOT operator.
#include int main ( ) { int num, result; cout<< "Enter the number"; cin>>num; result = (!num); cout<
Operators
66
6. Unary Operators (NOT operator !, increment operator ++, decrement operator --) Unary operators operate on only one operand, which could be a constant or a variable. We’ve already discussed about the NOT operator. Another simple unary operator is the unary minus operator. This will act on only one operand and will change the sign of the number it operates on. For example: • •
-(5) = -5 -(-5) = +5
The ++ and – operator are very important if you are taking a course in C++ (teachers usually love this topic and they are bound to ask some questions on unary operators). ++ is known as the increment operator and it can be used in two ways. •
As a prefix : i.e. when the operator precedes the variable (as in ++ i, where i is the integer variable)
If we write: i1 = 10; i2 = ++ i1; This is the same as: i1 = 10; i1 = i1 + 1; i2 = i1; At the end of these steps the value of both i1 and i2 will be 11. When used as a prefix, the variable is first incremented and later assigned. •
As a suffix: i.e. when the variable precedes the operator (as in i ++).
If we write: i1 = 10; i2 = i1 ++ ; This is the same as writing: i1 = 10; i2 = i1; i1 = i1 + 1;
Operators
67 At the end of these steps the value of i2 will be 10 and that of i1 will be 11. When used as a postfix, the variable value is first assigned and later incremented. The decrement operator works in the same way. The only difference is that it decreases the value of the operand by one. Again the decrement operator also can be used in two ways: as a prefix or as a suffix (or postfix). Suppose we have the following coding in a program, what will be the value of ‘sum’ at the end of each statement? int sum=5; cout<
//‘sum’ is 5 // ‘sum’ is 5. Only in the next reference to ‘sum’ will //‘sum’ is now 6
Thus in the case of the postfix operator the value is assigned first and increment is carried out in the next step. If you make use of the prefix operator then: int sum=5; cout<<++sum; cout<<" "<
//‘sum’ is 5 // ‘sum’ is immediately 6 //‘sum’ is 6
Remember: When used as a prefix ( ++i ) compiler will first increment and then assign. When used as a suffix, assignment is done first and then incrementing is performed. Beware: The ++ and – operators cannot be used on expressions (i.e. ++ (y + 1); is an incorrect statement and will lead to a compiler error).
Operators
68
More on Operators (V) The following topics are covered in this section: • • • • • •
SizeOf Operator (sizeof) This is again another unary operator. Just as the name implies, this operator returns the length (in bytes) of the variable that is mentioned in the parentheses. For example the size of a character is 1 (i.e. 1 byte). // Using sizeof operator #include int main ( ) { int a; cout<
Conditional Operator (?:) The conditional operator is a ternary operator (i.e. it operates on 3 operands at a time). We’ll take a closer look at it in the next chapter. For the time being just remember that it needs three operands and it is denoted by: ? :
Operators
69
Operator Precedence When a statement has more than one operator, the operator precedence determines as to which operator is given priority. For example if you have a * and a +, the compiler has to decide whether it will multiply and add or add and multiply. This will depend on the operator precedence. The parentheses have the highest precedence. For instance if you write: (5+3)*5 the result will be (8)*5 or 40. If you write the same without using parentheses as 5+3*5 the result will be 20 (because * has a greater priority than +). The order for operator precedence is listed below (starting from the highest priority): Parentheses ( ) Unary Operators ! Unary minus Multiplication * Division / Modulo % Addition + Subtraction Relational Operators < <= > >= Equality operator == Inequality operator != Logical Operator && || Conditional operator ?: Assignment = Arithmetic assignment *=, /= , %= , += , -=
Operators
70 Beware: It is better to write long expressions using parentheses otherwise it could lead to a lot of confusion and also to potential logical errors.
Associativity If two operators have a different priority level then their execution order depends on the operator precedence. What will happen if two operators have the same priority level? When 2 operators in an expression have the same priority, the expression is evaluated using their associativity. Consider the statement: net = basic + allowance – tax; Both + and – have the same priority level. Almost all of the operators except the assignment (and arithmetic assignment) operators have associativity from left to right. The assignment operator has associativity from right to left. Since the + and – binary operators have an associativity of left to right the expression for ‘net’ is the same as: net = (basic + allowance) – tax; When you perform multiple assignments: x = y = 0; the associativity of the assignment operator (=) is taken into account and thus the order of evaluation will be: y = 0 (which will give ‘y’ a value of 0) followed by x = y (which will assign 0 to ‘x’).
Operators
71
Comma Operator The comma operator can accept two expressions on either side of the comma. When executed, the left side expression is first evaluated followed by the right side expression. Ultimately it is the expression on the right side that will be the value of the entire expression. int x,y; cout<<(x = 1, y = 5);
// 5 will be displayed
First the ‘x’ will be assigned 1 and then ‘y’ is assigned a value of 5. The comma operator is equivalent to saying "do this task and do this also". In this case the compiler will do: x = 1 and then y = 5 The rightmost expression is y=5 and hence the value of the entire expression (x=1,y=5) is 5. Be careful while assigning the value of a comma separated expression to a variable. The comma operator has lower operator precedence than the assignment operator. If we type: y = (x = 1 , x = 5 , x + 10); x will have a value of 5 while y will be 15. If we type: y = x = 1, x = 5 , x+10; the value of ‘y’ will be 1 and that of ‘x’ will be 5. This is because the compiler will perform the following operations: y = x = 1; x = 5; x + 10;
// y and x are set to 1 //x value is 5 //the value of the entire comma separated expression is 15
The comma operator will be used usually in ‘for’ loops.
Operators
72
Bitwise Operator: The bitwise operators available in C++ are tabulated below: Bitwise Operator Symbol
Function
&
AND individual bits
|
OR individual bits
^
EXOR individual bits
~
NOT of individual bits (or complement operator)
>>
Right-shift operator
<<
Left-shift operator
These operators will operate on individual bits of the operand (i.e. on the binary representation of data). These operators are dealt with in detail in Chapter 13. Remember: Do not confuse the && and & operators or the || and | operators (logical operators are entirely different from bitwise operators).
Recap • • • • • • • • • • •
Arithmetic operators are used to perform mathematical operations. The modulo/remainder operator is applicable only on integer operands. Escape sequences are character constants that are used to format the output displayed on the screen. = is the assignment operator and the Rvalue is assigned to the target (or the Lvalue). Information will not be lost when converting from a smaller data type to a larger data type. Shorthand operators are a combination of one of the arithmetic operators and the assignment operator. Relational operators are used to compare two values or expressions. They will return a value of true or false. Logical operators are used to combine two or more expressions. The operator precedence determines which operator has a higher priority. When 2 operators have the same priority the expression is evaluated based on their associativity. Bitwise operators are used to operate on individual bits.
Operators
73
The basics (chapters 1 to 3) 1.Q) What do you think is the output??? 2.Q) The following coding: int main( ) { int a=3,b; b=a++ + a++; cout<>x;cout<<"You entered:"<>x; cout<<"You entered:"<
Q.) What are tokens? What’s lexical analysis? What is the output of: int i=5,j=2; cout<
80 a, b, c are identifiers and +, = are the operators. A question arises, how does the compiler decide how long a token is? How does the compiler know when to consider one character for a token and when to consider more? To simplify this situation, the compiler tries to extract the largest possible chunk in a statement to form a token (in other words it’ll try to extract the largest token while scanning from left to right). In our statement: a=b+c; the compiler cannot consider a= as a single token (because it knows that = is the assignment operator). So it picks ‘a’ as a symbol and so on. This might not seem to be a significant point but it’ll help us tackle the second question: int i=5,j=2; cout<
81 Note: In the previous question the following was termed illegal: k = i+++++j; In this case, the compiler would break up the statement as: k = (i++)++ + j; Upto k = i++ everything is fine, but after that it can’t form a legal token and thus produces a compile time error. It will form: (i++)++ which is not valid. It is better to avoid coding statements like i---j even though they produce the desired result (such code is neither pleasing to the eye nor the mind!).
Q.) Deduce the output in both of the following cases: a.)
int x=5; if ( (x= =5) || (x=7) ) { cout<<"True"; } cout<
What do you think is the value of ‘x’? A.) Since ‘x’ is 5, the first condition is satisfied. When we use an OR operator, the program will not evaluate the second condition if the first is true. Thus the value of ‘x’ will be 5 and the output will be: True5 b.)
int x=5; if ( (x= =5) && (x=7) ) { cout<<"True"; } cout<
What do you think is the value of ‘x’? A.) Since ‘x’ is 5, the first condition is satisfied. When we use an AND operator, the program will evaluate the second condition if the first is true. In the second condition we have assigned a value of 7 to x. Thus the value of ‘x’ will become 7 and the program will output: True7
82 Q.) Will the following program (not having an iostream.h) compile and execute? Explain with reasons. int main( ) { return 0; } A.) iostream.h is needed when we use cin, cout, <<, >> and similar objects (which we use in most of our programs). Otherwise you can do without iostream.h and the above program will compile and execute successfully.
Interview/Viva/ Test questions (along with the logical questions solved earlier): 1. 2. 3. 4. 5. 6. 7.
What is the difference between a compiler and an interpreter? Why does 1Kb = 1024 bytes and not 1000? Distinguish between machine language, assembly language and middle level languages? How do the post increment and pre increment operator function? Are they the same? How many bits are present in a nibble, a byte and a word? Why do we need to give iostream.h? Is the statement: int x; a declaration or a definition?
Other Questions and Programs: Q.) What is an interpreter? Interpreters are programs which will interpret the source code line-by-line and execute them. Compilers, on the other hand, read through the entire source code and convert it into an object code (they do not execute the code). The UNIX/Linux shell is a command interpreter (it executes your instructions on a line-by-line basis). The DOS command.com is an interpreter. Interpreters do not produce object code (which means that each time you run a program in an interpreter, the interpreter will repeatedly check the syntax etc. before executing the code). There are some languages which are called interpreted languages (i.e. these languages are always interpreted). C/C++ is not an interpreted language. Q.) What is a linker and a loader? Generally when writing a program, you will make use of different libraries and other source files. On compiling each source file we obtain an object code. All the object codes need to be put together to form an executable (because one code might call a function written in another code and only the linker will be able to resolve this). This is the basic function of a linker.
83 A loader is generally a program which loads the program (the executable) into main memory. All our programs and applications reside on a secondary storage device (hard disk) but the processor requires programs to be present in the main memory (RAM)- access time for main memory is much lower than secondary storage (i.e the CPU can access main memory quicker than secondary memory). The loader takes the program from secondary and loads it into main memory. You will be clear about a linker when we discuss about multiple file programming.
Q.) Distinguish between machine language, assembly language and middle level languages? Q.) Why do we need compilers? Q.) What is the difference between a constant and a variable?
Q.) Write a C++ program to input the name and age of the user and then display a sentence on the screen along with these two data. Q.) Write a program to convert a given amount from US dollars into Indian Rupees (assume the conversion rate as USD 1 = Rs.45). Also display a statement. Q.) Write a program that will calculate the compound and simple interest on a principal amount for a given number of years (also obtain the rate of interest from the user). Q.) Write a program to help a shopkeeper calculate the percentage profit he makes when he sells an item (obtain the buying price and selling price as inputs from the shopkeeper). Q.) Write a program to solve quadratic equations of the form: ax2 + bx + c = 0 Obtain the values of a, b and c from the user and find out the values for x.
84
For Loops in Depth
The following topics are covered in this section: • • • • •
Introduction For Loop Statement More of For loops Body of For loops Nesting For loops
Statement and Expression A statement in C++ refers to a part of the code that is terminated in a semicolon. It is the smallest part of the program that can be executed. For example: sum = a + b; is a statement. A C++ program will consist of a set of statements. An expression is a grouping of variables, constants, function calls and operators (arithmetic, logical, relational) to return a value. In the above example, a + b is an expression. While dealing with control mechanisms you will come across blocks of instructions. A block of instruction refers to a set of statements grouped together within a block. The block is identified by using the curly braces ‘{‘ to start the block and ‘}’ to end of the block. Program flow and control: What is program flow? Whenever we write a program we have to decide on the sequence of operations that the program has to perform. Generally this is represented in the form of an algorithm. Writing algorithms for simple programs might seem trivial but when you write complex programs, the algorithm helps reduce programming errors. Let’s take a look at a simple algorithm to calculate the average weight of 3 persons: Step 1: Start the program. Step 2: Obtain the weight of all 3 persons (in weight1, weight2, weight3). Step 3: Calculate the average weight as average = (weight1 + weight2 + weight3)/3 Step 4: Display the result
Program Control Flow
85 Step 5: Stop the program By looking at the algorithm a programmer can easily write the entire program. When you have a complex program, if you have written an algorithm then you can eliminate potential logical errors in this stage itself. In the algorithm we define the way in which the program control should flow. First the program should get the 3 inputs from the user, then it should calculate the value for average and finally it should display the result. Thus we can say that the program flow has been clearly defined. But program flow needn’t always be so simple. You might need to take some decisions and alter program flow. Control refers to that part of the program which is currently being executed. Let’s write an algorithm to divide 2 numbers obtained from the user. Step 1: Start the program. Step 2: Obtain the 2 numbers (num and den) from the user. Step 3: Check if den is zero. If it is then go to step 4 else go to step 5. Step 4: Display “Denominator is zero. Cannot perform division”. Go to step 7. Step 5: Calculate the quotient by dividing num by den. Step 6: Display the result. Step 7: Stop the program. In this example, there are 2 routes the program can take. If the user enters the denominator as 0 then the error should be produced else normal division should be performed. The pictorial representation of an algorithm is called a flowchart.
Loops Suppose you want to add the numbers from 1 to 10, what would you do? Of course you could write a long statement that would calculate 1+2+3+4+…+10. What if 10 were changed to 100 or 1000? Whenever there are statements to be repeated you can make use of loops (in fact you should make use of loops). When using loops, some condition should be specified so that the loop will terminate (otherwise the set of statements within the loop will keep executing infinitely).
Program Control Flow
86
For loop statement For loop is used for performing a fixed number of iterations (or repetitions). The syntax is: for (initialize-variables; condition-to-test ; assign-new-values-to-variables) { //statements to be repeated (or the body of the loop) } The ‘for’ loop has three expressions that have to specified. Initialize variables: This expression is executed only once (when the program flow enters the loop for the first time). It is usually used to assign the loop variable an initial value. Condition to test: involves relational operators. It is executed each time before the body of the loop is executed. Only if the condition is true will the loop body be executed. If it is false then the loop is terminated and the control passes to the statement following the ‘for’ loop body. Assign new value to variable: It is executed at the end of every loop after the loop body. Usually it is used for assigning a new value to the loop variable. Example: // To find square of numbers from 1 to 10 #include int main( ) { int var; for (var = 1 ; var<=10; var++) { //beginning of ‘for block’ cout<< "Square of "<
In this program, ‘var’ is the loop variable. The loop variable controls the loop. We have initialized this variable to1. The condition that is to be tested before executing the loop each time is: Is ‘var’ less than or equal to 10 (i.e. var<=10)?
Program Control Flow
87 Thus each time var satisfies the above condition the loop body will be executed. ‘var’ is reinitialized with a value at the end of every loop using the expression: var++ (i.e. incrementing the value of var by 1 each time the body of the loop is executed). So, to put the program flow in sequence, it would be as follows: Initially ‘var’ is assigned a value of 1. The condition is tested. Since 1 is less than 10, the body of the loop is executed. In the body of the loop we display the square of var (in this case it is the square of 1). With this, the loop has completed one cycle. In programming terms this is called as ‘one iteration’. The variable ‘var’ is then assigned a new value depending on what is specified in the ‘for loop’. In this program we have written: var++ Thus ‘var’ is incremented by one. Now, the new ‘var’ value is 2. Again 2 is less than 10 and so the body is again executed with the ‘var’ value as 2. This will be the second iteration. This process is repeated until ‘var’ becomes 10. When ‘var’ is 10, the condition is again tested. Since 10 is equal to 10 the condition will return a true value (meaning that the condition is satisfied). The loop body is executed again. For the next iteration, ‘var’ is 11. But since 11 is greater than 10 the condition to test returns a false value and the loop body terminates. Program flow goes to the line after the ‘for loop’ body. In this case, it happens to be return 0 (which signals the end of the program). Beware: for (var = 1 ; var< =10; var++) //Will lead to an error { cout<< "Square of "<
Program Control Flow
88
More on ‘for’ Loops We've seen the usual ‘for loop’ but there are some interesting modifications to the ‘for loop’. The normal syntax for the ‘for loop’ is: for (initialize-variables; condition-to-test ; assign-new-values-to-variables) { //statements to be repeated (or the body of the loop) } Have you wondered what will happen if we don't specify one of the three parts of the ‘for loop’? #include int main( ) { int i = 2; for ( ; i<8 ; i++ ) loop variable! { cout<
// No initialization of
The ‘for loop’ does not have any initialization expression. Will it run? Yes it will because 'i' has been initialized outside the ‘for’ loop. This is perfectly valid and the output would be: 234567 But if you don't specify i = 2 and try to compile the program, the program would still run (but you won't know what value of ‘i’ has been assumed by the compiler). Thus the initialization expression can be left out in a ‘for’ loop if you have initialized the variable with a value before entering the ‘for loop’. Basically, it is not a must that we have to specify all the 3 parts in a ‘for’ loop. Check out the following program: #include int main( ) { int i; for ( i = 1; i != 2 ; i++ ) { cout<
Program Control Flow
89 What do you think the output will be? The output will be: 1 ‘i’ starts from 1, when it goes to the value 2 the condition i != 2 becomes false (because ‘i’ is now equal to 2). Whenever the condition becomes false the ‘for’ loop will immediately terminate. Suppose instead of for ( i = 1; i != 2 ; i++ ) we had coded as for ( i = 2; i != 2 ; i++ ) what will happen? Simple, the ‘for’ loop will be terminated instantaneously; in other words the program will not enter the ‘for’ loop even once because the condition becomes false in the first test itself. Another variation of the for loop is shown below: for ( ; ; ) { //body of loop will run infinitely without an end unless a break condition is used } This is called an infinite loop. If you're wondering why one would want an infinite loop; well, you can use it to create time delays or you could specify some condition within the loop to cause it to break out of the loop. Breaking out from a for loop (or an infinite for loop) can be accomplished by using a ‘break’ statement (this will be discussed later). So far we’ve seen ‘for loops’ using only one loop variable. But you can have ‘for loops’ with two loop variables as well. #include int main( ) { int x,y; for (x=1,y=1; x<10,y<5 ; x++,y++) { cout<<"\n"<
Program Control Flow
90 Here we’ve made use of two ‘for’ loop variables (x and y). Both are initialized to 1. There are two conditions specified: x<10, y<5 This actually means that x should be less than 10 and y should be less than 5. If both are satisfied then the loop body will be executed. If even one isn’t satisfied the loop will terminate. The output will be: 11 22 33 44 Remember: You have to make use of comma (,) to say that you want to initialize x to 1 and y to 1. It is a comma and not a semi-colon (many beginners use the comma and semi-colon interchangeably). Semi colon is used in the ‘for’ loop to separate each of the expressions (to separate the 3 parts of a ‘for’ statement: the initialize, test and re-initialize parts). You might wonder whether you could use more than two loop variables? The answer is yes. You can use more loop variables but usually you won’t need to use more than 2. Empty ‘for’ loops: If the ‘for’ loop has no body to execute then it is called an empty ‘for’ loop. For example: for (int j=0; j<100; j++); The ‘for’ loop has been terminated by the semi-colon. This ‘for’ loop has no body and it will simply keep incrementing the value of ‘j’ from 0 to 100. This can be used to produce any required delay (because the loop has to be repeated 100 times). Delays can be useful in I/O operations or even while displaying (if the output is appearing too fast).
Body of the ‘for’ loop The body of the ‘for’ loop is the statement that immediately follows the ‘for’ statement if the body of the loop hasn’t been enclosed within braces. For example: for ( j = 0; j<10; j++) cout<< "hi"; cout<< j ;
Program Control Flow
91 The compiler will only consider the statement: cout<< "hi"; as belonging to the ‘for’ loop body. Hence the above code fragment would print "hi" ten times and the value of ‘j’ will only be printed once. The above set of statements is equivalent to: for ( j = 0; j<10; j++) { cout<< "hi"; } cout<< j ; It is good programming practice to clearly specify the body of loops using braces (the same holds good for ‘while’ loops and ‘if’ statements).
Nesting ‘for’ Loops The term nesting is frequently used in programming languages. Nesting means one within the other. For example: when we say nesting of ‘for loops’ it means that there is one ‘for loop’ within the body of another ‘for loop’. for (…..;….;…..) { //body of 1st for loop (called the outer ‘for loop’) for (….; …..; …..) { //body of 2nd for loop (called the inner loop) } } You might wonder as to how the program executes in the case of two ‘for loops’. Let’s assume that the outer ‘for loop’ has a loop variable ‘j’ that takes values from 1 to 10 in increments of one. Let the second loop variable be ‘k’ assuming values from 1 to 20. Thus initially j=1, and the program will encounter the second ‘for loop’. Now k=1 and the body of the second (inner) loop is executed. Then k is incremented to 2 (while j is still at 1) and the inner loop is executed again. Next k=3 while j is still 1 and the inner loop is executed once more. When the ‘k’ value reaches 20, the inner loop terminates and only then will ‘j’ be incremented to 2. Again for a ‘j’ value of 2, the inner loop will be repeated 20 times (since k will take values from 1 to 20) and so on. How many iterations are performed totally? A total of 10 * 20 (=200) iterations are performed using these two ‘for loops’.
Program Control Flow
92
While Loop
The following topics are covered in this section: • •
While Loop Do...While Loop
While loop We saw that the ‘for’ loop has 3 parts in its syntax (initialization, testing and then re-initializing the loop variable). The ‘while’ loop has only one expression; the test condition. The ‘while’ loop keeps on repeating the loop body as long as the loop condition is true. The syntax is: while (test-condition) { // repeat the statements as long as condition is true } Before illustrating the ‘while’ loop using an example, you should know an important fact about relational operators and characters. You might recall that a character can hold a single character. We have seen comparison of numbers using relational operators but what about comparing characters? There are many instances when you will want to check whether the user entered a particular character. For example: you may want to check whether the user entered a y (for yes) or an n (for no). Depending on what was entered your program should continue on two different lines. If ‘letter’ is a character variable, we will obtain the input from the user using the statement: cin>>letter; If you want to check whether the user has entered a ‘y’ or an ‘n’ you can do the following: if (letter = = ‘y’) { //body of if statement ) You should not compare characters as shown below: if (letter = = y)
//WRONG
This method of comparing a character variable with a character constant is WRONG. It will yield an error. The character constant should always be enclosed in single quotes. Beware: Beginners often forget the single quotes while using character constants. Program Control Flow
93
//To find the square of a given number # include int main( ) { char reply; int num, square; cout<< "Do you want to find the square of a number(y/n)? "; cin>>reply; while (reply = = 'y') //No blank-space between the two equal signs. See note. { cout<<"\nEnter the number : "; cin>>num; square = num*num; cout<< "The square is : " <>reply; } return 0; } Note: A blank space has been left between the two ‘equal to’ symbols just to make it clear that there are two equal signs. Do not leave a space between the two = symbols when you write your program. At the start of the program, if the user types a ‘y’ the program will execute the loop body. At the end of the while loop, the program will prompt the user to enter the value for ‘reply’. If the user again types ‘y’, then the program will execute the while body a second time. Hence, at the end of each loop the program obtains the value for ‘reply’ and checks whether the test condition is true. As long as the test condition is true, the while loop is executed over and over again. The difference between the ‘while’ loop and the ‘for’ loop is that the ‘while’ loop does not have a fixed number of repetitions of the loop body. As long as condition is true it keeps executing the loop body. Usually the ‘while’ loop is used when the programmer is not sure about the number of iterations that need to be performed. The ‘for loop’ is used when the programmer knows how many iterations are to be performed. Of course, the use of both can be interchanged as well. In fact, a ‘for loop’ can be modeled into a ‘while loop’. The equivalent ‘while loop’ for a ‘for loop’ will be as below: Initialize a variable value; while (condition involving the variable) { //body of the loop //assign a new value to the loop variable; }
Program Control Flow
94 If we use the coding: while(1= = 1) { //body of loop } the while loop becomes an infinite loop (because 1 is always equal to 1).
Do…While loop The ‘do…while’ loop is a modification of the ‘while’ loop. If you noticed in the earlier program for the ‘while’ loop, initially we have to ask the user whether he/she wants to find the square of a number. Only if the user types a ‘y’ will the program enter into the ‘while’ loop. Hence in the program we have to ask the user twice whether he/she wants to square a number (once outside the loop and once inside the loop). The ‘do while’ loop, eliminates this repetition. When you make use of the ‘do-while’ loop, the body of the loop will be executed at least once. After the first iteration, if the loop condition holds true then the loop is repeated again. Thus the first iteration is compulsory in the ‘do…while’ loop. The program to find the square of a number can be re-written as follows: #include int main( ) { char reply; int num, square; do { cout<<"\nEnter the number : "; cin>>num; square = num*num; cout<< "The square is : " <>reply; }while (reply = = 'y'); return 0; } In this program, the body of the loop will be executed once for sure. After the first iteration, the user has the option of terminating the program or continuing to use the program for squaring another number. Beware: when using the ‘do…while’ loop, make sure that you put a semicolon at the end of the while statement: while (reply = = 'y');
Program Control Flow
95
Decision Statements (IF)
So far we have seen statements that help you in repeating a particular task as long as you desire. Another important form of program flow control is to be able to make a decision as to how you want the program to continue. Statements of this kind are referred to as decision statements (sometimes also called selection statements).
The following topics are covered in this section: • • •
If else Nested If Conditional Operator
If…Else If…else… ‘if-else’ is one of the decision statements available in C++. This enables the programmer to provide different paths for the program flow depending on certain conditions. For example: consider a program for dividing two numbers. Division by zero will lead to an error. To avoid this from happening, after obtaining the two numbers from the user the programmer will want to ensure that the denominator is not zero. Hence there needs to be two different program flow options. If the denominator is zero a message saying, "Division not possible" should be displayed. Otherwise the program should carry out the division and display the results. In simpler terms this can be stated as: If the denominator is zero, then display a message and do not divide else perform division and display the output. Syntax: if (condition) { //statements to be executed; } else { //statements to be executed; } The program flow is as follows: If the condition being tested within the ‘if’ statement is true, then the body of ‘if’ will be executed otherwise the body of ‘else’ will be executed. Thus the program takes a decision as to what it should do next.
Program Control Flow
96 You can also make use of the ‘if’ statement alone (‘else’ is not compulsory) or you can even make use of a series of ‘if...else’ statements as follows: if (condition1) { //body } else if (condition2) { //body } else { //body } Example: // To find the greater among two numbers #include int main( ) { int a, b; cout<< "Enter the two numbers you want to compare : "; cin>>a>>b; if (a = =b) { cout << "The two numbers are equal"; } else if (a>b) { cout <
Program Control Flow
The output is: Enter a name: ajay Enter the email address : [email protected] Contents of file are : ajay [email protected] The program upto the write ( ) function is normal. We open a stream called out for writing the structure to the file. out.write((char *) &user, sizeof (struct email)); Compare the above line with the syntax of the write ( ) function. You'll notice that instead of ‘buf’ we've used ‘&user’. This is because we want to save the structure variable ‘user’ into the file. So we point to the address of ‘user’. Then instead of ‘n’ we've used: sizeof (struct email) In the general syntax, ‘n’ refers to the number of bytes you want to write. In this case we want to write as many bytes as the structure will occupy. Instead of specifying some fixed number, it's better to use the ‘sizeof’ operator to find the number of bytes. The name of the structure is ‘email’. So by saying sizeof (struct email)
Files & Streams - C++
396 the compiler will find out how many bytes the structure ‘email’ occupies. This value is the same as that of ‘user’ (since ‘user’ is a variable of structure ‘email’). Next we create a stream to read the contents of the file test.txt (just to see whether the structure was saved in the file). in.read((char *) &check, sizeof(struct email)); The syntax is same as that of write ( ) function. Except that we've made use of another structure variable namely: ‘check’. This is also a variable belonging to structure type ‘email’ except that we haven't obtained any values for check.name and check.num Now, we will read ‘sizeof (struct email)’ bytes and store it in the structure variable ‘check’ and then print the values of check.name and check.num. Instead of using ‘check’, you can also use the structure variable ‘user’. A different structure variable is used for the two purposes to demonstrate clearly that we are really writing and reading from the file. Remember: It is always a good idea to store structures in binary-format rather than textformat (since they contain a mixture of data types and you wouldn’t want conversion of data).
Padding/Packing of Structures What do you think is the space occupied by the structure variable ‘t’ below: struct test { long int num; char a; }t;
// long int is 4 bytes //char is 1 byte
The actual size is 5 bytes but the compiler might do padding to the structure and make the size of the structure 8 bytes. Why? Some compilers prefer to uniformly allocate memory space to the structure members (i.e. each structure member is given a fixed length). In the above structure ‘test’ the compiler notes that ‘long int’ is the maximum element size (of 4 bytes) and so it prefers to allocate memory space in increments of 4 bytes. Thus if ‘num’ occupies memory from byte number 1 to byte 4, then ‘char a’ will occupy memory from byte number 5 to byte 8 (even though ‘char’ requires only 1 byte). In this case we say that the packing is 4 bytes (because each member is packed to 4 bytes). Why does the compiler do packing? Consider the structure: struct test { short int s; char c; double d; long int i; }
Files & Streams - C++
// 2 bytes // 1 byte // 8 bytes // 4 bytes
397 The structure ‘test’ will now have packing of 8 bytes (because a ‘double’ occupies 8 bytes). Let us assume that the compiler allocates memory starting from byte 0. The allocation will be: [2+1+(5)] + [8] + [4 + (4)] = 24 bytes The number within ( ) denotes the number of extra bytes added for the sake of uniformity (the padded bytes). ‘s’ and ‘c’ (both combined occupy less than 8 bytes) are put together in one field. Since ‘d’ cannot be accommodated within the same 8-byte field, five bytes are padded to the first field. ‘d’ will occupy the next 8-byte field. No padding of bytes is required here. The last field is occupied by the integer ‘i’ and it occupies only 4 bytes. So another 4 bytes are padded here to make this field 8 bytes long. Thus the starting location of each member (in terms of bytes) when 8-byte packing is used is: 0, 2, 8, 16 (a total memory space of 24 bytes is needed). Suppose we didn’t use padding (i.e. if packing is 1 byte), the positions will be: 0, 2, 3, 11 (a total of 15 bytes) If we use packing of 4 bytes, the positions will be: 0, 2, 4, 12 (a total of 16 bytes) As you might have noticed, when we use packing, all the members will be at multiples of 8 or 4 (depending on the packing used) and such an alignment is faster to access. A 32-bit processor would be able to access members at 32 bit boundaries faster. If there is no packing the elements are at varying positions (i.e. they are not uniformly placed in memory). Though more space is occupied by padding, the speed of the program can be improved using padding. To take advantage of this fact some compilers implement padding whenever they deal with structures. There is no need to worry about padding if you are developing software entirely in a single compiler. The problem arises when you write two programs (one for writing structures and another for reading the file) on two different compilers. If both compilers implement packing of bytes then there won’t be a problem. For example: Turbo C++ does not use padding while VC++ uses padding. So, if you write a program to write structures to files in VC++, the program will use padding to store the data in the file. While reading the file using a TC++ program, the compiler does not know about packing and so when you use the read ( ) function, you will get strange results. An example to highlight this problem is given below. //Program to write structures to a file using a compiler which implements padding #include #include struct test { char a,b,c; long int num; }t;
//it will write a structure of 8 bytes in the file
This program writes the structure to a file (and it uses padding). Let us suppose that we write another program to read this file in a compiler which doesn’t support padding. //Program to read structures using a compiler which does not know padding #include #include struct test { char a,b,c; long int num; }t; //compiler assumes ‘t’ as occupying 7 bytes int main( ) { ifstream str("c:\\text.txt"); str.read((char *)&t, sizeof(t)); //it reads only 7 bytes. cout<
Files & Streams - C++
399 #pragma pack(1) is the same as using no padding. Remember: Compilers that do not use padding (like TC++) will not support the #pragma pack ( ) directive. These compilers always use a packing of 1 and this cannot be changed. Thus if programmers feel that they will be working across different compilers, they prefer to specify the directive: #pragma pack(1) which instructs the compiler to treat the members just as they are (no padding). This directive has to be added along with the #include directive (outside the main ( ) function). Thus: #pragma pack(1) struct test { short int s; // 2 bytes char c; // 1 byte double d; // 8 bytes long int i; // 4 bytes }; will cause any variable of type ‘test’ to occupy only 15 bytes. Remember: Padding is a problem mainly when you are working with storing data in files and when you are using two compilers.
End of File There exists a member function that you can use to identify whether the end of file (EOF) has been reached or not. int eof( ); This function will return true if the end of file has been reached. You could test for EOF using something similar to the following: while( !in.eof( ) ) { //body of while loop } where ‘in’ is an input stream.
Files & Streams - C++
400
Streams and Files - V The following topics are covered in this section: • • • •
File Pointers Sequential and Random Access Command Line Arguments Recap
Random Access of Files (File Pointers) Using file streams, we can randomly access binary files. By random access, you can go to any position in the file as you wish (instead of going in a sequential order from the first character to the last). Earlier in this chapter we discussed about a bookmarker that will keep moving as you keep reading a file. This bookmarker will move sequentially but you can also make it move randomly using some functions. Technically this bookmarker is a file pointer and it determines as to where to write the next character (or from where to read the next character). We have seen that file streams can be created for input (ifstream) or for output (ofstream). For ifstream the pointer is called as ‘get’ pointer and for ofstream the pointer is called as ‘put’ pointer. fstream can perform both input and output operations and hence it has one ‘get’ pointer and one ‘put’ pointer. The ‘get’ pointer indicates the byte number in the file from where the next input has to occur. The ‘put’ pointer indicates the byte number in the file where the next output has to be made. There are two functions to enable you move these pointers in a file wherever you want to: seekg ( ) - belongs to the ifstream class seekp ( ) - belongs to the ofstream class We’ll write a program to copy the string "Hi this is a test file" into a file called mydoc.txt. Then we’ll attempt to read the file starting from the 8th character (using the seekg( ) function). Strings are character arrays terminated in a null character (‘\0’). If you want to copy a string of text into a character array, you should make use of the function: strcpy (character-array, text); to copy the text into the character array (even blank spaces will be copied into the character array). To make use of this function you might need to include the string.h header file. #include #include #include int main( ) { ofstream out("c:/mydoc.txt",ios::binary); char text[80]; strcpy(text,"Hi this is a test file"); out<
Files & Streams - C++
402 The syntax for seekg( ) or seekp( ) is: seekg(position, ios::beg) seekg(position, ios::cur) seekg(position, ios::end) By default (i.e. if you don’t specify ‘beg’ or ‘cur’ or ‘end’) the compiler will assume it as ios::beg. ios::beg – means that the compiler will count the position from the beginning of the file. o ios::cur – means the compiler starts counting from the current position. o ios::end – it will move the bookmarker starting from the end of the file. o
Just like we have 2 functions to move the bookmarker to different places in the file, we have another 2 functions that can be used to get the present position of the bookmarker in the file. o o
For input streams we have: tellg( ) For output streams we have : tellp( )
You would think that the value returned by tellg ( ) and tellp ( ) are integers. They are like integers but they aren’t. The actual syntax for these functions will be: streampos tellg ( ); where streampos is an integer value that is defined in the compiler (it is actually a typedef). Of course you can say: int position = tellg ( ); Now, the variable ‘position’ will have the location of the bookmarker. But you can also say: streampos position = tellg( ); This will also give the same result. ‘streampos’ is defined internally by the compiler specifically for file-streams. Similarly, the syntax of seekg ( ) and seekp ( ) was mentioned as: seekg(position, ios::beg) Again in the above syntax, ‘position’ is actually of type ‘streampos’.
Sequential and Random Access Files Basically variables are used for temporary storage and files are used for permanent storage of data. Based on how files are accessed, they can be divided into sequential and random access files. Actually this division of files depends on how we read and write to files (physically the file is stored as a sequence of bytes in memory).
Files & Streams - C++
403 All data is represented in the form of bits. A set of 8 bits (or a byte) can be used to represent one character. A set of similar bytes will form a ‘field’. A set of related fields will form a record and a file consists of a set of related records. Let us suppose that a University maintains a database consisting of its student’s details.
Fields and records are the terms used when we deal with files. Records are equivalent to ‘structures’ or ‘objects’ in C++. Usually when storing such data (like a student record as shown above), the programmer will use one unique ‘key field’. A ‘key field’ is the field which can be used to identify or locate a particular record in the file. For example in the above diagram, the student ID number will be the key field (thus if we want to access the details about the student Ajay then we can just refer to student ID number 1). The key field should be something unique (i.e. no two records should have the same key field). Usually we write and read records from a file. Sequential access files are the simplest way of organizing a file. In sequential access files we write the variables continuously one after the other. The length of each record isn’t fixed and can vary (i.e. each record needn’t occupy the same amount of memory). The advantage of this is that we do not waste any memory. The disadvantage is that if you want to access the 3rd record stored in the file, you will have to read the first two records before accessing the third (i.e. you cannot directly jump to the third record). The reason for this is because in sequential access files the record length is not fixed and you cannot predict as to where the third record might be stored. This leads to a few other problems. It is not possible to directly insert a new data in the middle of the file. If a new record has to be inserted, the old record has to be copied into a new file (up to the point where you want to insert), then the new entry should be added to the new file and then the remaining records from the old file have to be copied to the new one. You cannot directly update/modify a record in sequential access files. Let us suppose that we have a disk file containing the data:
Files & Streams - C++
404
This file has been stored sequentially and maybe the name Ajay needs to be modified to Williams. If you attempt to overwrite the existing record the resultant will be: 1 Williams2 Suresh 88
Because data is stored continuously in a sequential file, if the modified entry you make is longer in length than the existing entry then the neighbouring field will get overwritten. Random access files overcome this problem since they have fixed length records. The problem here is that even if we want to store a small sized record we still have to occupy the entire fixed record length. This leads to wastage of some memory space. For example if we are using 10 bytes to store a complete sentence in the file then even if you want to store a single letter (like ‘a’) 10 bytes will also be used up for this. But even though some memory space is wasted this method will speed up access time (because now we know where each record is stored. If a record length is fixed as 10 bytes, then the fifth record will start at byte number 50 and it is easier to jump directly to that location instead of reading the first four records before accessing the fifth). Word processing program usually store files in a sequential format while database management programs store files in a random access format. A simple real life analogy: Audio tapes are accessed sequentially while audio CDs (Compact Discs) are accessed randomly. So, how do we create sequential and random access files in C++? Actually we have already covered both these topics without explicitly using the terms sequential and random access. Whenever you make use of the ‘read’ and ‘write’ functions to write structures/objects to a file, you are actually creating a random access file (because every record will have the size of the structure). Whenever you use the << and >> operator to read and write to disk files, you are accessing the file sequentially (this was the first example program). Whenever you write to a stream (or a file) using << operator, you are writing varying length records to the file. For example: You might first write a string of 10 characters followed by an integer. Then you may write another string of 20 characters followed by a ‘double’. Thus the records are all of varying lengths. To effectively use random access files we make use of the seekp( ) and seekg ( ) functions. Though these can be used on sequential files it will not be very useful in sequential access files (because when you are searching for a data you are forced to read each and every character/byte, whereas in random access files you can jump to the particular record that you are interested in).
Files & Streams - C++
405
Command Line Arguments You know that functions can have arguments. You also know that main ( ) is a function. In this section we'll take a look at how to pass arguments to the main function. Usually filenames are passed to the program. First of all, let us suppose that we have a file by the name marks.cpp. From this file we make an exe file called marks.exe. This is an executable file and you can run it from the command prompt. The command prompt specifies what drive and directory you are currently in. The command prompt can be seen as the ms-dos prompt. C:\WINDOWS> This denotes that you are currently in C drive and in the directory named Windows. (By the way, if you want to go to the MS DOS command prompt from Windows, just go to "Start" and click on "Run". Type "command" in the text box and click "Ok"). Your marks.exe program is in this directory (let us assume it is here. If it isn’t in this directory then you have to change to that particular directory). To run the program you will type: C:\WINDOWS> marks name result You must be thinking that we will type only the name of the program? In this case the C++ program that you wrote is assumed to have arguments for the main ( )function (i.e. in the marks.cpp file you have provided arguments for the main( ) function): int main (int argc, char * argv[ ] ) argc (the first argument - argument counter) stands for the number of arguments passed from the command line. argv (argument vector) is an array of character type that points to the command line arguments. In our example, the value of ‘argc’ is 3 (marks, name, result). Hence for ‘argv’ we have an array of 3 elements. They are: argv[0] which is marks. argv[1] which is name argv[2] which is result Note: argv[0] will be the name that invokes the program (i.e. it is the name of the program that you have written). If you feel a little vague in this section don't worry. In the next section we'll take a look at a simple program. A program using Command Line Arguments // This file is named test.cpp #include int main ( int argc, char * argv[ ] )
Files & Streams - C++
406 { cout<<"The value of argument counter (argc) is: "<test one two three You have to go to the folder in which you have the test.exe file (I assume that your program is in the windows directory in C drive). The output will be: The value of argument counter (argc) is: 4 c:\windows\t.exe one two three There are numerous things you can do with this. You can pass the names of files upon which you want the C++ program to operate, or you could pass the name of a file that you want your program to create, etc. Depending on your application, you can make use of the arguments. For example: if you write a program for zipping files, then the arguments can be used to specify the files that you want to zip.
Recap • • • • • • • •
A stream is an intermediate medium used for accessing other devices (like disk files or printers). The general I/O classes are: istream, ostream and iostream. The predefined streams are: cout, cin, cerr and clog. The stream status flags (or bits) are used to determine the state of the stream. The file I/O classes are derived from the general I/O classes. They are: ifstream, ofstream and fstream. In random access of files two pointers called the ‘get’ and ‘put’ pointers are used. They are also called file position indicator or file pointer. The read ( ) and write ( ) function can be used to read and write structures from a file. Command line arguments are used to pass arguments to the main ( ) function.
Files & Streams - C++
Chapters 9 to 11 (Inheritance, Operator overloading and streams)
Q.) Complete the following tabulation on operator overloading by specifying the return type which one would normally use: Operator overloaded + :: = < += ! == ?:
Return type
Q.) Which form (postfix/ prefix) of the unary operator ++ involve more overhead? Or are they just different in terms of representation alone? A.) When we say: x = ++y; ‘y’ is incremented and the value is assigned to ‘x’ (first increment and then obtain value). Whereas in: x = y++; the value of ‘y’ is first assigned to ‘x’ and then ‘y’ is incremented (so ‘x’ actually has the old value of ‘x’). Here we obtain the value first and then increment. To remember how they work all you need to do is take a look at how we use them; when we say ++y the ++ comes before the ‘y’. Thus we first increment and then obtain the value whereas in y++, we get ‘y’ first and then increment it (it’s just a memory aid since many new programmers tend to get confused with the 2 forms). Next let’s consider the following statement: y++++;
//Error
This is equivalent to: (y++)++;
Q&A - 9-11
Obviously y++ returns an integer, so why is it not permitted? That’s because the return type of the postfix operator is a constant (in this case a constant integer). Thus you can modify the returned value again. Let’s consider the other statement: ++++y;
//no error
You wouldn’t get an error because the prefix operator returns a reference (rather than an object). Why the difference in return types? In ++y, all that needs to be done is to increment the current value and return a reference to the same object. But in y++, we need to create a new object to store the old value of ‘y’, then increment ‘y’ and then return the new object (which is why postfix form means ‘get value and then increment’). Thus the postfix form would be less efficient than the prefix form since it involves an extra object. And the following fragment will work: i=5; ++++++i; would end up incrementing ‘i’ 3 times (‘i’ would have a value of 8). The statement is equal to: (++ ( ++ (++i) ) ); And the prefix form of ++ has associativity from right to left. This works because the prefix form of ++ returns a reference to the same object. But we wouldn’t want to use such a statement because it reduces readability (you will have to keep counting the number of +s each time you go through the code).
Q&A - 9-11
Q.) Why doesn’t the following code compile? class employee { public: employee(int x) {} }; class teacher:public employee { public: teacher( ) {} }; class librarian:public employee { public: librarian(int y):employee(y) {} }; int main( ) { teacher f1; librarian m1(5); return 0; }
A.) When an object of type teacher is created, the compiler will try to call the default constructor of the base class (i.e. of the class employee). But employee doesn’t have a default constructor and thus the following code produces the error: public: teacher( ) {} The class librarian is fine since here we are explicitly calling the parameterized constructor for employee (here the compiler won’t call the default constructor).
Q.) What does “is a kind of” relationship in C++? A.) This term is just another name for the “is a” relation (public inheritance).
Q&A - 9-11
Q.) Why would a programmer place a constructor in the protected region of a class? Or is this an error? class base { protected: base( ) { cout<<"base constructor"; } }; class derived:public base {}; int main( ) { derived d1; return 0; }
A.) This is perfectly legal. By placing the base constructor in the protected region, a user cannot instantiate an object of type ‘base’. The following code would cause a compiler error: base b1;
//error-cannot access protected constructor
But any class derived from ‘base’ will be able to invoke the base class constructor (as done in the question). The output of the program will be: base constructor
Q.) Is it legal to place the constructor in the private region of a class? class base { private: base( ) { cout<<"base constructor"; } };
A.) This would serve no useful purpose because now even a derived class cannot access the constructor of ‘base’ (because private members are inaccessible by the derived class irrespective of how you inherit the base class). And as discussed in the earlier question, neither can we directly instantiate an object of type ‘base’ (this would lead to a compiler error).
Q&A - 9-11
Q.) What does the following code do? What is the purpose of the keyword ‘using’? class base { protected: void b_func( ) { cout<
A.) Any object of type ‘derived’ can now invoke the function b_func( ) just as if b_func ( ) were a member function of class ‘derived’. If the code: public: using base::b_func; was not written then the following would be illegal: int main( ) { derived d1; d1.b_func( ); //error- cannot access protected member return 0; } The general syntax for ‘using’ in such a scenario is: using base-class-name:: base-class-function-name; We shouldn’t specify the parameters. i.e. the following is wrong: using base::b_func( );
//error
Q.) Why don’t we have a virtual constructor? The concept of ‘virtual’ functions is used since the type of object is not known at compile-time. The calling object is determined at run-time instead. But a constructor is used to create/ construct an object and this is almost certainly known while compiling. We can construct something only if we know what we are going to create. Thus C++ doesn’t provide for virtual constructors (but there are some ways of obtaining the effect of virtual constructors- generally not required).
Q&A - 9-11
Q.) What is wrong with the following code (it crashes sometimes and sometimes it gives weird results): class myarray { public: int *ptr; myarray(int val) { ptr= new int; *ptr=val; } ~myarray( ) { delete ptr;
}
void display( ) { cout<
}
}; int main( ) { myarray arr1(10); cout<
A.) • • • • • •
Object arr1 is visible throughout the program but arr2 is visible only within the scope of the inner block. Within the inner block, arr1 is assigned to arr2 (since assignment operator hasn’t been overloaded for the class the compiler provides a default assignment operator which perform bitwise copy operation). Now the pointer of both arr2 and arr1 point to the same memory location. When we exit the inner block, arr2 object will be destroyed (local objects are destroyed at the end of their scope). The destructor of arr2 will free the memory pointed to by ‘ptr’ of object arr2 (which is actually the same memory location as that pointed by ptr of arr1). Now when arr1 tries to retrieve the value stored, it can’t do so because the memory has already been freed.
Remember: It is advisable to overload the assignment operator and copy constructor in a class which uses dynamic memory allocation.
Q&A - 9-11
Q.) What would be the output of the following? Explain the problem as well. class myChar { private: char alpha; public: myChar(char c):alpha(c) {} int operator!= (const myChar &right) { if (right.alpha!=alpha) return 1; else return 0; } }; int main( ) { myChar ch1('a'); int i = 97; cout<<(ch1 != i); return 0; }
A.) The output will be 0. When the compiler encounters: ch1! = i it is equivalent to: ch1.operator!=(i); But the operator!= function requires a character and the compiler will perform an implicit conversion of 97 into a character (97 happens to be the ASCII value for ‘a’). Thus since a= = a, the output will be 0. To avoid this from occurring, we should use an explicit constructor.
Q&A - 9-11
Bonus questions - Units IX to XI
Interview/ viva questions: 1. What is meant by operator overloading? Why is it necessary? 2. What is polymorphism? 3. Explain run-time polymorphism with an example. Differentiate between runtime and static polymorphism. 4. What is the meaning of ‘has a’ and ‘is a’ relationship in C++? 5. What is the need for an abstract class? 6. What are the operators that cannot be overloaded? Can new operators be defined in C++? 7. What are the dynamic memory operators in C++? What are the equivalent of malloc and free in C++? 8. What are streams? How are they useful? 9. Name some of the predefined streams. 10. What is the difference between: •
cin and cout
•
cout and cerr
•
Random access and sequential access
11. Write a simple program to write and read a character from a file. 12. How is the file pointer used for random file access? 13. Write down the syntax of the main function (including its arguments). 14. If we create a class with no member functions, what are the functions which will be automatically created?
Q&A - 9-11
Extra Stuff: Q.) In the complex numbers class that you had created, overload the + and – operators to add and subtract two complex numbers. Q.) In the same complex numbers class, overload the * and / operator to multiply and divide complex numbers. Q.) Create a class called ‘string’ which will consist of a character array (of maximum length 80). Overload the + operator such that two string objects will be concatenated. Q.) In the same string class, overload the ‘= =’ operator such that we can test whether 2 string objects are equal or not (for comparing use ASCII values/you can also use the ‘strcmp’ function. The overloaded operator function should return a Boolean value just like the normal == operator depending on the comparison). Q.) In the same string class, overload the ‘<=’ operator such that we can test whether the left side string object is less than or equal to the right side string object (the function should return a value just like the normal <= operator depending on the comparison). Q.) Create a class called person and then derive two classes for teaching faculty and non teaching faculty (for a school). The person class should be an abstract class. Q.) Create a file using C++ in which we can store an employee’s details (like name, experience and salary). The user should be able to store as many records as he wants. Q.) Write a C++ program to read the employee record file and display the contents. Q.) Write a C++ program to check whether a given word is present in a text document. Q.) Write a program that obtains the file name from the user through command line argument and returns the number of bytes occupied by the file. Q.) Write a program to encrypt a file (using your own algorithm for encryption) and store it in a different file. Write a decryption program that will retrieve the original file contents.
Q&A - 9-11
416
Useful Classes and Functions - I
The following topics are covered in this section: • • •
Manipulators Flushing setw and other manipulators
There are two ways in which we can perform stream-formatting operations: using manipulators or using the stream member functions.
Manipulators There are many stream-formatting functions available but it is a little tedious to use them. Manipulators duplicate these functions and make it easier to carry out stream-formatting operations. Usually it is the output (what we display on the screen) that needs to be formatted to suit our needs. In this case, ‘cout’ is the stream. One of the most commonly used manipulators is ‘endl’. This is used to end the current line and go to the next line. You cannot use ‘endl’ separately. All the manipulators have to operate on streams. Manipulators that do not require any arguments are defined in the iostream.h header (manipulators with arguments are defined in the iomanip.h header file). Let's check out an example program. # include int main( ) { cout<< "Hi, this is a test program"<
You should see three empty lines above this Each ‘endl’ will cause the present line to be terminated and hence, there will be three empty lines in between the two sentences. Remember: Don't use double quotes on endl.
Useful Classes & Funtions
417 The above program can also be written as: cout<< "Hi, this is a test program"<
Useful Classes & Funtions
418 # include #include #include int main( ) { ofstream out("c:\\test.txt",ios::binary); out<<12; system("PAUSE"); out.close( ); return 0; } When the program executes: out<<12; It will not write the number ‘12’ to the file test.txt. To verify this run your program in windows. While the program is running, because of: system("PAUSE"); The program will pause for the user to press a key. Now go to the desktop, open "My Computer" and in C: drive open the file test.txt (or open this file from ‘Notepad’). You will notice that the file is empty. Thus, though we think that 12 has been written to the file, in reality it hasn’t been written as yet. The number ‘12’ is still in the buffer. Close the Notepad and press a key to continue the execution of the program. After execution once again open the file test.txt in Notepad and you will see 12 written in the file. Thus since the buffer was not full (because we wanted to write only a small number to the file) the data was not written immediately. Instead the buffer waited hoping that more data would be entered. But when out.close( ) was encountered it knew that now it had to flush the data from the stream (because the stream is going to be disassociated from the file). To instantly update the file, we can make use of the flush( ) function as shown below: # include #include #include int main( ) { ofstream out("c:\\test.txt",ios::binary); out<<12; out.flush( ); //12 is written immediately to the file system("PAUSE"); out.close( ); return 0; } In the above program we forcefully flush the stream and data is immediately written to the file. Flushing forcefully is useful in situations where you need to update the file continuously (when there is likeliness of frequent power shortages etc.) otherwise you might lose the data present in the buffer.
Useful Classes & Funtions
419
setw( ) This manipulator is used to set the width of the data being displayed. If the data is longer than the width you specify, this manipulator will not truncate your data to fit the width. Instead it will display the entire data but this will lead to misalignment. The setw( ) manipulator defined in the header since it takes arguments. #include #include int main( ) { cout<
Harry Joe
The diagram should make it clear as to how the setw( ) works.
The statement: cout< #include int main( ) { cout<
Useful Classes & Funtions
420 Output is: 3.46 3 You can see that the result gets rounded up to the number of places that you want. 3.456 is displayed as 3.46 because we set the precision to 3 digits only. The other manipulators available in are: Name of the Manipulator
Purpose
ends
Inserts a null character (‘\0’)
dec
Display integer in base 10
oct
Display integer in base 8
hex
Display integer in base 16
ws
Skip whitespace in input stream
flush
Flush the stream
The manipulators available in are (these manipulators require arguments): Name of the Manipulator
Purpose
setiosflags(names-of-flags)
Set formatting flags
resetiosflags(names-of-flags)
Reset formatting flags
setbase(int base)
where base=8,10 or 16
setfill (char c)
Uses ‘c’ as the fill character
setprecision (int n)
Changes precision to ‘n’
setwidth (int n)
Width set to ‘n’
In the table above ‘names-of-flags’ refers to the formatting flags that can be set/reset depending on which manipulator you use. If a formatting flag is set then that particular format will be active (or effective). The formatting flags are tabulated below:
Useful Classes & Funtions
421 Name of the Formatting Flag
Purpose
ios::showbase
display an integer’s base
ios::showpos
indicates positive numbers by using the + sign
ios::skipws
skip whitespaces
* ios::dec
display integers in base 10.
ios::hex
display integer in base 16
ios::oct
display integer in base 8
*ios::fixed
use fixed notation for displaying floating point numbers
ios::scientific
use scientific notation (i.e. using exponential)
*ios::left
align left
ios::right
align right
* indicates that these flags are already set by default. By the way, these flags are usually known as IOS Formatting flags. If you want to play around with these flags, then you should make use of setiosflags(names-of-flags) or resetiosflags(names-of-flags) An example to illustrate the use of these formatting flags is given below: #include #include int main( ) { cout<
Useful Classes & Funtions
422 The output is: +20 +3.004567e+002 Beware: All the formatting flags come under the namespace std::ios:: This is why we have used ios::scientific and ios::showpos. This will be dealt with in the section on ‘namespaces’.
Useful Classes & Funtions
423
Useful Classes and Functions - II
The following topics are covered in this section: • •
Custom Manipulators Strings
Creating New Manipulators (Custom Manipulators) In the previous chapter we dealt with how to overload << and >>. C++ also provides a way to create your own manipulators. You can create your manipulators to work along with ‘cout’ (output stream object) or with ‘cin’ (input stream object). Let us consider an example of a manipulator dealing with ‘cout’. The syntax for creating a manipulator is: ostream& name (ostream &str) { //code to be executed; } In the case of manipulators to be used with input stream, only one modification is necessary. istream& name (istream &str) { //code to be executed; } Remember: You should return the stream at the end of your manipulator definition. Only then can you use your manipulator in cascaded operation. #include ostream& divider (ostream &str) { str<<"--------------------------------"<>x; cout<
Useful Classes & Funtions
424 The output is: -------------------------------Enter an integer value : 98 -------------------------------The value you entered is : 98 In the above program we have created a new manipulator called ‘divider’. This will simply display a series of dashes on the screen as can be seen in the output. This manipulator can be used for objects belonging to ‘ostream’. Since ‘cout’ belongs to ‘ostream’, we can use this manipulator to operate only on output streams. In a similar way you can also create manipulators to operate on input streams as well. The advantage of creating your own manipulators is that you can reduce on the amount of typing and make your code easier to understand. Stream Formatting Member Functions: The alternative to manipulators is to use the member functions available in streams. Manipulators can be used to perform all the stream-formatting that can be done using member functions. Each stream has a set of format flags associated with it (just like what we have seen earlier). There are member functions available to either set/reset these format flags (the information is formatted depending on the setting of these flags). The functions available are: • • • • •
These functions can be called from streams using the dot operators (since they are all member functions). The setf( ) and unsetf( )functions are used to set and reset the format flags respectively. The list of format flags used are the same as those described for manipulators. The bitwise OR operator ( | ) can be used to set or reset more than one flag using setf( ) or unsetf( ).
Strings There are two methods for creating and using strings. A character array terminated in a null character (‘\0’) is called a string but in new compilers you can make use of a class called ‘string’. There are a few reasons why you might prefer to use the existing string class instead of creating a character array. The problem with a character array is that many (in fact none) of the operators will work with character arrays. You cannot use ‘+’ and expect the two
Useful Classes & Funtions
425 arrays to get added (adding in this case would mean concatenation of the two character arrays). You cannot use the assignment operator and assign a value to your character array. Many beginners make this fundamental mistake. int main( ) { char name[40]; name="John"; //wrong cout<>name; cout< // New style headers #include using namespace std; int main( ) { string name; name="John"; cout<
Useful Classes & Funtions
426 string name; //an empty string string name("John"); //initialized to John string name(another-string); //initialized to value of another-string string name(5,’j’); //initialized to "jjjjj" Again there are different ways to assign values to strings: name = "John"; name = another–string; For example: int main( ) { string s1("Jenny"); string s2; s2=s1; cout<
Useful Classes & Funtions
427 The program would be as below: int main( ) { string s1("Jenny"); string s2; s2=s1; s2=s1+s2; cout< or <. How do you think two strings are compared? See the following results: e > bee - True Hello>hello - True dear>bee - True He>HE - False From the above results it is clear that upper case letters are greater than corresponding lower case letters. An alphabet that comes later in A to Z is greater than one that comes before it. Comparison does not depend on the length of the strings. To find the length of a string, you can make use of the length ( ) member function of the string class. If s1 is a string object with the value of "Jenny", then s1.length( ) Will return a value of 5. Extracting strings: You can extract a part of the string by using the substr ( ) function. The syntax is: substr(position-from-where-you-want-to-start, number-of-characters-to-extract);
Useful Classes & Funtions
428 This function can be called only through a string object (because it is a member function of the class ‘string’). int main( ) { string s1("Jenny is a wonderful person"); cout<
is a
Searching in Strings: There are two functions for the purpose of searching for particular characters or strings within strings. These are the find (find) and reverse find (rfind) member functions. Both functions will return the position (an integer) of the place where the particular character/string is located. Remember: The results that you get are always starting from 0. So if your result is 2, it actually means the 3rd character. #include #include using namespace std; int main( ) { string s1("penny is a wonderful person"); cout<
Useful Classes & Funtions
429
Useful Classes & Funtions
430
Useful Classes and Functions - III
The following topics are covered in this section: • •
Character Arrays Mathematical Functions
Some compilers may not permit String Objects The string objects that we discussed above can be created only in new C++ compilers. In older compilers you cannot create an object of type string (this is because the string class is not defined). In such cases you have to use character arrays terminated by a null character for strings. But there are many functions provided for operating on such strings as well. A few of these are discussed below. These functions were part of the standard C library and it has been incorporated into the C++ library as well. To use these functions, if you are using an old C++ compiler, you should include the header file string.h. If you have a new C++ compiler that supports namespaces, then you should use the header Copying strings and concatenating strings: For copying we have a strcpy( ) function and for concatenation we have the strcat( ) function. int main( ) { char s1[20]; char s2[20]; strcpy(s1,"hi"); strcpy(s2,"bye"); strcat(s1,s2); cout<
Useful Classes & Funtions
431 String length: This function will give you the length of the character array. strlen(character-array) String Comparison: The function will return an integer. The syntax for the function is: int strcmp(char-array s1, char-array s2); If s1>s2, then the result will be 1. If s1 char letter = 'a'; letter = toupper(letter); cout<
Useful Classes & Funtions
432
Mathematical Functions In many of your program you might want to perform some special mathematical operations (other than the basic ones). For this purpose it will be helpful if you know about the existing mathematical functions provided in C++. In older compilers you have to make use of the math.h header file. If you are using a new compiler and adding headers through namespaces, then you should include the header Descriptive Name Trignormetric functions (and hyperbolic)
Purpose
Syntax
calculate the cosine of a given angle
double cos(double angle)
Inverse trigonometric functions
returns the value of the angle in radians
double acos(double value)
Hypotenuse
Pass two arguments, this function will find the hypotenuse.
double hypot (double val1, double val2)
Similar functions sin, tan, cosh, sinh, tanh
asin, atan
Remarks Remember to give the angle in radians (and not in degrees). Radians are in terms of the constant ‘pi’ (one radian equal 180 degrees). It is the opposite of the cos, sin and tan functions. Value should be between –1 and 1 when using acos ( ) or asin ( ) functions. For example: if we code: cout<
Square root
Logarithm
Returns the square root of the given number. Functions to calculate natural logarithm and also base 10 logarithms.
Absolute value Two functions to obtain the absolute value of a
double sqrt (double val)
double log (double val) To calculate natural logarithm of ‘val’.
int abs (int val) The abs( ) function will operate on integers and return
Useful Classes & Funtions
double log10 (double val) To calculate logarithm of ‘val’ to the base 10. double fabs (double val)
log10(10) = 1 log(10) = 2.3
fabs(2.1) = 2.1 fabs(-2.1) = 2.1
To retain the decimal
433
Raising to power
Exponent
number. Absolute value means only positive numbers.
integers.
Raise a base to the power of an exponent. If we say, 23, then 2 is the base and 3 is the exponent. This function will raise the natural logarithm base ‘e’ to the power of the argument provided. The value of e is 2.718.
double pow(double base, double exponent)
double exp(double power)
places use the fabs( ) function.
abs (2.1) = 2. abs (-2.1) = 2 abs( ) will ignore decimal places. pow(2,3) is equal to 8.
cout<
Rounding Off values: ceil: The ceil ( ) function is used for rounding up a number to an integer. But it doesn’t exactly do rounding up. It will return the lowest integer that is greater than the number you gave as argument. double ceil (double val) If val=2.1, then the returned value will be 3. If val= -2.1 then the returned value will be –2. Floor: This is again similar to ceil ( ) function except that this will return the highest integer that is less than the value you provided. double floor (double val) Suppose that val=2.1, after the floor ( ) operation you will get a result of 2. If val = -2.1 then the result will be –3.
Useful Classes & Funtions
434 Random Numbers: The rand ( ) function can be used to generate random numbers. The srand( ) function can be used to initialize the random number generator. srand ( ) will not return any value while the rand ( ) will return a random integer. • •
int rand( ) void srand (unsigned int val)
The example below should clarify your doubts about srand ( ). int main( ) { srand(10); cout<
Useful Classes & Funtions
435
Useful Classes and Functions - IV The following topics are covered in this section: • • •
Good Programming Practices Typedef Bitwise Operators
Good Programming Practices First of all, there isn’t any international standard for the purpose of writing C++ programs. You are free to write the way you want to. This section is something like a section of tips that you could use. •
Always before you start coding, it is better to have a rough idea written on paper. This is called a pseudocode or an algorithm. You will realize the advantages of this when you start coding larger programs.
•
Give a good thought before creating classes. Have a clear idea about what data you want to hide and what are the methods you need.
•
Whenever you make use of mathematical calculations in your program, do not use long statements. For instance do not type: x = 3+4*10/5+1-4/2; Writing something like this might make one feel that the person who wrote this was a genius! Avoid such statements (even the programmer himself would have to break his head before he is certain of the answer). Use simple brackets to break the long calculations into smaller parts as shown below: ( 3 + ( 4 * (10/5) ) + 1 - (4/2) ); Now, anyone will be able to say that the result of the above statement is 10. See how a few brackets can make a big difference!
•
Leave adequate whitespaces in your C++ programs. Whitespaces are not taken into consideration by the C++ compiler. So by using whitespaces you can keep your program clean. ( 3 + ( 4 * (10/5) ) + 1 - (4/2) ); is better than (3+(4*(10/5))+1-(4/2));
•
Indent your C++ programs properly. for (i =0; i<10 ; i++) { for (int j = 0 ; j<10 ; j++) { cout<
Useful Classes & Funtions
436 •
Use braces to clearly define your ‘for’ loops, ‘if’ and while blocks. By using braces to define the starting and ending of these blocks, there will be no confusion as to what statements come under the particular block. { for (int i = 0 ; i<5 ; i++) cout<
The statement i*i is not considered as part of the ‘for’ loop itself. This problem can be avoided if you clearly specify the ‘for’ block. int main( ) { for (int i=0;i<5;i++) { cout<
Useful Classes & Funtions
437 Now there is no confusion about what is included in the ‘for’ block. •
Make sure your variable names are not too long and also try to give some meaningful names for variables. The same rule applies for names of functions, classes and objects as well.
•
Use lots of comments throughout your program. You should always write what you are trying to do within comments. This way, even if you continue your work after one week you will know exactly what you were attempting to do. Use comments as frequently as possible (for functions you can describe about the function in short; for classes you can give a brief idea about the class etc.). Don’t use comments for selfexplanatory code.
•
A new line is not a statement terminator. For instance when using ‘cout’, if the line to be displayed seems to be very long in your compiler you can split it up in two lines as follows: cout<<"This is a really really really" " long line that i am typing";
•
In classes, make sure that you provide all the needed constructors. It is always good to provide a no argument constructor that will initialize all your member data.
•
Avoid using macros for constants and functions. Make use of C++ inline function and the const keyword for these purposes.
•
Be very careful while using the ‘new’ operator. Make sure that every ‘new’ has a corresponding ‘delete’ in the program.
•
Use correct data types (particularly whenever using numerical data types like float, int or double). If an ‘int’ is sufficient don’t go in for ‘double’ (since this occupies more memory and also calculations involving double will take longer). Do not rely on implicit type conversions.
•
Minimize the amount of work performed within loops. Perform calculations which do not involve the iteration variable outside the loop.
•
Avoid using function calls within a ‘for’ loop conditional expression. A commonly used for loop is: for (int j=0; j<=strlen(str);j++) { } This forces the strlen ( ) function to be invoked for every iteration (every function call involves a lot of overhead). You can rewrite the above code as: int len=strlen(str); for (int j=0; j<=len;j++) { } Now the strlen( ) function is invoked only once.
Useful Classes & Funtions
438
Typedef Typedef basically stands for type definition. The keyword ‘typedef’ lets you create your own data types; well, not exactly your own data types but your own names for the standard data types. Consider the following: int x; means that x is declared to be an integer variable. Now, using typedef you can give a different name for ‘int’. The syntax for using different names instead of the standard ones is: typedef standard-name new-name; For example: typedef int weight; weight x=50; In this case, we are using the name of ‘weight’ instead of the standard name ‘int’. Thus you can now declare your variables by using ‘weight’ instead of ‘int’. Basically we are just giving ‘int’ a new name called ‘weight’. Remember: By using typedef we are only giving a new name for an existing standard data type. We are not creating a new data type.
Bit-wise Operators This section will explain another set of operators. Bit-wise operators will act on binary digits. Maybe you know that C programming supports assembly level programming. In assembly level programming there is a definite necessity for operating on bits (mind you, not decimal numbers but binary digits). There might be other applications where you would like to manipulate data at the bit level. For example you could use it for hiding data within images (this is called steganography). You can make some bit level manipulations in your image file (such that the image appears to be the same) and hide data within the file. Basically if you want to operate on the individual bits instead of operating on the number as a decimal number, then you should use bitwise operators. Bit-wise operators can also be used to perform some calculations faster or with lesser number of steps. The bit operators are: • • • •
AND (&) OR ( | ) EX-OR (^) NOT (~)
If you’ve done a course in digital electronics you will know about the AND gate, OR gate, EXOR gate and NOT gate. These operators are exactly similar to these logic gates. Let us assume that ‘x’ and ‘y’ are two separate bits (they are binary digits not decimal numbers). The effect of each operation on these bits is shown below:
Useful Classes & Funtions
439 x
y
x&y
0
0
0
0
1
0
1
0
0
1
1
1
AND Operation The AND operator will give a result of 1, only if both ‘x’ and ‘y’ are equal to 1. Similarly, the OR operator will give a resultant value of 1 if ‘x’ or ‘y’ value is 1. EX-OR (stands for exclusively OR). This operator will yield a value of 1 only if ‘x’ alone is 1 or if ‘y’ alone is 1. The NOT operator is also known as the complementary operator. If you give an input of 1, the output will be 0 and vice-versa. x
y
x | y (x OR y)
0
0
0
0
1
1
1
0
1
1
1
1
x
y
x ^ y (x EXOR y)
0
0
0
0
1
1
1
0
1
1
1
0
OR Operation
EX-OR Operation x
~x (NOT x)
0
1
1
0
NOT Operation
Useful Classes & Funtions
440 First of all, you should have an idea about how these operations will work on a group of binary digits. Eight bits make up a byte and we have already seen the process of conversion from decimal to binary format in the first chapter itself. Let us assume that we have two numbers: 128 and 255. Their binary representations will be: 0100 0000 and 1000 0000.
0 1 0 0 0 0 0 0 When you perfom bitwise AND operation, each pair of bits & 1 0 0 0 0 0 0 0 are ANDed individually -----------------0 0 0 0 0 0 0 0 Resultant value (0)
0 1 0 0 0 0 0 0 OR operation performed |10000000 -----------------1 1 0 0 0 0 0 0 Resultant value (192)
0 1 0 0 0 0 0 0 NOT operation performed on decimal value of 64 ~ -----------------1 0 1 1 1 1 1 1 Resultant value (191)
Useful Classes & Funtions
441 If you apply the tabulation that was given, you will get the results shown above. We can do obtain the same result using a C++ program. int main( ) { unsigned char x,y; x=128; y=64; cout<>1);
Useful Classes & Funtions
443
Preprocessor Directives
The preprocessor is part of your compiler and when you compile a program the preprocessor is the first person to scan through your coding. It is particularly bothered with some specific instructions that are meant exclusively for it. These instructions are known as preprocessor directives and the preprocessor searches your program for them. How does it know that some code is meant for it? Preprocessor directives start with the # (hash) symbol and also they need not be terminated by a semi-colon. You have different types of preprocessor directives: • • • • • • • •
#include: You’ve already been using this in all of your C++ coding. It simply includes the contents of whatever you include into the source file. One favourite C++ question is: What’s the difference between #include "iostream.h" #include The only difference is in the way the compiler searches for the iostream.h file. If you use the double quotes, the compiler will start searching for the header file in the directory where you have your source file. If it doesn’t find it here, it will then search in the directory where the compiler is supposed to search for header files. If you use the angle brackets, the compiler will search in the directory supposed to be having all header files. You usually have an option in your C++ compiler where you can change the default directory used by the compiler (of course don’t change it unless you have specifically made a new directory with the header files). #define: We’ve seen about this in the section on constants. This used to be the method for C programmers to create constants. Constants created using #define are also called as "macros". It is not only used to create constants but can also be used for defining functions. It is equivalent to the inline function option available in C++. Of course in C++ it is not advisable to use #define because you can make use of the inline keyword and also the const keyword. #undef: This is the opposite of #define. It will undefine anything that you defined earlier. For example: #define speed 100 #undef speed #define speed 200
Preprocessor Directives
444 #if….#elif…..#else…..#endif : This is equal to our if….else if….else statement construct. Endif is used at the end to indicate the end of the ‘if’ block. #include using namespace std; #define speed 200 int main( ) { #if speed= =200 cout<<"Speed is 200"; #elif speed = =100 cout<<"Speed is 100"; #else cout<<"I don't know what's the speed!"; #endif return 0; } The output is: Speed is 200 #elif is actually a shortened expression for else if. Instead of mentioning the directives inside the main ( ) function you could use them for defining the value of some other value outside the main ( ) function. #include using namespace std; #define speed 200 #if speed= =200 #define brake 100 #elif speed= =100 #define brake 50 #else #define brake 25 #endif int main( ) { cout<
Preprocessor Directives
445 #ifdef…..#ifndef: #ifdef means if the particular variable is defined do something. #ifndef means if the particular variable is not defined do something. The syntax is: # ifdef variable-name //body #endif This can also be written as: # if defined (variable-name) //body #endif Check out a short program given below: #include using namespace std; #define speed 200 int main( ) { #ifdef speed cout<<"Speed Defined"; #else cout<<"Speed not defined"; #endif #ifndef brake cout<
Preprocessor Directives
446 #line: All your programs have a certain number of lines (depending on the size of your coding). When you get errors, the compiler will inform you about the line number, which has an error. Using the #line line-number you can change the starting line number that is used by the compiler for the program. This might not seem very useful to you but anyway it’s better to know about the various directives that exist. #pragma: This is used to make changes in your compiler settings. The use of this directive is compilerdependent since each compiler will have its own pragma directives. For example the #pragma pack ( ) directive (which we discussed in an earlier chapter with reference to padding of structures) is available in VC++ but not in Turbo C++. The preprocessor also recognizes two operators: # and ##. 1. The String operator (#): When this operator is used in a macro definition, it will convert whatever follows it into a string. #include using namespace std; #define disp(val) cout<<"You passed : " #val int main( ) { disp(20); return 0; } The output is: You passed : 20 Similarly if you write: disp(Hi); the output would be: You passed : Hi Actually, the operator # is equal to inserting two double quotation marks (this makes anything into a string).
Preprocessor Directives
447 2. Concatenation Operator (##): This is used to concatenate the operands. The operands could be anything. #define conc(a,b,c) a ## b ## c int main( ) { cout<
hiandbye
It might seem crazy to use such operators, but it is better to know about the various features that exist. Actually, programmers rarely use these two operators.
Standard defined Macros The compiler already has some defined macros. They are: __FILE__ gives the filename __LINE__ gives the line number __DATE__ gives the date the code was compiled __TIME__ gives the time the code was compiled __cplusplus this is defined when the source code in compiled as a C++ code Check out an example: #include using namespace std; int main( ) { cout<
Preprocessor Directives
448
Namespaces and new headers The programs that we've seen so far are not wrong but they can't be called as 100% C++ programs either. The reason is because we've been making use of header files (such as iostream.h etc...). Making use of these .h files is more similar to C programming. Of course it isn't wrong to use them in C++ but there is a better method provided in C++. This method makes use of namespaces. Instead of # include we shall use the following two lines: #include using namespace std; Firstly take note of the fact that there is no ‘.h’ in the include statement. This is the format of the new style headers used in C++. They do not use .h files. Old C++ compilers may not support namespaces. In case you are using such a compiler then you have to stick to the old method of #include with the ‘.h’ extension. But all the latest C++ compilers will support namespaces and they will also support the old style headers. When C++ was created it used header files like those used in C programming. But then changes were made. Though it still supports the old header files, they are not recommended. You know that iostream.h is a file. C++ created a new style of its own. In this method we do not specify filenames. Instead it is a method that makes sure that appropriate definitions required by the C++ library have been declared. The new style headers are not filenames and hence the .h extension is left out. The tabulation below lists out the various headers we have used and their equivalent new style headers. Old style header name
Equivalent new style header
Purpose
iostream.h
General purpose I/O
fstream.h
File I/O operations
iomanip.h
Manipulators
string.h
character array string operations
math.h
Mathematical Functions
stdlib.h
Memory allocation, numeric and system functions
-
(supports the string class).
String objects
Namespaces & Newheaders
449 If you notice in the tabulation above, some of the new style headers make use of the prefix ‘c’ in the header file. For example: stdlib.h is now: These headers were actually part of Standard C and since C++ supports C these headers were retained. Though these headers are supported they could be eliminated in the future. When using a new style header you can make use of the namespace statement: using namespace std; A namespace is a region of declaration. The purpose of namespace is to avoid collision of identifiers (i.e. to avoid collision of variables or class names or function names etc.). Elements declared in one namespace are different from those declared in another. Always make use of C++ new style headers whenever you write a C++ program. The first.cpp program using namespaces: You can make use of new style headers in all the other programs that we have seen so far. Thus the first program you wrote will become: # include using namespace std; int main( ) { char letter; cout << "Enter any letter" ; cin >>letter; cout << "The letter you entered is : " <
Namespaces & Newheaders
450 The problem can even be extended to function names( ). You could end up having a program that has two functions performing different tasks, having the same name and arguments. For example, both libraries may have the function draw( ). But both may have a different meaning to the function. So when you use the function draw ( ), the compiler would have a problem in deciding which library function it should use. Namespaces were created to solve these problems. What namespace does? Namespace localizes the visibility of names declared within it (the names are only visible within that particular namespace). So when you refer to some name, you can specify the namespace to which it belongs to avoid conflicts. Syntax: General form for creating a namespace is: namespace name { // declarations } Anything defined within a namespace is only within the scope of the namespace and not available outside. Example: #include using namespace std; namespace bact //A namespace called bact { int life; class bacteria //class belonging to namespace bact { private: int step; public: bacteria( ) { cout<<"\nCreated Bacteria"; } }; } //End of bact namespace
Namespaces & Newheaders
451 namespace vir //Another namespace ‘vir’ { int life; class virus { private: int step; public: virus( ) { cout<<"\nCreated virus"; } }; } int main( ) { bact::life=1; vir::life=0; bact::bacteria fever; vir::virus cholera; cout<<"\nThe bacteria has a life : "<
‘life’ is visible within 2 namespaces. So when referring to it, you have to use the scope resolution operator to access it (to indicate which ‘life’ you are referring to).
Similarly when creating an object belonging to the class ‘bacteria’, remember: •
The class ‘bacteria’ is present within the namespace ‘bact’. So you have to specify the namespace and then the scope resolution operator.
But once the object has been created, you needn't keep specifying the namespace (i.e. for calling member functions you needn’t specify the namespace again).
Namespaces & Newheaders
452 The ‘using’ Keyword In the previous section, you must have noticed that each time we refer to a namespace variable you have to make use of scope resolution operator. You also need to specify the namespace. Doing this repeatedly is a tedious job, especially if you are going to make use of a particular namespace frequently. Imagine how a simple program cluttered with the name of the namespace and the scope resolution operator everywhere would look. The best solution is to use the keyword using. What about namespace std? The std namespace is the area in which the entire Standard C++ library is declared. Remember how we start our programs: #include using std namespace; int main ( ) { } The ‘using’ statement informs the compiler that you want to use the ‘std’ namespace. The keywords cout, cin etc. are all present in the std namespace. You can also make use of the following method for writing programs: #include int main ( ) { int x ; std:: cout<< "Enter a number : "; std:: cin>>x; std :: cout<
Namespaces & Newheaders
453 There are two methods to make use of the using keyword: using namespace name; //This is called as the Using Declaration Example: using namespace bact; or using name :: member; //This is called as the Using Directive Example: using bact :: life; In the first case, you don't need to make use of scope resolution when you are referring to any variable or function belonging to namespace ‘bact’. In the second case, only the variable ‘life’ belonging to the ‘bact’ namespace is made visible. Check out the example below: #include //we’ve not specified using namespace std; namespace bact { int life; class bacteria { private: int step; }; } namespace vir { int life; class virus { private: int step; }; } int main( ) { using namespace bact; //the compiler uses bact namespace life=1; //this refers to ‘life’ of ‘bact’ vir::life=0; bacteria fever; vir::virus cholera; std::cout<<"\nThe bacteria has a life : "<
Namespaces & Newheaders
454 In the main ( ) function, we have said using namespace bact; ‘life’ is a variable belonging to ‘bact’ namespace and the class ‘bacteria’ is a class belonging to the ‘bact’ namespace. Since you’ve asked the compiler to use the ‘bact’ namespace, you don't need the scope resolution operator whenever you are referring to something from ‘bact’ namespace. Suppose you want to refer to the ‘life’ variable of the namespace ‘vir’, then you have to explicitly say so (using scope resolution). Note that in the above program we are making use of std::cout because we haven’t mentioned: using namespace std; So, we need to explicitly state the namespace to which ‘cout’ belongs to (otherwise your compiler will say that it doesn’t know what ‘cout’ is). What if we declare a variable outside the namespaces? In C++, if you declare a variable outside the main ( ) function and outside all other functions, then the variable becomes a global variable. If you declare a variable outside all namespaces, then the variable is now present in the global namespace. Check out the example below: #include int life=0; //life in the global namespace namespace bact { int life; //life in bact namespace class bacteria { private: int step; }; } int main( ) { bact::life=1; ::life=0; std::cout<<"\nThe bacteria has a life : "<
Namespaces & Newheaders
455 The output would be: The bacteria has a life : 1 The organism has a life : 0 When we say: ::life it refers to the ‘life’ present in the global namespace. Beware: You cannot create namespaces within functions. Namespaces can be nested (one within the other).
Namespaces & Newheaders
456
Advanced C++ - Part IIIa (Creating Libraries)
Creating Libraries We still haven’t achieved implementation hiding in the real sense. Any user of your class can open the *.cpp files and modify your function definitions. Generally, the creator of a class wouldn’t want to let a user do this. Also, the user will now have to copy the car.cpp file into his project directory and compile it before he can run his program. Wouldn’t it be simpler if the user could just put a #include statement in his project without having to bother about someone else’s *.cpp files? This is where a library comes into the picture. The class creator can create a library file as well. When others want to use his class, they have to just include his header file and also ask the linker to search for the class implementation in the new library. A library is a collection of object files (i.e. the output of the compiler stage). Let’s create a library to make the concept clear. (We’ll look at the process to be followed in VC++. The appendix section will deal with C++ on Unix/Linux). First we’ll need to create a library. Go to File and choose New. In the options tab choose Projects.
Name the project as carlib and click the OK button. The next dialog box will ask if you want to include MFCs or other files. Don’t choose any of these (for this library we don’t require anything extra). Add a new header file (carlib.h) to this project. Include the following code:
Creating Libraries
457 //carlib.h #include using namespace std; class car { private: char color[20]; int speed; public: car( ); void input( ); void display( ); ~car( ); }; Include a source file (named car.cpp) in this project. The code for this will contain the implementation details of the class ‘car’. #include "carlib.h" car::car( ) { cout<>color; cout<>speed; } void car::display( ) { cout<
Creating Libraries
458 If someone wants to use your library then all you need to do is supply them with the carlib.lib file and carlib.h header file. The *.lib file contains the object code while the carlib.h file just contains the interface to your class. Anyone who uses your class can take a look at the *.h file to know what functions are available for them to use (of course for a more complex class you would have to provide some documentation in the header file). Let’s use this library in a program to check if it works. Create a new application project and include a C++ source file into the project. Name it as mycar.cpp and type the following code: //mycar.cpp #include "carlib.h" int main( ) { car mine; mine.input( ); mine.display( ); return 0; } Now compile mycar.cpp and voila! You should have an error saying: c:\tutorial\mycar\mycar.cpp(2) : fatal error C1083: Cannot open include file: 'carlib.h': No such file or directory The error message is self-explanatory. We haven’t told the compiler as to where the file carlib.h is present. You have 2 options at this point: 1. Copy that file into your current project directory and include it in this project. 2. Your compiler will search for include files in particular directories. You can add the name of the directory where your *.h file is present to this list (this is usually present in the Project Settings option). Since we are planning to distribute our library to a 3rd person, we’ll take the first route. Copy the file carlib.h into your current project directory. Then go to VC++ and make sure that you include this file into your current project (otherwise VC++ will not consider this file as part of the project even though it is physically present in the same directory). For doing this go to PROJECT -> Add to Project -> Files and choose the header file. Now compile mycar.cpp. You shouldn’t have any errors this time. The next step is to build the exe file mycar.exe. Go to ‘BUILD -> Build mycar.exe’ option. Well, you should be getting a bunch of errors this time (which might appear weird). The following messages appeared in my computer: Linking...
Creating Libraries
459 mycar.obj : error LNK2001: unresolved external symbol "public: __thiscall car::~car(void)" (??1car@@QAE@XZ) mycar.obj : error LNK2001: unresolved external symbol "public: void __thiscall car::display(void)" (?display@car@@QAEXXZ) mycar.obj : error LNK2001: unresolved external symbol "public: void __thiscall car::input(void)" (?input@car@@QAEXXZ) mycar.obj : error LNK2001: unresolved external symbol "public: __thiscall car::car(void)" (??0car@@QAE@XZ) Debug/mycar.exe : fatal error LNK1120: 4 unresolved externals Error executing link.exe. Don’t panic on seeing the strange symbols. From the error messages we can deduce that this is a linker error (compiling went fine but linking failed). Why? If you look at the individual messages you’ll realize the problem. The linker hasn’t been able to resolve references to certain function definitions (car::~car( ), car::display( ) etc.). They are not present in carlib.h, not in mycar.cpp and certainly not in #include. So, should we add car.cpp to this file (but then we are not using the power of libraries). Car.cpp contains the definitions of these functions but carlib.lib contains the object code for all these functions. The linker only requires the object code for the functions and hence we should include the library carlib.lib in our current project. Then the linker will search carlib.lib and find the required object code for all the definitions. To do this, there are 2 settings you have to change in your project: 1. Tell VC++ the path where to search for the libraries. 2. Tell VC++ the name of the library. For step 1 go to TOOLS-> OPTIONS. You’ll get a pop-up box with lots of tabs at the top (as shown below):
Creating Libraries
460 We need to tell the path for the Library files. So change the SHOW DIRECTORIES FOR option as shown below:
Now add the pathname where you have the file carlib.lib (click the new icon present in the options dialog box to add a new path).
In my case it is in the current project directory itself. Click OK. Next we need to tell VC++ the name of the library we want to use. Go to PROJECT-> SETTINGS and you’ll see a dialog box as shown below:
Creating Libraries
461
In the object/Library modules text field, enter our library name carlib.lib at the end of the list. This will automatically get added in the Project Options textbox at the bottom. Click OK and now build the exe file. You shouldn’t encounter any errors this time since the linker will be able to obtain the object code for all the function definitions. What have we achieved by using a library? The class user is now totally unaware about the implementation details of your function. Since the library is in object code, no user would try to manipulate the contents of the library file (you could open the library file in Notepad to view the contents which would appear cryptic). The library file would be something like this (the snapshot is only part of the file):
This isn’t something that a user would want to play around with! What we created is called a static library. There are 2 types of libraries: • Static • Dynamic (or shared). Static is simple to create but has a major disadvantage. If many programs make use of static libraries and if all of them are being run at the same time, then each program will have a copy of the library in memory. Shared libraries (or dynamic link libraries) overcome this problem.
Creating Libraries
462
Advanced C++ - Part IIIb (Creating libraries)
The programs that we've seen so far are not wrong but they can't be called as 100% C++ programs either. The reason is because we've been making use of header files (such as iostream.h etc...). Making use of these .h files is more similar to C programming. Of course it isn't wrong to use them in C++ but there is a better method provided in C++. This method makes use of namespaces. Instead of # include we shall use the following two lines: #include using namespace std; Firstly take note of the fact that there is no ‘.h’ in the include statement. This is the format of the new style headers used in C++. They do not use .h files. Old C++ compilers may not support namespaces. In case you are using such a compiler then you have to stick to the old method of #include with the ‘.h’ extension. But all the latest C++ compilers will support namespaces and they will also support the old style headers. When C++ was created it used header files like those used in C programming. But then changes were made. Though it still supports the old header files, they are not recommended. You know that iostream.h is a file. C++ created a new style of its own. In this method we do not specify filenames. Instead it is a method that makes sure that appropriate definitions required by the C++ library have been declared. The new style headers are not filenames and hence the .h extension is left out.
Multiple File C++ Programming So far we’ve been writing programs within one source file. The entire program (including class declarations, functions etc.) was written in one single *.cpp file. This file was compiled and run to check the output. In this section I would like to explain the process by which an executable file is created from our C++ source file. Let’s assume that we’ve written a source file called test.cpp. What are the steps involved after that? Broadly you can say that four people are involved: • • • •
Preprocessor Compiler Assembler Linker
As mentioned earlier, the preprocessor will search for the preprocessor directives. It will convert these directives into C++ coding. In other words, the include header files will be
Creating Libraries
463 inserted into your source file. If you have used macros, they will be substituted in the corresponding places. Next, the compiler takes over. The compiler will read through the whole program, and in case it finds any problems while compiling, it will display compile time errors. Otherwise it will convert your source code into assembly language code. The assembler will finally convert your assembly language code into machine language (i.e. into language that your computer can understand). What is produced by the assembler is known as an object module (or a single translation unit because the source code has been translated into object code). Is the process over? Even in a most basic C++ program we make use of other library functions and objects. Thus there are a few other object modules that are related to your C++ program. Only when these modules are linked with your object module, can the program work. The linker takes care of this aspect. All the different units are linked together to form the final executable program. So, nowadays when we say a compiler, for example the Turbo C++ compiler, it usually contains all the required parts (a compiler, preprocessor, linker and assembler). Thus you needn’t worry about these individual parts of the compiler. But when you do create a multiple file C++ program, you should beware that just because all your individual files compile successfully doesn’t mean that they will be linked properly. You can get linker errors (possibly you forgot to include some header file or something like that). The question arises, why should we go in for multiple file programming? When a program is very complex it is always advisable to break up the program into smaller units. In C programming, we would define the functions in a separate header file and then include this header file in our source file. In C++ we are bothered with classes. So, instead of functions, we will define classes separately. What we will do is to define the class in a header file. We will not define the member functions in this file (we will only declare all the member functions). The definitions will be done in a *.cpp file using the scope resolution operator. Now when you want to write a program using that class, you will simply include the header file in your source code. What about compiling? Header files cannot be compiled. But now you have two *.cpp files (one with the member function definitions and the other is your source file). We will compile both files separately and then when you build your project the linker will link up both the object modules together. Might seem a bit confusing but an example should make things easier to understand. One more point to remember: Member functions are defined in a separate *.cpp file. This is usually referred to as the implementation. The idea is to keep the implementation of the class separate from the declarations.
Creating Libraries
464 Creating a Multiple File Program The example demonstrated below has been tried in Turbo C++ as well as in VC++. Other compilers will also have similar options available. First of all we shall take a look at how to create a multiple file program in Visual C++ (later we’ll deal with Turbo C++). Click on File and then choose New. You’ll have a lot of options like (File, Project, Workspace etc…). Choose the Projects tab and in that you’ll have numerous choices available. Choose Win 32 Console Application from the list and give a suitable name. Let us assume that the name is given as carcheck. Choose the option which says "Create New Workspace" and then click on Finish/Ok. If you are using Visual C++ 6.0, another menu will pop up asking what type of application you want to create (whether you want an empty project or whether you want a "Hello World" application etc.). Choose "Empty Project". The new workspace will be created with the name as "carcheck". You will have two tabs with the labels "Class View" and "File View". This will give you an idea as to what are the classes and files present under your current project. Click on the FileView tab and you’ll see that there are no files there. Now again go to "File" and choose "New". This time create a new C++ header file. Give a suitable name (here we have named it as ‘car’). Now you’ll have a new header file called car.h within the "FileView". This file will be empty and we shall now declare our class within this file. //car.h #include using namespace std; class car { private: char color[20]; int speed; public: car( ); void input( ); void display( ); ~car( ); }; As you can see, all the functions have only been declared in the file car.h (we have not defined them here). Next, go to "File", "New" and create a new C++ source file. Name this file as car.cpp. Open the file in your editor window and type in the following:
Creating Libraries
465 //car.cpp – we will define the various functions in this file. #include "car.h" car::car( ) { cout<>color; cout<>speed; } void car::display( ) { cout< using namespace std; was used in the car.h file. car.h is a header file created by us. So, it is present in the directory where we are doing our project. But is present in the compiler’s include directory. Hence for car.h we include it using the double quotation and for iostream we use the angle brackets.
Creating Libraries
466 //main.cpp #include "car.h" int main( ) { car mine; mine.input( ); mine.display( ); return 0; } Now, compile this file (main.cpp). After compilation, you can go to "Build" and choose "Build carcheck.exe". It is now that the linker will work and link up everything. After this, go back to "Build" and choose "Execute carcheck.exe". The output will be: New car created. Enter the color : red Enter the top speed : 300 The color is : red The top speed is : 300 Your car is destroyed. Remember: The header car.h already include , so there is no need for you to include again in the file main.cpp. Beware: When you are linking many object modules together, only one of them should have the main ( ) function. In the above case we created two object files from car.cpp and main.cpp but only main.cpp had the main ( ) function. By the way, in Visual C++ workspace files are stored with the extension *.dsw. Preventing multiple inclusions of Header files in a program: Usually when creating header files, the programmer will provide a provision so that the header file is not included in the source code more than once. To do this, we can make use of the preprocessor directives. The modified ‘car.h’ file will be: #ifndef CAR_H #define CAR_H #include using namespace std; class car { private:
Creating Libraries
467 char color[20]; int speed; public: car( ); void input( ); void display( ); ~car( ); }; #endif The header file be included within the source code only if CAR_H has not been defined. This prevents multiple inclusions of the ‘car.h’ in a program. CAR_H actually stands for ‘car.h’ (you cannot use the dot in preprocessor directives and thus have to use an underscore). You can use car_h or CAR_H (programmers usually prefer to use the latter). If you are using Turbo C++ In Turbo C++, the above process is much easier. First of all create 3 files as done previously (car.h, car.cpp and main.cpp). Next you have an option that says "Project". Click on that and choose "Open Project". Since you still haven’t created a project, just type in some name for the project (ex: carcheck.prj) and click open. (In Turbo C++, project files have a *.prj extension). You’ll see a new project window at the bottom of the screen. Again click on "Projects" and choose "Add new item". A list of all the files will pop up on your screen. Locate the two files (car.cpp and main.cpp) and click on "Add". After adding both the files, choose "Done". Now both your *.cpp files have been added to the project. Next, you have to compile both these files. Do the compiling as you normally would. Open the file car.cpp and then click on "Compile". Then open main.cpp and compile this file also. Now if you look at the project window at the bottom of the screen you’ll notice that there are two items listed: car.cpp main.cpp Once you compile the two files, the project window will also display the number of lines in each file, whether they have any data etc. Next choose "Build All" from the "Compile" option menu. Once successfully done, you can choose "Run" and your program will execute. To summarize the concept of multiple file programming:
Creating Libraries
468 All C++ classes can be divided into two main parts: Class Declaration: This is also called as the interface of the class. It just contains information about the data members and the various functions that are available in the class. This is usually in the header file (in our case it was in car.h). Class Implementation: This defines all the member functions that were declared in your header file (in our case it is car.cpp). This will help you to hide the implementation from the user. Even though you may change your implementation, the class interface will remain the same.
The keyword ‘extern’ The ‘extern’ keyword was briefly mentioned in the chapter on data types. This is basically another storage class specifier (the others are static, auto and register) which you can use with your data types when you declare them. When you develop a project consisting of many separate files there is a good possibility that you will be using global variables. The question arises, "Should we declare and define global variables in each of the files?" (because each *.cpp file will be compiled separately). This is not allowed in C++ and it is bad programming practice to declare and define the same global variable in more than one place. Hence in C++ we have ‘extern’. This will only declare a variable, it will not define it. Only when we define a variable will the compiler allot memory for the variable. Usually declaration and definition of a variable occur at the same time. But when we use the extern specifier (which stands for external), we tell the compiler that this is only a declaration (the definition of the variable is done in some other file). Thus usually all global variables are defined in one separate file and in all the other places we only declare the variable. The syntax is: extern data-type variable-name; The linker will take up the responsibility of resolving all the references made to the external variable. Another use of Extern: By default, C++ compilers will link all your functions as C++ functions. All C++ compilers make use of name mangling. When the compiler reads through the source code and encounters a function name, it stores the details about the function in a table (called a symbol table). For example if we have a function: void sub (int, int);
Creating Libraries
469 the compiler will store this function in its symbol table. But functions in C++ are name mangled, i.e. the function name stored by the compiler is not the same one as given by us. The compiler may store it as: sub_int_int Thus whenever the compiler encounters a function call to sub ( ) it will retrieve the appropriate definition for the function at compile-time. Name mangling permits C++ to implement function overloading. Let us say we have another function with the same name ‘sub’: void sub (double,double); The compiler will store this as sub_double_double Thus whenever there is a statement calling the function sub ( ), the compiler will check the data types of the arguments and call the correct function. C compilers do not implement name mangling and thus they do not support functionoverloading. Different C++ compilers (supplied by different vendors) may implement name mangling in a different way but the bottom-line is that all C++ compilers implement name mangling. Other languages mostly do not use name mangling (C compilers do not mangle names). Let us suppose that you have written a few functions in C and have compiled them using a C compiler. Now in your C++ program you want to use those C functions. Is it possible? C++ will mangle all the function references and the mangled names will not be the same as the original C function name. Thus the linker won’t be able to match the function call (compiled in C++) to the function definition (compiled in C). To overcome this problem we need to tell the C++ compiler, "Do not mangle this function since it is not a C++ function". The extern keyword can be used for this purpose. The syntax is: extern "name-of-the-language" function Beware: You cannot use this within a function (it wouldn’t make any sense to do so). Always make such declarations global. Example: extern "C" int display ( ); //global declaration
Creating Libraries
Creating Libraries
The C++ compiler now knows that the function display( ) is a C compiled function and so it won’t mangle the function name display( ). Sometimes if you have many functions that you want to link as a different programming function then you can do it as follows: extern "name-of-language" { //function declarations/headers can also be included here }
470
471
Advanced C++ - Part IV The following topics are covered in this section: • • •
Exception Handling Templates Template Classes
Exception handling Older C++ compilers will not support exception handling. This feature is used for dealing with certain specific situations (these situations are not supposed to occur and if you don’t provide some way of dealing with them then your program could simply freeze when the conditions really occur). To implement exception handling, C++ provides us with three keywords: o Try o Catch o Throw How do they work? First you try (or execute) the coding and in case some special situation is encountered, then the program will throw an exception. The exception that is thrown will be caught by the catch part of the program. #include using namespace std; int main( ) { float a,b; try { cout<<"\nEnter a number : "; cin>>a; cout<<"\nEnter the denominator : "; cin>>b; if (b= =0) { throw 0; } cout<<"\nResult of division : "<
Advanced C++ - IV