The Origins of C++
C++ began as an expanded version of C. The C++ extensions were first invented by Bjarne Stroustrup in 1979 at Bell Laboratories in Murray Hill, New Jersey. He initially called the new language "C with Classes." However, in 1983 the name was changed to C++. Although C was one of the most liked and widely used professional programming languages in the world, the invention of C++ was necessitated by one major programming factor: increasing complexity. Over the years, computer programs have become larger and more complex. Even though C is an excellent programming language, it has its limits. In C, once a program exceeds from 25,000 to 100,000 lines of code, it becomes so complex that it is difficult to grasp as a totality. The purpose of C++ is to allow this barrier to be broken. The essence of C++ is to allow the programmer to comprehend and manage larger, more complex programs.What Is Object-Oriented Programming?
Since object-oriented programming (OOP) drove the creation of C++, it is necessary to understand its foundational principles. OOP is a powerful way to approach the job of programming. Programming methodologies have changed dramatically since the invention of the computer, primarily to accommodate the increasing complexity of programs. For example, when computers were first invented, programming was done by toggling in the binary machine instructions using the computer's front panel. As long as programs were just a few hundred instructions long, this approach worked. As programs grew, assembly language was invented so that a programmer could deal with larger, increasingly complex programs, using symbolic representations of the machine instructions. As programs continued to grow, high-level languages were introduced that gave the programmer more tools with which to handle complexity. The first widespread language was, of course, FORTRAN. Although FORTRAN was a very impressive first step, it is hardly a language that encourages clear, easy-tounderstand programs.Object-oriented programming took the best ideas of structured programming and combined them with several new concepts. The result was a different way of organizing a program. In the most general sense, a program can be organized in one of two ways: around its code (what is happening) or around its data (who is being affected). Using only structured programming techniques, programs are typically organized around code.
This approach can be thought of as "code acting on data." For example, a program written in a structured language such as C is defined by its functions, any of which may operate on any type of data used by the program. Object-oriented programs work the other way around. They are organized around data, with the key principle being "data controlling access to code." In an object-oriented language, you define the data and the routines that are permitted to act on that data. Thus, a data type defines precisely what sort of operations can be applied to that data.
Encapsulation
Encapsulation is the mechanism that binds together code and the data it manipulates, and keeps both safe from outside interference and misuse. In an object-oriented language, code and data may be combined in such a way that a self-contained "black box" is created. When code and data are linked together in this fashion, an object is created. In other words, an object is the device that supports encapsulation. Within an object, code, data, or both may be private to that object or public. Private code or data is known to and accessible only by another part of the object. That is, private code or data may not be accessed by a piece of the program that exists outside the object. When code or data is public, other parts of your program may access it even though it is defined within an object. Typically, the public parts of an object are used to provide a controlled interface to the private elements of the object.Polymorphism Object-oriented programming languages support polymorphism, which is characterized by the phrase "one interface, multiple methods." In simple terms, polymorphism is the attribute that allows one interface to control access to a general class of actions
The specific action selected is determined by the exact nature of the situation. A real-world example of polymorphism is a thermostat. No matter what type of furnace your house has (gas, oil, electric, etc.), the thermostat works the same way. In this case, the thermostat (which is the interface) is the same no matter what type of furnace (method) you have. For example, if you want a 70-degree temperature, you set the thermostat to 70 degrees. It doesn't matter what type of furnace actually provides the heat.
This same principle can also apply to programming. For example, you might have a program that defines three different types of stacks. One stack is used for integer values, one for character values, and one for floating-point values. Because of polymorphism, you can define one set of names, push() and pop(), that can be used for all three stacks. In your program you will create three specific versions of these functions, one for each type of stack, but names of the functions will be the same. The compiler will automatically select the right function based upon the data being stored. Thus, the interface to a stack—the functions push() and pop()—are the same no matter which type of stack is being used. The individual versions of these functions define the specific implementations (methods) for each type of data.
Inheritance
Inheritance is the process by which one object can acquire the properties of another object. This is important because it supports the concept of classification. If you think about it, most knowledge is made manageable by hierarchical classifications. For example, a Red Delicious apple is part of the classification apple, which in turn is part of the fruit class, which is under the larger class food. Without the use of classifications, each object would have to define explicitly all of its characteristics. However, through the use of classifications, an object need only define those qualities that make it unique within its class. It is the inheritance mechanism that makes it possible for one object to be a specific instance of a more general case. As you will see, inheritance is an important aspect of object-oriented programming.A Sample C++
Program Let's start with the short sample C++ program shown here.
#include <iostream> using namespace std;
int main()
{
int i;
cout << "This is output.\n"; // this is a single line comment
/* you can still use C style comments */
// input a number using >>
cout << "Enter a number: ";
cin >> i;
// now, output a number using <<
cout << i << " squared is " << i*i << "\n";
system("pause"); // to stop screan
return 0;
}
As you can see, this program looks much different from the C subset programs found in Part One. A line-by-line commentary will be useful. To begin, the header <iostream> is included. This header supports C++-style I/O operations. (<iostream> is to C++ what stdio.h is to C.) Notice one other thing: there is no .h extension to the name iostream. The reason is that <iostream> is one of the new-style headers defined by Standard C++. New-style headers do not use the .h extension. The next line in the program is
using namespace std;
This tells the compiler to use the std namespace. Namespaces are a recent addition to C++. A namespace creates a declarative region in which various program elements can be placed. Namespaces help in the organization of large programs. The using statement informs the compiler that you want to use the std namespace. This is the namespace in which the entire Standard C++ library is declared. By using the std namespace you simplify access to the standard library. The programs in Part One, which use only the C subset, don't need a namespace statement because the C library functions are also available in the default, global namespace.
int main()
Notice that the parameter list in main() is empty. In C++, this indicates that main() has no parameters. This differs from C. In C, a function that has no parameters must use void in its parameter list, as shown here:
int main(void)
This was the way main() was declared in the programs in Part One. However, in C++, the use of void is redundant and unnecessary. As a general rule, in C++ when a function takes no parameters, its parameter list is simply empty; the use of void is not required
The next line contains two C++ features.
cout << "This is output.\n"; // this is a single line comment
First, the statement
cout << "This is output.\n";.
causes This is output. to be displayed on the screen, followed by a carriage returnlinefeed combination. In C++, the << has an expanded role. It is still the left shift operator, but when it is used as shown in this example, it is also an output operator. The word cout is an identifier that is linked to the screen. (Actually, like C, C++ supports I/O redirection, but for the sake of discussion, assume that cout refers to the screen.) You can use cout and the << to output any of the built-in data types, as well as strings of character
Next, the program prompts the user for a number. The number is read from the keyboard with this statement:
cin >> i;
In C++, the >> operator still retains its right shift meaning. However, when used as shown, it also is C++'s input operator. This statement causes i to be given a value read from the keyboard. The identifier cin refers to the standard input device, which is usually the keyboard. In general, you can use cin >> to input a variable of any of the basic data types plus strings.
Although it is not illustrated by the example, you are free to use any of the C-based input functions, such as scanf(), instead of using >>. However, as with cout, most programmers feel that cin >> is more in the spirit of C++. Another interesting line in the program is shown here:
cout << i << "squared is " << i*i << "\n";
Assuming that i has the value 10, this statement causes the phrase 10 squared is 100 to be displayed, followed by a carriage return-linefeed. As this line illustrates, you can run together several << output operations. The program ends with this statement:
return 0;
This causes zero to be returned to the calling process (which is usually the operating system). This works the same in C++ as it does in C. Returning zero indicates that the program terminated normally. Abnormal program termination should be signaled by returning a nonzero value. You may also use the values EXIT_SUCCESS and EXIT_ FAILURE if you like.
The New C++
Headers As you know, when you use a library function in a program, you must include its header file. This is done using the #include statement. For example, in C, to include the header file for the I/O functions, you include stdio.h with a statement like this:
#include <stdio.h>
Here, stdio.h is the name of the file used by the I/O functions, and the preceding statement causes that file to be included in your program. The key point is that this #include statement includes a file. When C++ was first invented and for several years after that, it used the same style of headers as did C. That is, it used header files. In fact, Standard C++ still supports C-style headers for header files that you create and for backward compatibility. However, Standard C++ created a new kind of header that is used by the Standard C++ library. The new-style headers do not specify filenames. Instead, they simply specify standard identifiers that may be mapped to files by the compiler, although they need not be. The new-style C++ headers are an abstraction that simply guarantee that the appropriate prototypes and definitions required by the C++ library have been declared. Since the new-style headers are not filenames, they do not have a .h extension. They consist solely of the header name contained between angle brackets. For example, here are some of the new-style headers supported by Standard C++.
<iostream> <fstream> <vector> <string>
The new-style headers are included using the #include statement. The only difference is that the new-style headers do not necessarily represent filenames. Because C++ includes the entire C function library, it still supports the standard C-style header files associated with that library. That is, header files such as stdio.h or ctype.h are still available. However, Standard C++ also defines new-style headers that you can use in place of these header files. The C++ versions of the C standard headers simply add a "c" prefix to the filename and drop the .h. For example, the C++ new-style header for math.h is <cmath>. The one for string.h is <cstring>. Although it is currently permissible to include a C-style header file when using C library functions, this approach is deprecated by Standard C++ (that is, it is not recommended). For this reason, from this point forward, If your compiler does not support new-style headers for the C function library, then simply substitute the old-style, C-like headers. Since the new-style header is a recent addition to C++, you will still find many, many older programs that don't use it. These programs employ C-style headers, in which a filename is specified. As the old-style skeletal program shows, the traditional way to include the I/O header is as shown here.
#include <iostream.h>
This causes the file iostream.h to be included in your program. In general, an old-style header file will use the same name as its corresponding new-style header with a .h appended. As of this writing, all C++ compilers support the old-style headers. However, the old-style headers have been declared obsolete and their use in new programs is not recommended.
Namespaces
When you include a new-style header in your program, the contents of that header are contained in the std namespace. A namespace is simply a declarative region. The purpose of a namespace is to localize the names of identifiers to avoid name collisions. Elements declared in one namespace are separate from elements declared in another. Originally, the names of the C++ library functions, etc., were simply put into the global namespace (as they are in C). However, with the advent of the new-style headers, the contents of these headers were placed in the std namespace.
using namespace std;
brings the std namespace into visibility (i.e., it puts std into the global namespace). After this statement has been compiled, there is no difference between working with an old-style header and a new-style one. One other point: for the sake of compatibility, when a C++ program includes a C header, such as stdio.h, its contents are put into the global namespace. This allows a C++ compiler to compile C-subset programs.
Introducing C++ Classes
This section introduces C++'s most important feature: the class. In C++, to create an object, you first must define its general form by using the keyword class.Aclass is similar syntactically to a structure. Here is an example. The following class defines a type called stack, which will be used to create a stack:
#define SIZE 100
// This creates the class stack.
class stack {
int stck[SIZE]; int tos; public:
void init();
void push(int i); int pop();
};
A class may contain private as well as public parts. By default, all items defined in a class are private. For example, the variables stck and tos are private. This means that they cannot be accessed by any function that is not a member of the class. This is one way that encapsulation is achieved—access to certain items of data may be tightly controlled by keeping them private. Although it is not shown in this example, you can also define private functions, which then may be called only by other members of the class. To make parts of a class public (that is, accessible to other parts of your program), you must declare them after the public keyword. All variables or functions defined after public can be accessed by all other functions in the program.
Essentially, the rest of your program accesses an object through its public functions. Although you can have public variables, good practice dictates that you should try to limit their use. Instead, you should make all data private and control access to it through public functions. One other point: Notice that the public keyword is followed by a colon. The functions init( ), push( ), and pop() are called member functions because they are part of the class stack. The variables stck and tos are called member variables (or data members). Remember, an object forms a bond between code and data. Only member functions have access to the private members of their class. Thus, only init( ), push( ), and pop() may access stck and tos. Once you have defined a class, you can create an object of that type by using the class name. In essence, the class name becomes a new data type specifier. For example, this creates an object called mystack of type stack:
stack mystack;
When you declare an object of a class, you are creating an instance of that class. In this case, mystack is an instance of stack. You may also create objects when the class is defined by putting their names after the closing curly brace, in exactly the same way as you would with a structure. To review: In C++, class creates a new data type that may be used to create objects of that type. Therefore, an object is an instance of a class in just the same way that some other variable is an instance of the int data type, for example. Put differently, a class is a logical abstraction, while an object is real. (That is, an object exists inside the memory of the computer.)
The general form of a simple class declaration is
class class-name {
private data and functions
public: public data and functions
} object name list;
Of course, the object name list may be empty. Inside the declaration of stack, member functions were identified using their prototypes. In C++, all functions must be prototyped. Prototypes are not optional. The prototype for a member function within a class definition serves as that function's prototype in general. When it comes time to actually code a function that is the member of a class, you must tell the compiler which class the function belongs to by qualifying its name with the name of the class of which it is a member. For example, here is one way to code the push() function:
void stack::push(int i)
{
if(tos==SIZE) { cout << "Stack is full.\n";
return;
} stck[tos] = i; tos++;
}
The :: is called the scope resolution operator. Essentially, it tells the compiler that this version of push() belongs to the stack class or, put differently, that this push() is in stack's scope. In C++, several different classes can use the same function name. The compiler knows which function belongs to which class because of the scope resolution operator. When you refer to a member of a class from a piece of code that is not part of the class, you must always do so in conjunction with an object of that class. To do so, use the object's name, followed by the dot operator, followed by the name of the member. This rule applies whether you are accessing a data member or a function member. For example, this calls init() for object stack1.
stack stack1, stack2;
stack1.init();This fragment creates two objects, stack1 and stack2, and initializes stack1. Understand that stack1 and stack2 are two separate objects. This means, for example, that initializing stack1 does not cause stack2 to be initialized as well. The only relationship stack1 has with stack2 is that they are objects of the same type.
Within a class, one member function can call another member function or refer to a data member directly, without using the dot operator. It is only when a member is referred to by code that does not belong to the class that the object name and the dot operator must be used.
The program shown here puts together all the pieces and missing details and illustrates the stack class:
#include <iostream> using namespace std;
#define SIZE 100
// This creates the class stack. class stack {
int stck[SIZE]; int tos;
public: void init(); void push(int i);
int pop();
};
void stack::init() {
tos = 0;
}
void stack::push(int i)
{
if(tos==SIZE) {
cout << "Stack is full.\n"; return;
}
stck[tos] = i; tos++;
}
int stack::pop() {
if(tos==0) { cout << "Stack underflow.\n"; return 0;
} tos--;
class class-name {
private data and functions
public: public data and functions
} object name list;
Of course, the object name list may be empty. Inside the declaration of stack, member functions were identified using their prototypes. In C++, all functions must be prototyped. Prototypes are not optional. The prototype for a member function within a class definition serves as that function's prototype in general. When it comes time to actually code a function that is the member of a class, you must tell the compiler which class the function belongs to by qualifying its name with the name of the class of which it is a member. For example, here is one way to code the push() function:
void stack::push(int i)
{
if(tos==SIZE) { cout << "Stack is full.\n";
return;
} stck[tos] = i; tos++;
}
The :: is called the scope resolution operator. Essentially, it tells the compiler that this version of push() belongs to the stack class or, put differently, that this push() is in stack's scope. In C++, several different classes can use the same function name. The compiler knows which function belongs to which class because of the scope resolution operator. When you refer to a member of a class from a piece of code that is not part of the class, you must always do so in conjunction with an object of that class. To do so, use the object's name, followed by the dot operator, followed by the name of the member. This rule applies whether you are accessing a data member or a function member. For example, this calls init() for object stack1.
stack stack1, stack2;
stack1.init();This fragment creates two objects, stack1 and stack2, and initializes stack1. Understand that stack1 and stack2 are two separate objects. This means, for example, that initializing stack1 does not cause stack2 to be initialized as well. The only relationship stack1 has with stack2 is that they are objects of the same type.
Within a class, one member function can call another member function or refer to a data member directly, without using the dot operator. It is only when a member is referred to by code that does not belong to the class that the object name and the dot operator must be used.
The program shown here puts together all the pieces and missing details and illustrates the stack class:
#include <iostream> using namespace std;
#define SIZE 100
// This creates the class stack. class stack {
int stck[SIZE]; int tos;
public: void init(); void push(int i);
int pop();
};
void stack::init() {
tos = 0;
}
void stack::push(int i)
{
if(tos==SIZE) {
cout << "Stack is full.\n"; return;
}
stck[tos] = i; tos++;
}
int stack::pop() {
if(tos==0) { cout << "Stack underflow.\n"; return 0;
} tos--;
return stck[tos];
}
int main()
{
stack stack1, stack2; // create two stack objects
stack1.init();
stack2.init();
stack1.push(1); stack2.push(2);
stack1.push(3); stack2.push(4);
cout << stack1.pop() << " "; cout << stack1.pop() << " "; cout << stack2.pop() << " ";
cout << stack2.pop() << "\n";
return 0;
}
The output from this program is shown here.
3 1 4 2
One last point: Recall that the private members of an object are accessible only by functions that are members of that object. For example, a statement like
stack1.tos = 0; // Error, tos is private.
could not be in the main() function of the previous program because tos is private.
Function Overloading
One way that C++ achieves polymorphism is through the use of function overloading. In C++, two or more functions can share the same name as long as their parameter declarations are different. In this situation, the functions that share the same name are said to be overloaded, and the process is referred to as function overloading.
To see why function overloading is important, first consider three functions defined by the C subset: abs( ), labs( ), and fabs(). The abs() function returns the absolute value of an integer, labs() returns the absolute value of a long, and fabs() returns the absolute value of a double. Although these functions perform almost identical actions, in C three slightly different names must be used to represent these essentially similar tasks. This makes the situation more complex, conceptually, than it actually is. Even though the underlying concept of each function is the same, the programmer has to remember three things, not just one. However, in C++, you can use just one name for all three functions, as this program illustrates:
#include <iostream>
using namespace std;
// abs is overloaded three ways
int abs(int i); double abs(double d); long abs(long l);
int main()
{
cout << abs(-10) << "\n";
cout << abs(-11.0) << "\n";
cout << abs(-9L) << "\n";
return 0;
}
int abs(int i) {
cout << "Using integer abs()\n";
return i<0 ? -i : i;
}
double abs(double d) {
cout << "Using double abs()\n";
return d<0.0 ? -d : d;
}
long abs(long l)
{
cout << "Using long abs()\n";
return l<0 ? -l : l;
}
The output from this program is shown here.
Using integer abs()
10
Using double abs()
11
Using long abs()
9
In general, to overload a function, simply declare different versions of it. The compiler takes care of the rest. You must observe one important restriction when overloading a function: the type and/or number of the parameters of each overloaded function must differ. It is not sufficient for two functions to differ only in their return types. They must differ in the types or number of their parameters. (Return types do not provide sufficient information in all cases for the compiler to decide which function to use.) Of course, overloaded functions may differ in their return types, too. Here is another example that uses overloaded functions:
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
void stradd(char *s1, char *s2);
void stradd(char *s1, int i);
int main()
{
char str[80];
strcpy(str, "Hello ");
stradd(str, "there");
cout << str << "\n";
stradd(str, 100);
cout << str << "\n";
return 0;
}
// concatenate two strings void stradd(char *s1, char *s2)
{
strcat(s1, s2);
}
// concatenate a string with a "stringized" integer
void stradd(char *s1, int i) {
char temp[80];
sprintf(temp, "%d", i); strcat(s1, temp);
}
In this program, the function stradd() is overloaded. One version concatenates two strings (just like strcat() does). The other version "stringizes" an integer and then appends that to a string. Here, overloading
Operator Overloading
Polymorphism is also achieved in C++ through operator overloading. As you know, in C++, it is possible to use the << and >> operators to perform console I/O operations. They can perform these extra operations because in the <iostream> header, these operators are overloaded. When an operator is overloaded, it takes on an additional meaning relative to a certain class. However, it still retains all of its old meanings.
Inheritance
inheritance is one of the major traits of an object oriented programming language.In C++,inheritanceis supported by allowing one class to in corporate another class into its declaration. Inheritance allows a hierarchy of classes to be built, moving from most general to most specific.The process involves first defining a base class, which defines those qualities common to all objects to be derived from the base. The base class represents the most general description.The classes derived from the base are usually referred to as derived classes. A derived class includes all features of the generic base class and then adds qualities specific to the derived class. for ex.
class building {
int rooms; int floors;
int area; public:
void set_rooms(int num);
int get_rooms();
void set_floors(int num);
int get_floors();
void set_area(int num); int get_area();
};
Because (for the sake of this example) all buildings have three common features—one or more rooms, one or more floors, and a total area—the building class embodies these components into its declaration. The member functions beginning with set set the values of the private data. The functions starting with get return those values.
Constructors and Destructors
It is very common for some part of an object to require initialization before it can be used. For example, think back to the stack class developed earlier in this chapter. Before the stack could be used, tos had to be set to zero. This was performed by using the function init(). Because the requirement for initialization is so common, C++ allows objects to initialize themselves when they are created. This automatic initialization is performed through the use of a constructor function.
A constructor function is a special function that is a member of a class and has the same name as that class. For example, here is how the stack class looks when converted to use a constructor function for initialization:
class stack { int stck[SIZE];
int tos; public:
stack(); // constructor
void push(int i);
int pop();
};
Notice that the constructor stack() has no return type specified. In C++, constructor functions cannot return values and, thus, have no return type. The stack() function is coded like this:
// stack's constructor function
stack::stack() {
tos = 0; cout << "Stack Initialized\n";
}
Keep in mind that the message Stack Initialized is output as a way to illustrate the constructor. In actual practice, most constructor functions will not output or input anything. They will simply perform various initializations.
The C++ Keywords
There are 63 keywords currently defined for Standard C++. These are shown in Table 11-1. Together with the formal C++ syntax, they form the C++ programming language. Also, early versions of C++ defined the overload keyword, but it is obsolete. Keep in mind that C++ is a case-sensitive language and it requires that all keywords be in lowercase.
Comments
Post a Comment