Unit 2
Classes, Objects and Methods
C++ is an object-oriented programming language.
Everything in C++ is associated with classes and objects, along with its attributes and methods. For example: in real life, a car is an object. The car has attributes, such as weight and colour, and methods, such as drive and brake.
Attributes and methods are basically variables and functions that belongs to the class. These are often referred to as "class members".
A class is a user-defined data type that we can use in our program, and it works as an object constructor, or a "blueprint" for creating objects.
Create a Class
To create a class, use the class keyword:
Example
Create a class called "MyClass":
Class MyClass { // The class
public: // Access specifier
int myNum; // Attribute (int variable)
string myString; // Attribute (string variable)
};
Example explained
- The class keyword is used to create a class called MyClass.
- The public keyword is an access specifier, which specifies that members (attributes and methods) of the class are accessible from outside the class. You will learn more about access specifiers later.
- Inside the class, there is an integer variable myNum and a string variable myString. When variables are declared within a class, they are called attributes.
- At last, end the class definition with a semicolon;.
Create an Object
In C++, an object is created from a class. We have already created the class named MyClass, so now we can use this to create objects.
To create an object of MyClass, specify the class name, followed by the object name.
To access the class attributes (myNum and myString), use the dot syntax (.) on the object:
Example
Create an object called "myObj" and access the attributes:
Class MyClass { // The class
public: // Access specifier
int myNum; // Attribute (int variable)
string myString; // Attribute (string variable)
};
int main() {
MyClass myObj; // Create an object of MyClass
// Access attributes and set values
myObj.myNum = 15;
myObj.myString = "Some text";
// Print attribute values
cout << myObj.myNum <<"\n";
cout << myObj.myString;
return0;
}
Data hiding is one of the important features of Object Oriented Programming which allows preventing the functions of a program to access directly the internal representation of a class type. The access restriction to the class members is specified by the labeled public, private, and protected sections within the class body. The keywords public, private, and protected are called access specifiers.
A class can have multiple public, protected, or private labeled sections. Each section remains in effect until either another section label or the closing right brace of the class body is seen. The default access for members and classes is private.
Class Base {
Public:
// public members go here
Protected:
// protected members go here
Private:
// private members go here
};
The public Members
A public member is accessible from anywhere outside the class but within a program. You can set and get the value of public variables without any member function as shown in the following example −
#include <iostream>
Using namespace std;
Class Line {
Public:
Double length;
Void setLength( double len );
Double getLength( void );
};
// Member functions definitions
Double Line::getLength(void) {
Return length ;
}
Void Line::setLength( double len) {
Length = len;
}
// Main function for the program
Int main() {
Line line;
// set line length
Line.setLength(6.0);
Cout<< "Length of line : " << line.getLength() <<endl;
// set line length without member function
Line.length = 10.0; // OK: because length is public
Cout<< "Length of line : " << line.length <<endl;
Return 0;
}
When the above code is compiled and executed, it produces the following result −
Length of line : 6
Length of line : 10
The private Members
A private member variable or function cannot be accessed, or even viewed from outside the class. Only the class and friend functions can access private members.
By default all the members of a class would be private, for example in the following class width is a private member, which means until you label a member, it will be assumed a private member −
Class Box {
Double width;
Public:
Double length;
Void setWidth( double wid );
Double getWidth( void );
};
Practically, we define data in private section and related functions in public section so that they can be called from outside of the class as shown in the following program.
#include <iostream>
Using namespace std;
Class Box {
Public:
Double length;
Void setWidth( double wid );
Double getWidth( void );
Private:
Double width;
};
// Member functions definitions
Double Box::getWidth(void) {
Return width ;
}
Void Box::setWidth( double wid ) {
Width = wid;
}
// Main function for the program
Int main() {
Box box;
// set box length without member function
Box.length = 10.0; // OK: because length is public
Cout<< "Length of box : " << box.length <<endl;
// set box width without member function
// box.width = 10.0; // Error: because width is private
Box.setWidth(10.0); // Use member function to set it.
Cout<< "Width of box : " << box.getWidth() <<endl;
Return 0;
}
When the above code is compiled and executed, it produces the following result −
Length of box : 10
Width of box : 10
The protected Members
A protected member variable or function is very similar to a private member but it provided one additional benefit that they can be accessed in child classes which are called derived classes.
You will learn derived classes and inheritance in next chapter. For now you can check following example where I have derived one child class SmallBox from a parent class Box.
Following example is similar to above example and here width member will be accessible by any member function of its derived class SmallBox.
#include <iostream>
Using namespace std;
Class Box {
Protected:
Double width;
};
Class SmallBox:Box { // SmallBox is the derived class.
Public:
Void setSmallWidth( double wid );
Double getSmallWidth( void );
};
// Member functions of child class
Double SmallBox::getSmallWidth(void) {
Return width ;
}
Void SmallBox::setSmallWidth( double wid ) {
Width = wid;
}
// Main function for the program
Int main() {
SmallBox box;
// set box width using member function
Box.setSmallWidth(5.0);
Cout<< "Width of box : "<< box.getSmallWidth() << endl;
Return 0;
}
When the above code is compiled and executed, it produces the following result −
Width of box : 5
Encapsulation
All C++ programs are composed of the following two fundamental elements −
- Program statements (code)−This is the part of a program that performs actions and they are called functions.
- Program data−The data is the information of the program which gets affected by the program functions.
Encapsulation is an Object Oriented Programming concept that binds together the data and functions that manipulate the data, and that keeps both safe from outside interference and misuse. Data encapsulation led to the important OOP concept of data hiding.
Data encapsulation is a mechanism of bundling the data, and the functions that use them and data abstraction is a mechanism of exposing only the interfaces and hiding the implementation details from the user.
C++ supports the properties of encapsulation and data hiding through the creation of user-defined types, called classes. We already have studied that a class can contain private, protected and public members. By default, all items defined in a class are private. For example −
Class Box {
Public:
Double getVolume(void) {
Return length * breadth * height;
}
Private:
Double length; // Length of a box
Double breadth; // Breadth of a box
Double height; // Height of a box
};
The variables length, breadth, and height are private. This means that they can be accessed only by other members of the Box class, and not by any other part of your program. This is one way encapsulation is achieved.
To make parts of a class public (i.e., accessible to other parts of your program), you must declare them after the public keyword. All variables or functions defined after the public specifier are accessible by all other functions in your program.
Making one class a friend of another exposes the implementation details and reduces encapsulation. The ideal is to keep as many of the details of each class hidden from all other classes as possible.
Data Encapsulation Example
Any C++ program where you implement a class with public and private members is an example of data encapsulation and data abstraction. Consider the following example –
#include <iostream>
Using namespace std;
Class Adder {
Public:
// constructor
Adder(int i = 0) {
Total = i;
}
// interface to outside world
Void addNum(int number) {
Total += number;
}
// interface to outside world
Int getTotal() {
Return total;
};
Private:
// hidden data from outside world
Int total;
};
Int main() {
Adder a;
a.addNum(10);
a.addNum(20);
a.addNum(30);
Cout<< "Total " << a.getTotal() <<endl;
Return 0;
}
When the above code is compiled and executed, it produces the following result −
Total 60
Above class adds numbers together, and returns the sum. The public members addNum and getTotal are the interfaces to the outside world and a user needs to know them to use the class. The private member total is something that is hidden from the outside world, but is needed for the class to operate properly.
Designing Strategy
Most of us have learnt to make class members private by default unless we really need to expose them. That's just good encapsulation.
This is applied most frequently to data members, but it applies equally to all members, including virtual functions.
Methods: Adding a Method to Class
Methods are functions that belongs to the class.
There are two ways to define functions that belongs to a class:
- Inside class definition
- Outside class definition
In the following example, we define a function inside the class, and we name it "myMethod".
Note: You access methods just like you access attributes; by creating an object of the class and by using the dot syntax (.):
Inside Example
Class MyClass { // The class
public: // Access specifier
void myMethod() { // Method/function defined inside the class
cout << "Hello World!";
}
};
int main() {
MyClass myObj; // Create an object of MyClass
myObj.myMethod(); // Call the method
return 0;
}
To define a function outside the class definition, you have to declare it inside the class and then define it outside of the class. This is done by specifiying the name of the class, followed the scope resolution :: operator, followed by the name of the function:
Outside Example
Class MyClass { // The class
public: // Access specifier
void myMethod(); // Method/function declaration
};
// Method/function definition outside the class
void MyClass::myMethod() {
cout << "Hello World!";
}
int main() {
MyClass myObj; // Create an object of MyClass
myObj.myMethod(); // Call the method
return 0;
}
Parameters
You can also add parameters:
Example
#include <iostream>
using namespace std;
class Car {
public:
int speed(int maxSpeed);
};
int Car::speed(int maxSpeed) {
return maxSpeed;
}
int main() {
Car myObj; // Create an object of Car
cout << myObj.speed(200); // Call the method with an argument
return 0;
}
A C++ program can be made easier to read and maintain by using references rather than pointers. A C++ function can return a reference in a similar way as it returns a pointer.
When a function returns a reference, it returns an implicit pointer to its return value. This way, a function can be used on the left side of an assignment statement. For example, consider this simple program −
#include <iostream>
#include <ctime>
Using namespace std;
Double vals[] = {10.1, 12.6, 33.1, 24.1, 50.0};
Double& setValues( int i ) {
Return vals[i]; // return a reference to the ith element
}
// main function to call above defined function.
Int main () {
Cout<< "Value before change" << endl;
For ( int i = 0; i < 5; i++ ) {
Cout<< "vals[" << i << "] = ";
Cout<< vals[i] << endl;
}
SetValues(1) = 20.23; // change 2nd element
SetValues(3) = 70.8; // change 4th element
Cout<< "Value after change" << endl;
For ( int i = 0; i < 5; i++ ) {
Cout<< "vals[" << i << "] = ";
Cout<< vals[i] << endl;
}
Return 0;
}
When the above code is compiled together and executed, it produces the following result −
Value before change
Vals[0] = 10.1
Vals[1] = 12.6
Vals[2] = 33.1
Vals[3] = 24.1
Vals[4] = 50
Value after change
Vals[0] = 10.1
Vals[1] = 20.23
Vals[2] = 33.1
Vals[3] = 70.8
Vals[4] = 50
When returning a reference, be careful that the object being referred to does not go out of scope. So it is not legal to return a reference to local var. But you can always return a reference on a static variable.
Int& func() {
Int q;
//! return q; // Compile time error
Static int x;
Return x; // Safe, x lives outside this scope
}
Functions allow to structure programs in segments of code to perform individual tasks.
In C++, a function is a group of statements that is given a name, and which can be called from some point of the program. The most common syntax to define a function is:
type name ( parameter1, parameter2, ...) { statements }
Where:
- type is the type of the value returned by the function.
- name is the identifier by which the function can be called.
- parameters (as many as needed): Each parameter consists of a type followed by an identifier, with each parameter being separated from the next by a comma. Each parameter looks very much like a regular variable declaration (for example: int x), and in fact acts within the function as a regular variable which is local to the function. The purpose of parameters is to allow passing arguments to the function from the location where it is called from.
- statements is the function's body. It is a block of statements surrounded by braces { } that specify what the function actually does.
Let's have a look at an example:
1 | // function example #include <iostream> Usingnamespace std;
Int addition (int a, int b) { Int r; r=a+b; Return r; }
Int main () { Int z; z = addition (5,3); Cout << "The result is " << z; } | The result is 8 |
|
This program is divided in two functions: addition and main. Remember that no matter the order in which they are defined, a C++ program always starts by calling main. In fact, main is the only function called automatically, and the code in any other function is only executed if its function is called from main (directly or indirectly).
In the example above, main begins by declaring the variable z of type int, and right after that, it performs the first function call: it calls addition. The call to a function follows a structure very similar to its declaration. In the example above, the call to addition can be compared to its definition just a few lines earlier:
The parameters in the function declaration have a clear correspondence to the arguments passed in the function call. The call passes two values, 5 and 3, to the function; these correspond to the parameters a and b, declared for function addition.
At the point at which the function is called from within main, the control is passed to function addition: here, execution of main is stopped, and will only resume once the addition function ends. At the moment of the function call, the value of both arguments (5 and 3) are copied to the local variables int a and int b within the function.
Then, inside addition, another local variable is declared (int r), and by means of the expression r=a+b, the result of a plus b is assigned to r; which, for this case, where a is 5 and b is 3, means that 8 is assigned to r.
The final statement within the function:
| Return r; |
|
Ends function addition, and returns the control back to the point where the function was called; in this case: to function main. At this precise moment, the program resumes its course on main returning exactly at the same point at which it was interrupted by the call to addition. But additionally, because addition has a return type, the call is evaluated as having a value, and this value is the value specified in the return statement that ended addition: in this particular case, the value of the local variable r, which at the moment of the return statement had a value of 8.
Therefore, the call to addition is an expression with the value returned by the function, and in this case, that value, 8, is assigned to z. It is as if the entire function call (addition(5,3)) was replaced by the value it returns (i.e., 8).
Then main simply prints this value by calling:
| Cout << "The result is " << z; |
|
A function can actually be called multiple times within a program, and its argument is naturally not limited just to literals:
1 | // function example #include <iostream> Usingnamespace std;
Int subtraction (int a, int b) { Int r; r=a-b; Return r; }
Int main () { Int x=5, y=3, z; z = subtraction (7,2); Cout << "The first result is " << z << '\n'; Cout << "The second result is " << subtraction (7,2) << '\n'; Cout << "The third result is " << subtraction (x,y) << '\n'; z= 4 + subtraction (x,y); Cout << "The fourth result is " << z << '\n'; } | The first result is 5 The second result is 5 The third result is 2 The fourth result is 6 |
|
Similar to the addition function in the previous example, this example defines a subtract function, that simply returns the difference between its two parameters. This time, main calls this function several times, demonstrating more possible ways in which a function can be called.
Let's examine each of these calls, bearing in mind that each function call is itself an expression that is evaluated as the value it returns. Again, you can think of it as if the function call was itself replaced by the returned value:
1 | z = subtraction (7,2); Cout << "The first result is " << z; |
|
If we replace the function call by the value it returns (i.e., 5), we would have:
1 | z = 5; Cout << "The first result is " << z; |
|
With the same procedure, we could interpret:
| Cout << "The second result is " << subtraction (7,2); |
|
as:
| Cout << "The second result is " << 5; |
|
since 5 is the value returned by subtraction (7,2).
In the case of:
| Cout << "The third result is " << subtraction (x,y); |
|
The arguments passed to subtraction are variables instead of literals. That is also valid, and works fine. The function is called with the values x and y have at the moment of the call: 5 and 3 respectively, returning 2 as result.
The fourth call is again similar:
| z = 4 + subtraction (x,y); |
|
The only addition being that now the function call is also an operand of an addition operation. Again, the result is the same as if the function call was replaced by its result: 6. Note, that thanks to the commutative property of additions, the above can also be written as:
| z = subtraction (x,y) + 4; |
|
With exactly the same result. Note also that the semicolon does not necessarily go after the function call, but, as always, at the end of the whole statement. Again, the logic behind may be easily seen again by replacing the function calls by their returned value:
1 | z = 4 + 2; // same as z = 4 + subtraction (x,y); z = 2 + 4; // same as z = subtraction (x,y) + 4; |
|
Functions with no type. The use of void
The syntax shown above for functions:
type name ( argument1, argument2 ...) { statements }
Requires the declaration to begin with a type. This is the type of the value returned by the function. But what if the function does not need to return a value? In this case, the type to be used is void, which is a special type to represent the absence of value. For example, a function that simply prints a message may not need to return any value:
1 | // void function example #include <iostream> Usingnamespace std;
Void printmessage () { Cout<< "I'm a function!"; }
Int main () { Printmessage (); } | I'm a function! |
|
void can also be used in the function's parameter list to explicitly specify that the function takes no actual parameters when called. For example, printmessage could have been declared as:
1 | Void printmessage (void) { Cout<< "I'm a function!"; } |
|
In C++, an empty parameter list can be used instead of void with same meaning, but the use of void in the argument list was popularized by the C language, where this is a requirement.
Something that in no case is optional are the parentheses that follow the function name, neither in its declaration nor when calling it. And even when the function takes no parameters, at least an empty pair of parentheses shall always be appended to the function name. See how printmessage was called in an earlier example:
| Printmessage (); |
|
The parentheses are what differentiate functions from other kinds of declarations or statements. The following would not call the function:
| Printmessage; |
|
The return value of main
You may have noticed that the return type of main is int, but most examples in this and earlier chapters did not actually return any value from main.
Well, there is a catch: If the execution of main ends normally without encountering a return statement the compiler assumes the function ends with an implicit return statement:
| Return 0; |
|
Note that this only applies to function main for historical reasons. All other functions with a return type shall end with a proper return statement that includes a return value, even if this is never used.
When main returns zero (either implicitly or explicitly), it is interpreted by the environment as that the program ended successfully. Other values may be returned by main, and some environments give access to that value to the caller in some way, although this behavior is not required nor necessarily portable between platforms. The values for main that are guaranteed to be interpreted in the same way on all platforms are:
Because the implicit return 0; statement for main is a tricky exception, some authors consider it good practice to explicitly write the statement.
Arguments passed by value and by reference
In the functions seen earlier, arguments have always been passed by value. This means that, when calling a function, what is passed to the function are the values of these arguments on the moment of the call, which are copied into the variables represented by the function parameters. For example, take:
1 | Int x=5, y=3, z; z = addition ( x, y ); |
|
In this case, function addition is passed 5 and 3, which are copies of the values of x and y, respectively. These values (5 and 3) are used to initialize the variables set as parameters in the function's definition, but any modification of these variables within the function has no effect on the values of the variables x and y outside it, because x and y were themselves not passed to the function on the call, but only copies of their values at that moment.
In certain cases, though, it may be useful to access an external variable from within a function. To do that, arguments can be passed by reference, instead of by value. For example, the function duplicate in this code duplicates the value of its three arguments, causing the variables used as arguments to actually be modified by the call:
1 | // passing parameters by reference #include <iostream> Usingnamespace std;
Void duplicate (int& a, int& b, int& c) { a*=2; b*=2; c*=2; }
Int main () { Int x=1, y=3, z=7; Duplicate (x, y, z); Cout << "x=" << x << ", y=" << y << ", z=" << z; Return 0; } |
|
|
To gain access to its arguments, the function declares its parameters as references. In C++, references are indicated with an ampersand (&) following the parameter type, as in the parameters taken by duplicate in the example above.
When a variable is passed by reference, what is passed is no longer a copy, but the variable itself, the variable identified by the function parameter, becomes somehow associated with the argument passed to the function, and any modification on their corresponding local variables within the function are reflected in the variables passed as arguments in the call.
In fact, a, b, and c become aliases of the arguments passed on the function call (x, y, and z) and any change on a within the function is actually modifying variable x outside the function. Any change on b modifies y, and any change on c modifies z. That is why when, in the example, function duplicate modifies the values of variables a, b, and c, the values of x, y, and z are affected.
If instead of defining duplicate as:
| Void duplicate (int& a, int& b, int& c) |
|
Was it to be defined without the ampersand signs as:
| Void duplicate (int a, int b, int c) |
|
The variables would not be passed by reference, but by value, creating instead copies of their values. In this case, the output of the program would have been the values of x, y, and z without being modified (i.e., 1, 3, and 7).
Efficiency considerations and const references
Calling a function with parameters taken by value causes copies of the values to be made. This is a relatively inexpensive operation for fundamental types such as int, but if the parameter is of a large compound type, it may result on certain overhead. For example, consider the following function:
1 | String concatenate (string a, string b) { Return a+b; } |
|
This function takes two strings as parameters (by value), and returns the result of concatenating them. By passing the arguments by value, the function forces a and b to be copies of the arguments passed to the function when it is called. And if these are long strings, it may mean copying large quantities of data just for the function call.
But this copy can be avoided altogether if both parameters are made references:
1 | String concatenate (string& a, string& b) { Return a+b; } |
|
Arguments by reference do not require a copy. The function operates directly on (aliases of) the strings passed as arguments, and, at most, it might mean the transfer of certain pointers to the function. In this regard, the version of concatenate taking references is more efficient than the version taking values, since it does not need to copy expensive-to-copy strings.
On the flip side, functions with reference parameters are generally perceived as functions that modify the arguments passed, because that is why reference parameters are actually for.
The solution is for the function to guarantee that its reference parameters are not going to be modified by this function. This can be done by qualifying the parameters as constant:
1 | String concatenate (const string& a, const string& b) { Return a+b; } |
|
By qualifying them as const, the function is forbidden to modify the values of neither a nor b, but can actually access their values as references (aliases of the arguments), without having to make actual copies of the strings.
Therefore, const references provide functionality similar to passing arguments by value, but with an increased efficiency for parameters of large types. That is why they are extremely popular in C++ for arguments of compound types. Note though, that for most fundamental types, there is no noticeable difference in efficiency, and in some cases, const references may even be less efficient!
Inline functions
Calling a function generally causes a certain overhead (stacking arguments, jumps, etc...), and thus for very short functions, it may be more efficient to simply insert the code of the function where it is called, instead of performing the process of formally calling a function.
Preceding a function declaration with the inline specifier informs the compiler that inline expansion is preferred over the usual function call mechanism for a specific function. This does not change at all the behavior of a function, but is merely used to suggest the compiler that the code generated by the function body shall be inserted at each point the function is called, instead of being invoked with a regular function call.
For example, the concatenate function above may be declared inline as:
1 | Inline string concatenate (const string& a, const string& b) { Return a+b; } |
|
This informs the compiler that when concatenate is called, the program prefers the function to be expanded inline, instead of performing a regular call. Inline is only specified in the function declaration, not when it is called.
Note that most compilers already optimize code to generate inline functions when they see an opportunity to improve efficiency, even if not explicitly marked with the inline specifier. Therefore, this specifier merely indicates the compiler that inline is preferred for this function, although the compiler is free to not inline it, and optimize otherwise. In C++, optimization is a task delegated to the compiler, which is free to generate any code for as long as the resulting behavior is the one specified by the code.
Default values in parameters
In C++, functions can also have optional parameters, for which no arguments are required in the call, in such a way that, for example, a function with three parameters may be called with only two. For this, the function shall include a default value for its last parameter, which is used by the function when called with fewer arguments. For example:
1 | // default values in functions #include <iostream> Usingnamespace std;
Int divide (int a, int b=2) { Int r; r=a/b; Return (r); }
Int main () { Cout << divide (12) << '\n'; Cout << divide (20,4) << '\n'; Return 0; } |
|
|
In this example, there are two calls to function divide. In the first one:
| Divide (12) |
|
The call only passes one argument to the function, even though the function has two parameters. In this case, the function assumes the second parameter to be 2 (notice the function definition, which declares its second parameter as int b=2). Therefore, the result is 6.
In the second call:
| Divide (20,4) |
|
The call passes two arguments to the function. Therefore, the default value for b (int b=2) is ignored, and b takes the value passed as argument, that is 4, yielding a result of 5.
Declaring functions
In C++, identifiers can only be used in expressions once they have been declared. For example, some variable x cannot be used before being declared with a statement, such as:
| Int x; |
|
The same applies to functions. Functions cannot be called before they are declared. That is why, in all the previous examples of functions, the functions were always defined before the main function, which is the function from where the other functions were called. If main were defined before the other functions, this would break the rule that functions shall be declared before being used, and thus would not compile.
The prototype of a function can be declared without actually defining the function completely, giving just enough details to allow the types involved in a function call to be known. Naturally, the function shall be defined somewhere else, like later in the code. But at least, once declared like this, it can already be called.
The declaration shall include all types involved (the return type and the type of its arguments), using the same syntax as used in the definition of the function, but replacing the body of the function (the block of statements) with an ending semicolon.
The parameter list does not need to include the parameter names, but only their types. Parameter names can nevertheless be specified, but they are optional, and do not need to necessarily match those in the function definition. For example, a function called protofunction with two int parameters can be declared with either of these statements:
1 | Int protofunction (int first, int second); Int protofunction (int, int); |
|
Anyway, including a name for each parameter always improves legibility of the declaration.
1 | // declaring functions prototypes #include <iostream> Usingnamespace std;
Void odd (int x); Void even (int x);
Int main() { Int i; Do { Cout << "Please, enter number (0 to exit): "; Cin >> i; Odd (i); } while (i!=0); Return 0; }
Void odd (int x) { If ((x%2)!=0) cout << "It is odd.\n"; Else even (x); }
Void even (int x) { If ((x%2)==0) cout << "It is even.\n"; Else odd (x); } | Please, enter number (0 to exit): 9 It is odd. Please, enter number (0 to exit): 6 It is even. Please, enter number (0 to exit): 1030 It is even. Please, enter number (0 to exit): 0 It is even. |
|
This example is indeed not an example of efficiency. You can probably write yourself a version of this program with half the lines of code. Anyway, this example illustrates how functions can be declared before its definition:
The following lines:
1 | Void odd (int a); Void even (int a); |
|
Declare the prototype of the functions. They already contain all what is necessary to call them, their name, the types of their argument, and their return type (void in this case). With these prototype declarations in place, they can be called before they are entirely defined, allowing for example, to place the function from where they are called (main) before the actual definition of these functions.
But declaring functions before being defined is not only useful to reorganize the order of functions within the code. In some cases, such as in this particular case, at least one of the declarations is required, because odd and even are mutually called; there is a call to even in odd and a call to odd in even. And, therefore, there is no way to structure the code so that odd is defined before even, and even before odd.
Recursivity
Recursivity is the property that functions have to be called by themselves. It is useful for some tasks, such as sorting elements, or calculating the factorial of numbers. For example, in order to obtain the factorial of a number (n!) the mathematical formula would be:
n! = n * (n-1) * (n-2) * (n-3) ... * 1
More concretely, 5! (factorial of 5) would be:
5! = 5 * 4 * 3 * 2 * 1 = 120
And a recursive function to calculate this in C++ could be:
1 | // factorial calculator #include <iostream> Usingnamespace std;
Long factorial (long a) { If (a > 1) Return (a * factorial (a-1)); Else Return 1; }
Int main () { Long number = 9; Cout<< number << "! = " << factorial (number); Return 0; } |
|
|
Notice how in function factorial we included a call to itself, but only if the argument passed was greater than 1, since, otherwise, the function would perform an infinite recursive loop, in which once it arrived to 0, it would continue multiplying by all the negative numbers (probably provoking a stack overflow at some point during runtime).
In C++ programming, this is a keyword that refers to the current instance of the class. There can be 3 main usage of this keyword in C++.
- It can be used to pass current object as a parameter to another method.
- It can be used to refer current class instance variable.
- It can be used to declare indexers.
C++ this Pointer Example
Let's see the example of this keyword in C++ that refers to the fields of current class.
- #include <iostream>
- Using namespace std;
- Class Employee {
- Public:
- Int id; //data member (also instance variable)
- String name; //data member(also instance variable)
- Float salary;
- Employee(int id, string name, float salary)
- {
- This->id = id;
- This->name = name;
- This->salary = salary;
- }
- Void display()
- {
- Cout<<id<<" "<<name<<" "<<salary<<endl;
- }
- };
- Int main(void) {
- Employee e1 =Employee(101, "Sonoo", 890000); //creating an object of Employee
- Employee e2=Employee(102, "Nakul", 59000); //creating an object of Employee
- e1.display();
- e2.display();
- Return 0;
- }
Output:
101 Sonoo 890000
102 Nakul 59000
Overloading (Function and Operator)
If we create two or more members having the same name but different in number or type of parameter, it is known as C++ overloading. In C++, we can overload:
- Methods,
- Constructors, and
- Indexed properties
It is because these members have parameters only.
Types of overloading in C++ are:
- Function overloading
- Operator overloading
C++ Function Overloading
Function Overloading is defined as the process of having two or more function with the same name, but different in parameters is known as function overloading in C++. In function overloading, the function is redefined by using either different types of arguments or a different number of arguments. It is only through these differences compiler can differentiate between the functions.
The advantage of Function overloading is that it increases the readability of the program because you don't need to use different names for the same action.
C++ Function Overloading Example
Let's see the simple example of function overloading where we are changing number of arguments of add() method.
// program of function overloading when number of arguments vary.
- #include <iostream>
- Using namespace std;
- Class Cal {
- Public:
- Static int add(int a,int b){
- Return a + b;
- }
- Static int add(int a, int b, int c)
- {
- Return a + b + c;
- }
- };
- Int main(void) {
- Cal C; // class object declaration.
- Cout<<C.add(10, 20)<<endl;
- Cout<<C.add(12, 20, 23);
- Return 0;
- }
Output:
30
55
Let's see the simple example when the type of the arguments vary.
// Program of function overloading with different types of arguments.
- #include<iostream>
- Using namespace std;
- Int mul(int,int);
- Float mul(float,int);
- Int mul(int a,int b)
- {
- Return a*b;
- }
- Float mul(double x, int y)
- {
- Return x*y;
- }
- Int main()
- {
- Int r1 = mul(6,7);
- Float r2 = mul(0.2,3);
- Std::cout << "r1 is : " <<r1<< std::endl;
- Std::cout <<"r2 is : " <<r2<< std::endl;
- Return 0;
- }
Output:
r1 is : 42
r2 is : 0.6
Function Overloading and Ambiguity
When the compiler is unable to decide which function is to be invoked among the overloaded function, this situation is known as function overloading.
When the compiler shows the ambiguity error, the compiler does not run the program.
Causes of Function Overloading:
- Type Conversion.
- Function with default arguments.
- Function with pass by reference.
- Type Conversion:
Let's see a simple example.
- #include<iostream>
- Using namespace std;
- Void fun(int);
- Void fun(float);
- Void fun(int i)
- {
- Std::cout << "Value of i is : " <<i<< std::endl;
- }
- Void fun(float j)
- {
- Std::cout << "Value of j is : " <<j<< std::endl;
- }
- Int main()
- {
- Fun(12);
- Fun(1.2);
- Return 0;
- }
The above example shows an error "call of overloaded 'fun(double)' is ambiguous". The fun(10) will call the first function. The fun(1.2) calls the second function according to our prediction. But, this does not refer to any function as in C++, all the floating point constants are treated as double not as a float. If we replace float to double, the program works. Therefore, this is a type conversion from float to double.
- Function with Default Arguments
Let's see a simple example.
- #include<iostream>
- Using namespace std;
- Void fun(int);
- Void fun(int,int);
- Void fun(int i)
- {
- Std::cout << "Value of i is : " <<i<< std::endl;
- }
- Void fun(int a,int b=9)
- {
- Std::cout << "Value of a is : " <<a<< std::endl;
- Std::cout << "Value of b is : " <<b<< std::endl;
- }
- Int main()
- {
- Fun(12);
- Return 0;
- }
The above example shows an error "call of overloaded 'fun(int)' is ambiguous". The fun(int a, int b=9) can be called in two ways: first is by calling the function with one argument, i.e., fun(12) and another way is calling the function with two arguments, i.e., fun(4,5). The fun(int i) function is invoked with one argument. Therefore, the compiler could not be able to select among fun(int i) and fun(int a,int b=9).
- Function with pass by reference
Let's see a simple example.
- #include <iostream>
- Using namespace std;
- Void fun(int);
- Void fun(int &);
- Int main()
- {
- Int a=10;
- Fun(a); // error, which f()?
- Return 0;
- }
- Void fun(int x)
- {
- Std::cout << "Value of x is : " <<x<< std::endl;
- }
- Void fun(int &b)
- {
- Std::cout << "Value of b is : " <<b<< std::endl;
- }
The above example shows an error "call of overloaded 'fun(int&)' is ambiguous". The first function takes one integer argument and the second function takes a reference parameter as an argument. In this case, the compiler does not know which function is needed by the user as there is no syntactical difference between the fun(int) and fun(int &).
C++ Operators Overloading
Operator overloading is a compile-time polymorphism in which the operator is overloaded to provide the special meaning to the user-defined data type. Operator overloading is used to overload or redefines most of the operators available in C++. It is used to perform the operation on the user-defined data type. For example, C++ provides the ability to add the variables of the user-defined data type that is applied to the built-in data types.
The advantage of Operators overloading is to perform different operations on the same operand.
Operator that cannot be overloaded are as follows:
- Scope operator (::)
- Sizeof
- Member selector(.)
- Member pointer selector(*)
- Ternary operator(?:)
Syntax of Operator Overloading
- Return_type class_name : : operator op(argument_list)
- {
- // body of the function.
- }
Where the return type is the type of value returned by the function.
Class_name is the name of the class.
Operator op is an operator function where op is the operator being overloaded, and the operator is the keyword.
Rules for Operator Overloading
- Existing operators can only be overloaded, but the new operators cannot be overloaded.
- The overloaded operator contains atleast one operand of the user-defined data type.
- We cannot use friend function to overload certain operators. However, the member function can be used to overload those operators.
- When unary operators are overloaded through a member function take no explicit arguments, but, if they are overloaded by a friend function, takes one argument.
- When binary operators are overloaded through a member function takes one explicit argument, and if they are overloaded through a friend function takes two explicit arguments.
C++ Operators Overloading Example
Let's see the simple example of operator overloading in C++. In this example, void operator ++ () operator function is defined (inside Test class).
// program to overload the unary operator ++.
- #include <iostream>
- Using namespace std;
- Class Test
- {
- Private:
- Int num;
- Public:
- Test(): num(8){}
- Void operator ++() {
- Num = num+2;
- }
- Void Print() {
- Cout<<"The Count is: "<<num;
- }
- };
- Int main()
- {
- Test tt;
- ++tt; // calling of a function "void operator ++()"
- Tt.Print();
- Return 0;
- }
Output:
The Count is: 10
Let's see a simple example of overloading the binary operators.
// program to overload the binary operators.
- #include <iostream>
- Using namespace std;
- Class A
- {
- Int x;
- Public:
- A(){}
- A(int i)
- {
- x=i;
- }
- Void operator+(A);
- Void display();
- };
- Void A :: operator+(A a)
- {
- Int m = x+a.x;
- Cout<<"The result of the addition of two objects is : "<<m;
- }
- Int main()
- {
- A a1(5);
- A a2(4);
- a1+a2;
- Return 0;
- }
Output:
The result of the addition of two objects is : 9
Since C++ is an object-oriented language, program is designed using objects and classes in C++.
Object
In C++, Object is a real world entity, for example, chair, car, pen, mobile, laptop etc.
In other words, object is an entity that has state and behavior. Here, state means data and behavior means functionality.
Object is a runtime entity, it is created at runtime.
Object is an instance of a class. All the members of the class can be accessed through object.
Let's see an example to create object of student class using s1 as the reference variable.
- Student s1; //creating an object of Student
In this example, Student is the type and s1 is the reference variable that refers to the instance of Student class.
Class
In C++, object is a group of similar objects. It is a template from which objects are created. It can have fields, methods, constructors etc.
Let's see an example of C++ class that has three fields only.
- Class Student
- {
- Public:
- Int id; //field or data member
- Float salary; //field or data member
- String name;//field or data member
- }
Object and Class Example
Let's see an example of class that has two fields: id and name. It creates instance of the class, initializes the object and prints the object value.
- #include <iostream>
- Using namespace std;
- Class Student {
- Public:
- Int id;//data member (also instance variable)
- String name;//data member(also instance variable)
- };
- Int main() {
- Student s1; //creating an object of Student
- s1.id = 201;
- s1.name = "Sonoo Jaiswal";
- Cout<<s1.id<<endl;
- Cout<<s1.name<<endl;
- Return 0;
- }
Output:
201
Sonoo Jaiswal
Initialize and Display data through method
Let's see another example of C++ class where we are initializing and displaying object through method.
- #include <iostream>
- Using namespace std;
- Class Student {
- Public:
- Int id;//data member (also instance variable)
- String name;//data member(also instance variable)
- Void insert(int i, string n)
- {
- Id = i;
- Name = n;
- }
- Void display()
- {
- Cout<<id<<" "<<name<<endl;
- }
- };
- Int main(void) {
- Student s1; //creating an object of Student
- Student s2; //creating an object of Student
- s1.insert(201, "Sonoo");
- s2.insert(202, "Nakul");
- s1.display();
- s2.display();
- Return 0;
- }
Output:
201 Sonoo
202 Nakul
Store and Display Employee Information
Let's see another example of C++ class where we are storing and displaying employee information using method.
- #include <iostream>
- Using namespace std;
- Class Employee {
- Public:
- Int id;//data member (also instance variable)
- String name;//data member(also instance variable)
- Float salary;
- Void insert(int i, string n, float s)
- {
- Id = i;
- Name = n;
- Salary = s;
- }
- Void display()
- {
- Cout<<id<<" "<<name<<" "<<salary<<endl;
- }
- };
- Int main(void) {
- Employee e1; //creating an object of Employee
- Employee e2; //creating an object of Employee
- e1.insert(201, "Sonoo",990000);
- e2.insert(202, "Nakul", 29000);
- e1.display();
- e2.display();
- Return 0;
- }
Output:
201 Sonoo 990000
202 Nakul 29000
Passing an Object as argument
To pass an object as an argument we write the object name as the argument while calling the function the same way we do it for other variables.
Syntax:
Function_name(object_name);
Example: In this Example there is a class which has an integer variable ‘a’ and a function ‘add’ which takes an object as argument. The function is called by one object and takes another as an argument. Inside the function, the integer value of the argument object is added to that on which the ‘add’ function is called. In this method, we can pass objects as an argument and alter them.
// C++ program to show passing // of objects to a function
#include <bits/stdc++.h> Using namespace std;
Class Example { Public: Int a;
// This function will take // an object as an argument Void add(Example E) { a = a + E.a; } };
// Driver Code Int main() {
// Create objects Example E1, E2;
// Values are initialized for both objects E1.a = 50; E2.a = 100;
Cout << "Initial Values \n"; Cout << "Value of object 1: " << E1.a << "\n& object 2: " << E2.a << "\n\n";
// Passing object as an argument // to function add() E2.add(E1);
// Changed values after passing // object as argument Cout << "New values \n"; Cout << "Value of object 1: " << E1.a << "\n& object 2: " << E2.a << "\n\n";
Return 0; } |
Output:
Initial Values
Value of object 1: 50
& object 2: 100
New values
Value of object 1: 50
& object 2: 150
Returning Object as argument
Syntax:
Object = return object_name;
Example: In the above example we can see that the add function does not return any value since its return-type is void. In the following program the add function returns an object of type ‘Example'(i.e., class name) whose value is stored in E3.
In this example, we can see both the things that are how we can pass the objects as well as return them. When the object E3 calls the add function it passes the other two objects namely E1 & E2 as arguments. Inside the function, another object is declared which calculates the sum of all the three variables and returns it to E3.
This code and the above code is almost the same, the only difference is that this time the add function returns an object whose value is stored in another object of the same class ‘Example’ E3. Here the value of E1 is displayed by object1, the value of E2 by object2 and value of E3 by object3.
// C++ program to show passing // of objects to a function
#include <bits/stdc++.h> Using namespace std;
Class Example { Public: Int a;
// This function will take // object as arguments and // return object Example add(Example Ea, Example Eb) { Example Ec; Ec.a = Ec.a + Ea.a + Eb.a;
// returning the object Return Ec; } }; Int main() { Example E1, E2, E3;
// Values are initialized // for both objects E1.a = 50; E2.a = 100; E3.a = 0;
Cout << "Initial Values \n"; Cout << "Value of object 1: " << E1.a << ", \nobject 2: " << E2.a << ", \nobject 3: " << E3.a << "\n";
// Passing object as an argument // to function add() E3 = E3.add(E1, E2);
// Changed values after // passing object as an argument Cout << "New values \n"; Cout << "Value of object 1: " << E1.a << ", \nobject 2: " << E2.a << ", \nobject 3: " << E3.a << "\n"; Return 0; } |
Output:
Initial Values
Value of object 1: 50,
Object 2: 100,
Object 3: 0
New values
Value of object 1: 50,
Object 2: 100,
Object 3: 200
An object is an instance of a class. Memory is only allocated when an object is created and not when a class is defined.
An object can be returned by a function using the return keyword. A program that demonstrates this is given as follows −
Example
#include <iostream>
Using namespace std;
Class Point {
Private:
Int x;
Int y;
Public:
Point(int x1 = 0, int y1 = 0) {
x = x1;
y = y1;
}
Point addPoint(Point p) {
Point temp;
Temp.x = x + p.x;
Temp.y = y + p.y;
Return temp;
}
Void display() {
Cout<<"x = "<< x <<"\n";
Cout<<"y = "<< y <<"\n";
}
};
Int main() {
Point p1(5,3);
Point p2(12,6);
Point p3;
Cout<<"Point 1\n";
p1.display();
Cout<<"Point 2\n";
p2.display();
p3 = p1.addPoint(p2);
Cout<<"The sum of the two points is:\n";
p3.display();
Return 0;
}
Output
The output of the above program is as follows.
Point 1
x = 5
y = 3
Point 2
x = 12
y = 6
The sum of the two points is:
x = 17
y = 9
Now, let us understand the above program.
The class Point has two data members i.e. x and y. It has a parameterized constructor and also 2 member functions. The function addPoint() adds two Point values and returns an object temp that stores the sum. The function display() prints the values of x and y. The code snippet for this is given as follows.
Class Point {
Private:
Int x;
Int y;
Public:
Point(int x1 = 0, int y1 = 0) {
x = x1;
y = y1;
}
Point addPoint(Point p) {
Point temp;
Temp.x = x + p.x;
Temp.y = y + p.y;
Return temp;
}
Void display() {
Cout<<"x = "<< x <<"\n";
Cout<<"y = "<< y <<"\n";
}
};
In the function main(), 3 objects of class Point are created. First values of p1 and p2 are displayed. Then the sum of values in p1 and p2 is found and stored in p3 by calling function addPoint(). Value of p3 is displayed. The code snippet for this is given as follows.
Point p1(5,3);
Point p2(12,6);
Point p3;
Cout<<"Point 1\n";
p1.display();
Cout<<"Point 2\n";
p2.display();
p3 = p1.addPoint(p2);
Cout<<"The sum of the two points is:\n";
p3.display();
- Like array of other user-defined data types, an array of type class can also be created.
- The array of type class contains the objects of the class as its individual elements.
- Thus, an array of a class type is also known as an array of objects.
- An array of objects is declared in the same way as an array of any built-in data type.
Syntax:
Class_name array_name [size] ;
Example:
#include <iostream>
Class MyClass {
Int x;
Public:
Void setX(int i) { x = i; }
Int getX() { return x; }
};
Void main()
{
MyClass obs[4];
Int i;
For(i=0; i < 4; i++)
Obs[i].setX(i);
For(i=0; i < 4; i++)
Cout<< "obs[" << i << "].getX(): " << obs[i].getX() << "\n";
Getch();
}
Output:
Obs[0].getX(): 0
obs[1].getX(): 1
obs[2].getX(): 2
obs[3].getX(): 3
C++ allows us to allocate the memory of a variable or an array in run time. This is known as dynamic memory allocation.
In other programming languages such as Java and Python, the compiler automatically manages the memories allocated to variables. But this is not the case in C++.
In C++, we need to deallocate the dynamically allocated memory manually after we have no use for the variable.
We can allocate and then deallocate memory dynamically using the new and delete operators respectively.
C++ new Operator
The new operator allocates memory to a variable. For example,
// declare an int pointer
Int* pointVar;
// dynamically allocate memory
// using the new keyword
PointVar = newint;
// assign value to allocated memory
*pointVar = 45;
Here, we have dynamically allocated memory for an int variable using the new operator.
Notice that we have used the pointer pointVar to allocate the memory dynamically. This is because the new operator returns the address of the memory location.
In the case of an array, the new operator returns the address of the first element of the array.
From the example above, we can see that the syntax for using the new operator is
PointerVariable = new dataType;
Delete Operator
Once we no longer need to use a variable that we have declared dynamically, we can deallocate the memory occupied by the variable.
For this, the delete operator is used. It returns the memory to the operating system. This is known as memory deallocation.
The syntax for this operator is
Delete pointerVariable;
Consider the code:
// declare an int pointer
Int* pointVar;
// dynamically allocate memory
// for an int variable
PointVar = newint;
// assign value to the variable memory
*pointVar = 45;
// print the value stored in memory
Cout<< *pointVar; // Output: 45
// deallocate the memory
Delete pointVar;
Here, we have dynamically allocated memory for an int variable using the pointer pointVar.
After printing the contents of pointVar, we deallocated the memory using delete.
Note: If the program uses a large amount of unwanted memory using new, the system may crash because there will be no memory available for the operating system. In this case, the delete operator can help the system from crash.
Example 1: C++ Dynamic Memory Allocation
Usingnamespacestd;
Intmain(){
// declare an int pointer
Int* pointInt;
// declare a float pointer
Float* pointFloat;
// dynamically allocate memory
PointInt = newint;
PointFloat = newfloat;
// assigning value to the memory
*pointInt = 45;
*pointFloat = 45.45f;
Cout<< *pointInt <<endl;
Cout<< *pointFloat <<endl;
// deallocate the memory
Delete pointInt, pointFloat;
Return0;
}
Output
45
45.45
In this program, we dynamically allocated memory to two variables of int and float types. After assigning values to them and printing them, we finally deallocate the memories using the code
Delete pointInt, pointFloat;
Note: Dynamic memory allocation can make memory management more efficient.
Especially for arrays, where a lot of the times we don't know the size of the array until the run time.
Example 2: C++ new and delete Operator for Arrays
// C++ Program to store GPA of n number of students and display it
// where n is the number of students entered by the user
Usingnamespacestd;
Intmain(){
Int num;
Cout<<"Enter total number of students: ";
Cin>> num;
Float* ptr;
// memory allocation of num number of floats
Ptr = newfloat[num];
Cout<<"Enter GPA of students."<<endl;
For (int i = 0; i < num; ++i) {
Cout<<"Student"<< i + 1<<": ";
Cin>> *(ptr + i);
}
Cout<<"\nDisplaying GPA of students."<<endl;
For (int i = 0; i < num; ++i) {
Cout<<"Student"<< i + 1<<" :"<< *(ptr + i) <<endl;
}
// ptr memory is released
Delete [] ptr;
Return0;
}
Output
Enter total number of students: 4
Enter GPA of students.
Student1: 3.6
Student2: 3.1
Student3: 3.9
Student4: 2.9
Displaying GPA of students.
Student1 :3.6
Student2 :3.1
Student3 :3.9
Student4 :2.9
In this program, we have asked the user to enter the number of students and store it in the num variable.
Then, we have allocated the memory dynamically for the float array using new.
We enter data into the array (and later print them) using pointer notation.
After we no longer need the array, we deallocate the array memory using the code delete [] ptr;.
Notice the use of [] after delete. We use the square brackets [] in order to denote that the memory deallocation is that of an array.
Example 3: C++ new and delete Operator for Objects
Usingnamespacestd;
ClassStudent {
Int age;
Public:
// constructor initializes age to 12
Student() : age(12) {}
VoidgetAge(){
Cout<<"Age = "<< age <<endl;
}
};
Intmain(){
// dynamically declare Student object
Student* ptr = newStudent();
// call getAge() function
Ptr->getAge();
// ptr memory is released
Delete ptr;
Return0;
}
Output
Age = 12
In this program, we have created a Student class that has a private variable age.
We have initialized age to 12 in the default constructor Student() and print its value with the function getAge().
In main(), we have created a Student object using the new operator and use the pointer ptr to point to its address.
The moment the object is created, the Student() constructor initializes age to 12.
We then call the getAge() function using the code:
Ptr->getAge();
Notice the arrow operator ->. This operator is used to access class members using pointers.
Static data members are class members that are declared using the static keyword. There is only one copy of the static data member in the class, even if there are many class objects. This is because all the objects share the static data member. The static data member is always initialized to zero when the first class object is created.
The syntax of the static data members is given as follows −
Static data_type data_member_name;
In the above syntax, static keyword is used. The data_type is the C++ data type such as int, float etc. The data_member_name is the name provided to the data member.
A program that demonstrates the static data members in C++ is given as follows −
Example
#include <iostream>
#include<string.h>
Using namespace std;
Class Student {
Private:
Int rollNo;
Char name[10];
Int marks;
Public:
Static int objectCount;
Student() {
ObjectCount++;
}
Void getdata() {
Cout<< "Enter roll number: "<<endl;
Cin>> rollNo;
Cout<< "Enter name: "<<endl;
Cin>> name;
Cout<< "Enter marks: "<<endl;
Cin>> marks;
}
Void putdata() {
Cout<<"Roll Number = "<< rollNo <<endl;
Cout<<"Name = "<< name <<endl;
Cout<<"Marks = "<< marks <<endl;
Cout<<endl;
}
};
Int Student::objectCount = 0;
Int main(void) {
Student s1;
s1.getdata();
s1.putdata();
Student s2;
s2.getdata();
s2.putdata();
Student s3;
s3.getdata();
s3.putdata();
Cout<< "Total objects created = " << Student::objectCount << endl;
Return 0;
}
Output
The output of the above program is as follows −
Enter roll number: 1
Enter name: Mark
Enter marks: 78
Roll Number = 1
Name = Mark
Marks = 78
Enter roll number: 2
Enter name: Nancy
Enter marks: 55
Roll Number = 2
Name = Nancy
Marks = 55
Enter roll number: 3
Enter name: Susan
Enter marks: 90
Roll Number = 3
Name = Susan
Marks = 90
Total objects created = 3
In the above program, the class student has three data members denoting the student roll number, name and marks. The objectCount data member is a static data member that contains the number of objects created of class Student. Student() is a constructor that increments objectCount each time a new class object is created.
There are 2 member functions in class. The function getdata() obtains the data from the user and putdata() displays the data. The code snippet for this is as follows −
Class Student {
Private:
Int rollNo;
Char name[10];
Int marks;
Public:
Static int objectCount;
Student() {
ObjectCount++;
}
Void getdata() {
Cout<< "Enter roll number: "<<endl;
Cin>> rollNo;
Cout<< "Enter name: "<<endl;
Cin>> name;
Cout<< "Enter marks: "<<endl;
Cin>> marks;
}
Void putdata() {
Cout<<"Roll Number = "<< rollNo <<endl;
Cout<<"Name = "<< name <<endl;
Cout<<"Marks = "<< marks <<endl;
Cout<<endl;
}
};
In the function main(), there are three objects of class Student i.e. s1, s2 and s3. For each of these objects getdata() and putdata() are called. At the end, the value of objectCount is displayed. This is given below −
Int main(void) {
Student s1;
s1.getdata();
s1.putdata();
Student s2;
s2.getdata();
s2.putdata();
Student s3;
s3.getdata();
s3.putdata();
Cout<< "Total objects created = " << Student::objectCount << endl;
Return 0;
}
We can define class members static using static keyword. When we declare a member of a class as static it means no matter how many objects of the class are created, there is only one copy of the static member.
A static member is shared by all objects of the class. All static data is initialized to zero when the first object is created, if no other initialization is present. We can't put it in the class definition but it can be initialized outside the class as done in the following example by redeclaring the static variable, using the scope resolution operator :: to identify which class it belongs to.
Let us try the following example to understand the concept of static data members −
#include <iostream>
Using namespace std;
Class Box {
Public:
Static int objectCount;
// Constructor definition
Box(double l = 2.0, double b = 2.0, double h = 2.0) {
Cout<<"Constructor called." <<endl;
Length = l;
Breadth = b;
Height = h;
// Increase every time object is created
ObjectCount++;
}
Double Volume() {
Return length * breadth * height;
}
Private:
Double length; // Length of a box
Double breadth; // Breadth of a box
Double height; // Height of a box
};
// Initialize static member of class Box
Int Box::objectCount = 0;
Int main(void) {
Box Box1(3.3, 1.2, 1.5); // Declare box1
Box Box2(8.5, 6.0, 2.0); // Declare box2
// Print total number of objects.
Cout<< "Total objects: " << Box::objectCount << endl;
Return 0;
}
When the above code is compiled and executed, it produces the following result −
Constructor called.
Constructor called.
Total objects: 2
Static Function Members
By declaring a function member as static, you make it independent of any particular object of the class. A static member function can be called even if no objects of the class exist and the static functions are accessed using only the class name and the scope resolution operator ::.
A static member function can only access static data member, other static member functions and any other functions from outside the class.
Static member functions have a class scope and they do not have access to the this pointer of the class. You could use a static member function to determine whether some objects of the class have been created or not.
Let us try the following example to understand the concept of static function members −
#include <iostream>
Using namespace std;
Class Box {
Public:
Static int objectCount;
// Constructor definition
Box(double l = 2.0, double b = 2.0, double h = 2.0) {
Cout<<"Constructor called." <<endl;
Length = l;
Breadth = b;
Height = h;
// Increase every time object is created
ObjectCount++;
}
Double Volume() {
Return length * breadth * height;
}
Static int getCount() {
Return objectCount;
}
Private:
Double length; // Length of a box
Double breadth; // Breadth of a box
Double height; // Height of a box
};
// Initialize static member of class Box
Int Box::objectCount = 0;
Int main(void) {
// Print total number of objects before creating object.
Cout<< "Inital Stage Count: " << Box::getCount() << endl;
Box Box1(3.3, 1.2, 1.5); // Declare box1
Box Box2(8.5, 6.0, 2.0); // Declare box2
// Print total number of objects after creating object.
Cout<< "Final Stage Count: " << Box::getCount() << endl;
Return 0;
}
When the above code is compiled and executed, it produces the following result −
Inital Stage Count: 0
Constructor called.
Constructor called.
Final Stage Count: 2
Forward Declaration refers to the beforehand declaration of the syntax or signature of an identifier, variable, function, class, etc. prior to its usage (done later in the program).
Example:
// Forward Declaration of the sum()
Void sum(int, int);
// Usage of the sum
Void sum(int a, int b)
{
// Body
}
In C++, Forward declarations are usually used for Classes. In this, the class is pre-defined before its use so that it can be called and used by other classes that are defined before this.
Example:
// Forward Declaration class A
Class A;
// Definition of class A
Class A{
// Body
};
Need for Forward Declarations:
Let us understand the need for forward declaration with an example.
Example 1:
. // C++ program to show // the need for Forward Declaration
#include <iostream> Usingnamespacestd;
ClassB {
Public: Intx;
Voidgetdata(intn) { x = n; } Friendintsum(A, B); };
ClassA { Public: Inty;
Voidgetdata(intm) { y = m; } Friendintsum(A, B); };
Intsum(A m, B n) { Intresult; Result = m.y + n.x; Returnresult; }
Intmain() { B b; A a; a.getdata(5); b.getdata(4); Cout << "The sum is : "<< sum(a, b); Return0; } |
Output:
Compile Errors :
Prog.cpp:14:18: error: 'A' has not been declared
Friend int sum(A, B);
^
Explanation: Here the compiler throws this error because, in class B, the object of class A is being used, which has no declaration till that line. Hence compiler couldn’t find class A. So what if class A is written before class B? Let’s find out in the next example.
Example 2:
// C++ program to show // the need for Forward Declaration
#include <iostream> Usingnamespacestd;
ClassA { Public: Inty;
Voidgetdata(intm) { y = m; } Friendintsum(A, B); };
ClassB {
Public: Intx;
Voidgetdata(intn) { x = n; } Friendintsum(A, B); };
Intsum(A m, B n) { Intresult; Result = m.y + n.x; Returnresult; }
Intmain() { B b; A a; a.getdata(5); b.getdata(4); Cout << "The sum is : "<< sum(a, b); Return0; } |
Output:
Compile Errors :
Prog.cpp:16:23: error: 'B' has not been declared
Friend int sum(A, B);
^
Explanation: Here the compiler throws this error because, in class A, the object of class B is being used, which has no declaration till that line. Hence compiler couldn’t find class B.
Now it is clear that any of the above codes wouldn’t work, no matter in which order the classes are written. Hence this problem needs a new solution- Forward Declaration.
Let us add the forward declaration to the above example and check the output again.
Example 3:
#include <iostream> Usingnamespacestd;
// Forward declaration ClassA; ClassB;
ClassB { Intx;
Public: Voidgetdata(intn) { x = n; } Friendintsum(A, B); };
ClassA { Inty;
Public: Voidgetdata(intm) { y = m; } Friendintsum(A, B); }; Intsum(A m, B n) { Intresult; Result = m.y + n.x; Returnresult; }
Intmain() { B b; A a; a.getdata(5); b.getdata(4); Cout << "The sum is : "<< sum(a, b); Return0; } |
Output:
The sum is : 9
The program runs without any errors now. A forward declaration tells the compiler about the existence of an entity before actually defining the entity. Forward declarations can also be used with other entity in C++, such as functions, variables and user-defined types.
Abstract Data type (ADT) is a type (or class) for objects whose behaviour is defined by a set of value and a set of operations.
The definition of ADT only mentions what operations are to be performed but not how these operations will be implemented. It does not specify how data will be organized in memory and what algorithms will be used for implementing the operations. It is called “abstract” because it gives an implementation-independent view. The process of providing only the essentials and hiding the details is known as abstraction.
The user of data type does not need to know how that data type is implemented, for example, we have been using Primitive values like int, float, char data types only with the knowledge that these data type can operate and be performed on without any idea of how they are implemented. So a user only needs to know what a data type can do, but not how it will be implemented. Think of ADT as a black box which hides the inner structure and design of the data type. Now we’ll define three ADTs namely List ADT, Stack ADT, Queue ADT.
- List ADT
- The data is generally stored in key sequence in a list which has a head structure consisting of count, pointers and address of compare function needed to compare the data in the list.
- The data node contains the pointer to a data structure and a self-referential pointer which points to the next node in the list.
//List ADT Type Definitions Typedefstructnode { Void*DataPtr; Structnode *link; } Node; Typedefstruct { Intcount; Node *pos; Node *head; Node *rear; Int(*compare) (void*argument1, void*argument2) } LIST; |
- The List ADT Functions is given below:
A list contains elements of the same type arranged in sequential order and following operations can be performed on the list.
- Get() – Return an element from the list at any given position.
- Insert() – Insert an element at any position of the list.
- Remove() – Remove the first occurrence of any element from a non-empty list.
- RemoveAt() – Remove the element at a specified location from a non-empty list.
- Replace() – Replace an element at any position by another element.
- Size() – Return the number of elements in the list.
- IsEmpty() – Return true if the list is empty, otherwise return false.
- IsFull() – Return true if the list is full, otherwise return false.
- Stack ADT
- In Stack ADT Implementation instead of data being stored in each node, the pointer to data is stored.
- The program allocates memory for the data and address is passed to the stack ADT.
- The head node and the data nodes are encapsulated in the ADT. The calling function can only see the pointer to the stack.
- The stack head structure also contains a pointer to top and count of number of entries currently in stack.
//Stack ADT Type Definitions Typedefstructnode { Void*DataPtr; Structnode *link; } StackNode; Typedefstruct { Intcount; StackNode *top; } STACK; |
A Stack contains elements of the same type arranged in sequential order. All operations take place at a single end that is top of the stack and following operations can be performed:
- Push() – Insert an element at one end of the stack called top.
- Pop() – Remove and return the element at the top of the stack, if it is not empty.
- Peek() – Return the element at the top of the stack without removing it, if the stack is not empty.
- Size() – Return the number of elements in the stack.
- IsEmpty() – Return true if the stack is empty, otherwise return false.
- IsFull() – Return true if the stack is full, otherwise return false.
- Queue ADT
- The queue abstract data type (ADT) follows the basic design of the stack abstract data type.
- Each node contains a void pointer to the data and the link pointer to the next element in the queue. The program’s responsibility is to allocate memory for storing the data.
//Queue ADT Type Definitions Typedefstructnode { Void*DataPtr; Structnode *next; } QueueNode; Typedefstruct { QueueNode *front; QueueNode *rear; Intcount; } QUEUE; |
A Queue contains elements of the same type arranged in sequential order. Operations take place at both ends, insertion is done at the end and deletion is done at the front. Following operations can be performed:
- Enqueue() – Insert an element at the end of the queue.
- Dequeue() – Remove and return the first element of the queue, if the queue is not empty.
- Peek() – Return the element of the queue without removing it, if the queue is not empty.
- Size() – Return the number of elements in the queue.
- IsEmpty() – Return true if the queue is empty, otherwise return false.
- IsFull() – Return true if the queue is full, otherwise return false.
From these definitions, we can clearly see that the definitions do not specify how these ADTs will be represented and how the operations will be carried out. There can be different ways to implement an ADT, for example, the List ADT can be implemented using arrays, or singly linked list or doubly linked list. Similarly, stack ADT and Queue ADT can be implemented using arrays or linked lists.
What is a Class?
A C++ class combines data and methods for manipulating the data into one. Classes also determine the forms of objects. The data and methods contained in a class are known as class members. A class is a user-defined data type. To access the class members, we use an instance of the class. You can see a class as a blueprint for an object.
A class be a prototype for a house. It shows the location and sizes of doors, windows, floors, etc. From these descriptions, we can construct a house. The house becomes the object. It's possible to create many houses from the prototype. Also, it's possible to create many objects from a class.
In the above figure, we have a single house prototype. From this prototype, we have created two houses with different features.
Class Declaration
In C+, a class is defined using the class keyword. This should be followed by the class name. The class body is then added between curly braces { }.
Syntax:
Class class-name
{
// data
// functions
};
- The class-name is the name to assign to the class.
- The data is the data for the class, normally declared as variables.
- The functions are the class functions.
Private and Public Keywords
You must have come across these two keywords. They are access modifiers.
- Private:
When the private keyword is used to define a function or class, it becomes private. Such are only accessible from within the class.
- Public:
The public keyword, on the other hand, makes data/functions public. These are accessible from outside the class.
Object Definition
Objects are created from classes. Class objects are declared in a similar way as variables are declared. The class name must start, followed by the object name. The object of the class type.
Syntax:
Class-name object-name;
- The class-name is the name of the class from which an object is to be created.
- The object-name is the name to be assigned to the new object.
This process of creating an object from a class is known as instantiation.
Accessing Data Members
To access public members of a class, we use the (.)dot operator. These are members marked with public access modifier.
Example 1:
#include <iostream>
Using namespace std;
Class Phone {
Public:
Double cost;
Int slots;
};
Int main() {
Phone Y6;
Phone Y7;
Y6.cost = 100.0;
Y6.slots = 2;
Y7.cost = 200.0;
Y7.slots = 2;
Cout<< "Cost of Huawei Y6 : " << Y6.cost << endl;
Cout<< "Cost of Huawei Y7 : " << Y7.cost << endl;
Cout<< "Number of card slots for Huawei Y6 : " << Y6.slots << endl;
Cout<< "Number of card slots for Huawei Y7 : " << Y7.slots << endl;
Return 0;
}
Output:
Here is a screenshot of the code:
Code Explanation:
- Include the iostream header file in our code in order to use its functions.
- Including the std namespace in our code to use its classes without calling it.
- Declare a class named Phone.
- Using the public access modifier to mark the variables we are about to create as publicly accessible.
- Declare the variable cost of a double data type.
- Declare an integer variable named slots.
- End of the class body.
- Calling the main()function. The program logic should be added within its body.
- Create an object named Y6 of type Phone. This is called instantiation.
- Create an object named Y7 of type Phone. This is called instantiation.
- Access the variable/member cost of class Phone using the object Y6. The value is set to 100.0. The cost of Y6 is now set to 100.0.
- Access the variable/member slots of class Phone using the object Y6. The value is set to 2. The slots for Y6 is now set to 2.
- Access the variable/member cost of class Phone using the object Y7. The value is set to 200.0. The cost of Y7 is now set to 200.0.
- Access the variable/member slots of class Phone using the object Y7. The value is set to 2. The slots for Y7 is now set to 2.
- Print the cost of Y6 on the console alongside other text.
- Print the cost of Y7 on the console alongside other text.
- Print the number of slots for Y6 alongside other text.
- Print the number of slots for Y7 alongside other text.
- The program must return a value upon successful completion.
- End of the body of main() function.
What is a Private Class?
Class members marked as private can only be accessed by functions defined within the class. Any object or function defined outside the class cannot access such members directly. A private class member is only accessed by member and friend functions.
What is a Protected Class?
Class members marked as protected have an advantage over those marked as private. They can be accessed by functions within the class of their definition. Additionally, they can be accessed from derived classes.
Example 2:
#include <iostream>
Using namespace std;
Class ClassA {
Public:
Void set_a(int val);
Int get_a(void);
Private:
Int a;
};
Int ClassA::get_a(void) {
Return a;
}
Void ClassA::set_a(int val) {
a = val;
}
Int main() {
ClassA a;
a.set_a(20);
Cout<< "Value of a is: " << a.get_a();
Return 0;
}
Output:
Here is a screenshot of the code:
Code Explanation:
- Include the iostream header file in our code to use its functions.
- Include the std namespace in our code to use its classes without calling it.
- Create a class named ClassA.
- Use the public access modifier to mark the class member to be created as publicly accessible.
- Create the function named set_a() that takes one integer value val.
- Create a function named get_a().
- Use the private access modifier to mark the class member to be created as privately accessible.
- Declare an integer variable named a.
- End of the class body.
- Use the class name and the scope resolution operator to access the function get_a(). We want to define what the function does when invoked.
- The function get_a() should return the value of variable a when invoked.
- End of the definition of the function get_a().
- Use the class name and the scope resolution operator to access the function set_a(). We want to define what the function does when invoked.
- Assigning the value of the variable val to variable a.
- End of definition of the function set_a().
- Call the main() function. The program logic should be added within the body of this function.
- Create an instance of ClassA and give it the name a.
- Use the above class instance and the function set_a() to assign a value of 20 to the variable a.
- Printing some text alongside the value of variable a on the console. The value of variable a is obtained by calling the get_a() function.
- The program must return value upon successful completion.
- End of the body of function main().
Example 3:
#include <iostream>
Using namespace std;
Class ParentClass {
Protected:
Int value;
};
Class ChildClass : public ParentClass {
Public:
Void setId(int x) {
Value = x;
}
Void displayValue() {
Cout<< "Value is: " << value << endl;
}
};
Int main() {
ChildClass c;
c.setId(21);
c.displayValue();
Return 0;
}
Output:
Here is a screenshot of the code:
Code Explanation:
- Include the iostream header file in our code to use its functions.
- Include the std namespace in our code to use its classes without calling it.
- Create a class named ParentClass.
- Use the protected access modifier to mark the class member to be created as protected.
- Create an integer variable named value.
- End of the class body.
- Create a new class named ChildClass that inherits the ParentClass.
- Use the protected access modifier to mark the class member to be created as accessible to child classes.
- Create the function named setId() that takes one integer value x.
- Assigning the value of the variable x to the variable value.
- End of definition of the function setId().
- Create a function named displayValue().
- Print the value of the variable named value on the console alongside other text.
- End of the body of the function displayValue().
- End of the body of the class named ChildClass.
- Call the main() function. The program logic should be added within the body of this function.
- Create an instance of ChildClass and give it the name c.
- Use the above class instance and the function setId() to assign a value of 21 to the variable x.
- Use the above class instance to call the function named displayValue().
- The program must return value upon successful completion.
- End of the body of function main().
Class Member Functions
Functions help us manipulate data. Class member functions can be defined in two ways:
- Inside the class definition
- Outside the class definition
If a function is to be defined outside a class definition, we must use the scope resolution operator (::). This should be accompanied by the class and function names.
Example 2:
#include <iostream>
#include <string>
Using namespace std;
Class Guru99
{
Public:
String tutorial_name;
Int id;
Void printname();
Void printid()
{
Cout<< "Tutorial id is: "<< id;
}
};
Void Guru99::printname()
{
Cout<< "Tutorial name is: " << tutorial_name;
}
Int main() {
Guru99 guru99;
Guru99.tutorial_name = "C++";
Guru99.id = 1001;
Guru99.printname();
Cout<< endl;
Guru99.printid();
Return 0;
}
Output:
Here is a screenshot of the code:
Code Explanation:
- Include the iostream header file in our program to use its functions.
- Include the string header file in our program to use its functions.
- Include the std namespace in our code to use its classes without calling it.
- Create a class named Guru99.
- Use the public access modifier to mark the class members we are about to create as publicly accessible.
- Create a string variable named tutorial_name.
- Create an integer variable named id.
- Create a function named printname(). This function is not defined within the class definition.
- Create a function named printed(). This function is defined within the class definition. Its body has been added within the class definition.
- Print the value of variable id alongside other text on the console. Note this has been added within the body of printid() function. It will only be executed when the printid() function is called.
- End of the body of function printid().
- End of the body of the class Guru99.
- The start of definition of the function printname().
- Print the value of variable tutorial_name on the console alongside other text. Note this has been added within the body of printname() function. It will only be executed when the printname() function is called.
- End of the definition of printname() function.
- Call the main() function. The program logic should be added within the body of this function.
- Create an instance of class Guru99 and giving it the name guru99.
- Use the above instance to assign a value of C++ to the variable tutorial_name.
- Use the guru99 instance to assign a value of 1001 to the variable id.
- Use the instance guru99 to call the function printname() .
- Call the end (end line) command to print a new blank line on the console.
- Use the instance guru99 to call the function printid().
- The program must return value upon successful completion.
- End of the body of main() function.
Constructors and Destructors
What is Constructors?
Constructs are special functions that initialize objects. The C++ compilers calls a constructor when creating an object. The constructors help to assign values to class members. Of course, this is after they have been allocated some memory space.
What is Destructors?
Destructors on the other hand help to destroy class objects.
The constructor name must be similar to the class name. Constructors do not have a return type.
The constructor can be defined inside or outside the class body. If defined outside the class body, it should be defined with the class name and the scope resolution operator (::).
Example 3:
#include <iostream>
Using namespace std;
Class ClassA {
Public:
ClassA() {
Cout<< "Class constructor called"<<endl;
}
~ClassA() {
Cout<< "Class destructor called"<<endl;
}
};
Int main() {
ClassA a;
Int p = 1;
If (p) {
ClassA b;
}
}
Output:
Here is a screenshot of the code:
Code Explanation:
- Include the iostream header file into the code to use its functions.
- Include the std namespace in our code to use its classes without calling it.
- Create a class named ClassA.
- Use the public access modifier to mark the member we are about to create as publicly accessible.
- Create a constructor for the class.
- Text to print on the console when the constructor is called. The endl is a C++ keyword, which means end line. It moves the mouse cursor to the next line.
- End of the body of the class constructor.
- Create a destructor for the class.
- Text to print on the console when the destructor is called. The endl is a C++ keyword, which means end line. It moves the mouse cursor to the next line.
- End of the body of the destructor.
- End of the class body.
- Call the main() function. The program logic should be added within the body of this function.
- Create a class object and give it the name a. The constructor will be called.
- Create an integer variable named p and assign it a value of 1.
- Create an if statement block using the variable p.
- Create a class object and give it the name b. The destructor will be called.
- End of the body of the if statement.
- End of the body of the main() function.
Summary:
- C++ is object-oriented.
- Classes form the main features of C++ that make it object-oriented.
- A C++ class combines data and methods for manipulating the data into one.
- A class is a blueprint for an object.
- Classes determine the form of an object.
- The data and methods contained in a class are known as class members.
- To access class members, you should use an instance of the class.
- To create a class, we use the class keyword.
- The class member functions can be defined inside or outside a class.
References:
1. Object-Oriented Programming and Java by Danny Poo (Author), Derek Kiong (Author), Swarnalatha Ashok (Author)Springer; 2nd ed. 2008 edition (12 October 2007), ISBN-10: 1846289629, ISBN-13: 978-1846289620,2007
2. Java The complete reference, 9th edition, Herbert Schildt, McGraw Hill Education (India) Pvt. Ltd.
3. Object-Oriented Design Using Java, Dale Skrien, McGraw-Hill Publishing, 2008, ISBN - 0077423097, 9780077423094. 4. UML for Java Programmers by Robert C. Martin, Prentice Hall, ISBN 0131428489,2003.