Unit 4
Unit 4
UNIT-4
These are just a few examples. The C++ Standard Library offers many more classes and
algorithms to simplify programming tasks.
PERSISTANT OBJECT
In C++, a persistent object typically refers to an object whose lifetime extends beyond the
scope or duration of the function or block in which it is created. There are different ways to
create persistent objects in C++, depending on the desired scope and storage duration. Here
are a few options:
1. Static Storage Duration:
Objects with static storage duration are created and initialized only once during the
program's lifetime.
They persist throughout the entire program execution.
You can create a static object using the static keyword outside of any function or
class.
Example of a static object with global scope
#include <iostream>
class PersistentObject {
public:
PersistentObject() {
std::cout << "PersistentObject constructor\n";
}
~PersistentObject() {
std::cout << "PersistentObject destructor\n";
}
};
int main() {
std::cout << "Inside main function\n";
// The global Object will persist throughout the program
return 0;
}
#include <iostream>
class PersistentObject {
public:
PersistentObject() {
std::cout << "PersistentObject constructor\n";
}
~PersistentObject() {
std::cout << "PersistentObject destructor\n";
}
};
int main() {
std::cout << "Inside main function\n";
// Dynamic object created on the heap
PersistentObject *dynamicObject = new PersistentObject();
3. File Storage:
You can use files to persistently store data between program executions.
Read and write data to a file to achieve persistence.
#include <iostream>
#include <fstream>
class PersistentObject {
public:
PersistentObject() {
std::cout << "PersistentObject constructor\n";
}
~PersistentObject() {
std::cout << "PersistentObject destructor\n";
}
void saveToFile() {
std::ofstream file("persistent_data.txt");
file << "Some data related to the object\n";
file.close();
}
void loadFromFile() {
std::ifstream file("persistent_data.txt");
if (file.is_open()) {
std::string data;
std::getline(file, data);
std::cout << "Loaded data: " << data << "\n";
file.close();
}
}
};
int main() {
std::cout << "Inside main function\n";
PersistentObject persistentObject;
persistentObject.saveToFile();
persistentObject.loadFromFile();
return 0;
}
So far, we have been using the iostream standard library, which provides cin and cout
methods for reading from standard input and writing to standard output respectively.
This topic will teach you how to read and write from a file. This requires another standard
C++ library called fstream, which defines three new data types −
ofstream
1 This data type represents the output file stream and is used to create files and to write
information to files.
ifstream
2
This data type represents the input file stream and is used to read information from files.
fstream
This data type represents the file stream generally, and has the capabilities of both ofstream
3
and ifstream which means it can create files, write information to files, and read information
from files.
To perform file processing in C++, header files <iostream> and <fstream> must be included
in your C++ source file.
Opening a File
A file must be opened before you can read from it or write to it. Either ofstream or fstream
object may be used to open a file for writing. And ifstream object is used to open a file for
reading purpose only.
Following is the standard syntax for open() function, which is a member of fstream, ifstream,
and ofstream objects.
ios::app
1
Append mode. All output to that file to be appended to the end.
ios::ate
2
Open a file for output and move the read/write control to the end of the file.
ios::in
3
Open a file for reading.
ios::out
4
Open a file for writing.
ios::trunc
5
If the file already exists, its contents will be truncated before opening the file.
You can combine two or more of these values by ORing them together. For example if you
want to open a file in write mode and want to truncate it in case that already exists, following
will be the syntax –
ofstream outfile;
outfile.open("file.dat", ios::out | ios::trunc );
Similar way, you can open a file for reading and writing purpose as follows −
fstream afile;
afile.open("file.dat", ios::out | ios::in );
Closing a File
When a C++ program terminates it automatically flushes all the streams, release all the
allocated memory and close all the opened files. But it is always a good practice that a
programmer should close all the opened files before program termination.
Following is the standard syntax for close() function, which is a member of fstream, ifstream,
and ofstream objects.
void close();
Writing to a File
While doing C++ programming, you write information to a file from your program using the
stream insertion operator (<<) just as you use that operator to output information to the
screen. The only difference is that you use an ofstream or fstream object instead of the cout
object.
#include <fstream>
#include <iostream>
using namespace std;
int main () {
char data[100];
// open a file in write mode.
ofstream outfile;
outfile.open("afile.dat");
cout << "Writing to the file" << endl;
cout << "Enter your name: ";
cin.getline(data, 100);
// again read the data from the file and display it.
infile >> data;
cout << data << endl;
return 0;
}
When the above code is compiled and executed, it produces the following sample input and
output −
$./a.out
Writing to the file
Enter your name: XYZ
Enter your age: 9
Reading from the file
XYZ
9
The argument to seekg and seekp normally is a long integer. A second argument can be
specified to indicate the seek direction. The seek direction can be ios::beg (the default) for
positioning relative to the beginning of a stream, ios::cur for positioning relative to the
current position in a stream or ios::end for positioning relative to the end of a stream.
The file-position pointer is an integer value that specifies the location in the file as a number
of bytes from the file's starting location. Some examples of positioning the "get" file-position
pointer are −
Namespace provide the space where we can define or declare identifier i.e., variable,
method, classes.
Using namespace, you can define the space or context in which identifiers are defined
i.e. variable, method, classes. In essence, a namespace defines a scope.
Advantage of Namespace to avoid name collision.
Example, you might be writing some code that has a function called xyz() and there is
another library available which is also having same function xyz(). Now the compiler has
no way of knowing which version of xyz() function you are referring to within your
code.
A namespace is designed to overcome this difficulty and is used as additional
information to differentiate similar functions, classes, variables etc. with the same name
available in different libraries.
The best example of namespace scope is the C++ standard library (std) where all the
classes, methods and templates are declared. Hence while writing a C++ program we
usually include the directive using namespace std;
Defining a Namespace:
A namespace definition begins with the keyword namespace followed by the namespace
name as follows:
namespace namespace_name
{
// code declarations i.e. variable (int a;)
method (void add();)
classes ( class student{};)
}
You can also avoid prepending of namespaces with the using namespace directive. This
directive tells the compiler that the subsequent code is making use of names in the
specified namespace.
The namespace is thus implied for the following code:
#include <iostream>
using namespace std;
// first name space
namespace first_space
{
void func()
{
cout << "Inside first_space" << endl;
}
}
Output:
Inside first_space
Names introduced in a using directive obey normal scope rules. The name is visible from
the point of the using directive to the end of the scope in which the directive is found.
Entities with the same name defined in an outer scope are hidden.
Nested Namespaces:
Namespaces can be nested where you can define one namespace inside another
namespace as follows:
SYNTAX:
namespace namespace_name1
{
// code declarations
namespace namespace_name2
{
// code declarations
}
}
You can access members of nested namespace by using resolution operators as follows:
In the above statements if you are using namespace_name1, then it will make elements
of namespace_name2 available in the scope as follows:
#include <iostream>
using namespace std;
return 0;
}
Output:
Inside second_space
Let us see how namespace scope the entities including variable and functions:
#include <iostream>
using namespace std;
int main ()
{
// Calls function from first name space.
first_space :: func();
Inside first_space
Inside second_space
int main()
{
int value;
value = 0;
double value; // Error here
value = 0.0;
}
Output:
Compiler Error:
'value' has a previous declaration as 'int value'
In each scope, a name can only represent one entity. So, there cannot be two variables
with the same name in the same scope. Using namespaces, we can create two variables
or member functions having the same name.
#include <iostream>
using namespace std;
// Global variable
int val = 100;
int main()
{
// Local variable
int val = 200;
return 0;
}
Output:
500
Definition and Creation:
Namespaces allow us to group named entities that otherwise would have global scope
into narrower scopes, giving them namespace scope. This allows organizing the elements
of programs into different logical scopes referred to by names. Namespaces provide the
space where we can define or declare identifiers i.e. names of variables, methods,
classes, etc.
namespace namespace_name
{
int x, y;
// code declarations where
// x and y are declared in
// namespace_name's scope
}
Defining a Namespace:
A namespace definition begins with the keyword namespace followed by the namespace
name as follows:
namespace namespace_name{
// code declarations i.e. variable (int a;)
method (void add();)
classes ( class student{};)
}
Let us see how namespace scope the entities including variable and functions:
#include <iostream>
using namespace std;
int main ()
{
// Calls function from first name space.
first_space::func();
// If we compile and run above code, this would produce the following result:
// Inside first_space
// Inside second_space
Output
Inside first_space
Inside second_space
The using directive:
You can avoid prepending of namespaces with the using namespace directive. This
directive tells the compiler that the subsequent code is making use of names in the
specified namespace. The namespace is thus implied for the following code:
#include <iostream>
using namespace std;
int main ()
{
// This calls function from first name space.
func();
return 0;
}
// If we compile and run above code, this would produce the following result:
// Inside first_space
Output:
Inside first_space
Instead of accessing the whole namespace, another option (known as using declaration)
is to access a particular item within a namespace. For example, if the only part of the std
namespace that you intend to use is cout, you can refer to it as follows:
using std::cout;
Subsequent code can refer to cout without prepending the namespace, but other items in
the std namespace will still need to be explicit as follows:
#include <iostream>
using std::cout;
int main ()
{
cout << "std::endl is used with std!" << std::endl;
return 0;
}
Output:
Names introduced in a using directive obey normal scope rules, i.e., they are visible from
the point the using directive occurs to the end of the scope in which the directive is
found. Entities with the same name defined in an outer scope are hidden.
Creating namespaces
#include <iostream>
using namespace std;
// namespace ns1
namespace ns1 {
int value() { return 5; }
}
// namespace ns2
namespace ns2 {
const double x = 100;
double value() {
return 2 * x;
}
}
int main()
{
// Access value function within ns1
cout << ns1::value() << '\n';
// Access value function within ns2
cout << ns2::value() << '\n';
return 0;
}
Output:
5
200
100
#include<iostream>
using namespace std;
namespace ns
{
// A Class in a namespace
class geek
{
public:
void display()
{
cout<<"ns::geek::display()"<<endl;;
}
};
}
int main()
{
// Creating Object of geek Class
ns::geek obj;
obj.display();
return 0;
}
Output
ns::geek::display()
A class can also be declared inside namespace and defined outside namespace using the
following syntax:
#include <iostream>
using namespace std;
// namespace ns
namespace ns {
// Only declaring class here
class geek;
}
int main()
{
// Creating Object of geek Class
ns::geek obj;
obj.display();
return 0;
}
Output
ns::geek::display()
We can define methods as well outside the namespace. The following is an example
code:
A code to demonstrate that we can define methods outside namespace.
#include <iostream>
using namespace std;
// Creating a namespace
// namespace ns
namespace ns {
void display();
class geek {
public:
void display();
};
}
// Driver code
int main()
{
ns::geek obj;
ns::display();
obj.display();
return 0;
}
Output:
ns::display()
ns::geek::display():
Nested Namespaces:
Namespaces can be nested, i.e., you can define one namespace inside another namespace
as follows:
namespace namespace_name1 {
// code declarations
namespace namespace_name2 {
// code declarations
}
}
You can access the members of a nested namespace by using the resolution operator (::)
as follows:
In the above statements, if you are using namespace_name1, then it will make the
elements of namespace_name2 available in the scope as follows:
#include <iostream>
using namespace std;
return 0;
}
// If we compile and run above code, this would produce the following result:
// Inside second_space
Output:
Inside second_space
For example, you might be writing some code that has a function called xyz() and there
is another library available in your code which also has the same function xyz(). Now the
compiler has no way of knowing which version of xyz() function you are referring to
within your code.
A namespace is designed to overcome this difficulty and is used as additional
information to differentiate similar functions, classes, variables, etc. with the same name
available in different libraries.
The best example of namespace scope is the C++ standard library (std), where all the
classes, methods and templates are declared. Hence, while writing a C++ program, we
usually include the directive
using namespace std;
EXCEPTION HANDLING
One of the advantages of C++ over C is Exception Handling. Exceptions are runtime
anomalies or abnormal conditions that a program encounters during its execution.
There are two types of exceptions: a)Synchronous, b)Asynchronous (i.e., exceptions
which are beyond the program’s control, such as disc failure, keyboard interrupts etc.).
C++ provides the following specialized keywords for this purpose:
The following are the main advantages of exception handling over traditional error
handling:
In traditional error handling codes, there are always if-else conditions to handle
errors. These conditions and the code to handle errors get mixed up with the normal
flow. This makes the code less readable and maintainable. With try/catch blocks, the
code for error handling becomes separate from the normal flow.
A function can throw many exceptions, but may choose to handle some of them.
The other exceptions, which are thrown but not caught, can be handled by the caller.
If the caller chooses not to catch them, then the exceptions are handled by the caller
of the caller.
A function can specify the exceptions that it throws using the throw keyword. The
caller of this function must handle the exception in some way (either by specifying
it again or catching it).
3) Grouping of Error Types:
Both basic types and objects can be thrown as exceptions. We can create a hierarchy
of exception objects, group exceptions in namespaces or classes and categorize them
according to their types.
C++ Exceptions:
When executing C++ code, different errors can occur: coding errors made by the
programmer, errors due to wrong input, or other unforeseeable things.
When an error occurs, C++ will normally stop and generate an error message. The technical
term for this is: C++ will throw an exception (error).
C++ try and catch:
Exception handling in C++ consists of three keywords: try, throw and catch:
The try statement allows you to define a block of code to be tested for errors while it is
being executed.
The throw keyword throws an exception when a problem is detected, which lets us create a
custom error.
The catch statement allows you to define a block of code to be executed if an error occurs
in the try block.
The try and catch keywords come in pairs:
We use the try block to test some code: If the value of a variable “age” is less than 18, we
will throw an exception, and handle it in our catch block.
In the catch block, we catch the error if it occurs and do something about it. The catch
statement takes a single parameter. So, if the value of age is 15 and that’s why we are
throwing an exception of type int in the try block (age), we can pass “int myNum” as the
parameter to the catch statement, where the variable “myNum” is used to output the value
of age.
If no error occurs (e.g. if age is 20 instead of 15, meaning it will be greater than 18), the
catch block is skipped.
Exception Handling in C++
1) The following is a simple example to show exception handling in C++. The output of the
program explains the flow of execution of try/catch blocks.
#include <iostream>
using namespace std;
int main()
{
int x = -1;
// Some code
cout << "Before try \n";
try {
cout << "Inside try \n";
if (x < 0)
{
throw x;
cout << "After throw (Never executed) \n";
}
}
catch (int x ) {
cout << "Exception Caught \n";
}
cout << "After catch (Will be executed) \n";
return 0;
}
GENERIC CLASSES:
Generics is the idea to allow type (Integer, String, … etc and user-defined types) to be a
parameter to methods, classes and interfaces. For example, classes like an array, map, etc,
which can be used using generics very efficiently. We can use them for any type.
#include <iostream>
using namespace std;
T myMax(T x, T y)
{
return (x > y) ? x : y;
}
int main()
{
return 0;
}
Output:
7
7
g
public:
Array(T arr[], int s);
void print();
};
int main()
{
int arr[5] = { 1, 2, 3, 4, 5 };
Array<int> a(arr, 5);
a.print();
return 0;
}
Output:
12345
#include <iostream>
using namespace std;
public:
A()
{
cout << "Constructor Called" << endl;
}
};
int main()
{
A<char, char> a;
A<int, double> b;
return 0;
}
Output:
Constructor Called
Constructor Called
The C++ Standard Template Library (STL) is a collection of algorithms, data structures,
and other components that can be used to simplify the development of C++ programs.
The STL provides a range of containers, such as vectors, lists, and maps, as well as
algorithms for searching, sorting and manipulating data.
One of the key benefits of the STL is that it provides a way to write generic, reusable
code that can be applied to different data types. This means that you can write an
algorithm once, and then use it with different types of data without having to write
separate code for each type.
The STL also provides a way to write efficient code. Many of the algorithms and data
structures in the STL are implemented using optimized algorithms, which can result in
faster execution times compared to custom code.
By using the STL, you can simplify your code, reduce the likelihood of errors, and
improve the performance of your programs.
1. Algorithms
The header algorithm defines a collection of functions specially designed to be used on a
range of elements. They act on containers and provide means for various operations for
the contents of the containers.
Algorithm
Sorting
Searching
Important STL Algorithms
Useful Array algorithms
Partition Operations
Numeric
valarray class
2. Containers
Containers or container classes store objects and data. There are in total seven standards
“first-class” container classes and three container adaptor classes and only seven header
files that provide access to these containers or container adaptors.
4. Iterators
As the name suggests, iterators are used for working on a sequence of values. They are the
major feature that allows generality in STL.
Utility Library
Defined in header <utility>.
MANIPULATORS
Manipulators are helping functions that can modify the input/output stream. It does not
mean that we change the value of a variable, it only modifies the I/O stream using
insertion (<<) and extraction (>>) operators.
Manipulators are special functions that can be included in the I/O statement to alter the
format parameters of a stream.
Manipulators are operators that are used to format the data display.
To access manipulators, the file iomanip.h should be included in the program.
For example, if we want to print the hexadecimal value of 100 then we can print it as:
cout<<setbase(16)<<100
Types of Manipulators
There are various types of manipulators:
#include <iostream>
#include <istream>
#include <sstream>
#include <string>
using namespace std;
int main()
{
istringstream str(" Programmer");
string line;
// Ignore all the whitespace in string
// str before the first word.
getline(str >> std::ws, line);
return 0;
}
Output:
Programmer
only a test
abc
2.Manipulators with Arguments: Some of the manipulators are used with the
argument like setw (20), setfill (‘*’), and many more. These all are defined in the
header file. If we want to use these manipulators then we must include this header file
in our program. For Example, you can use following manipulators to set minimum
width and fill the empty space with any character you want: std::cout << std::setw (6)
<< std::setfill (’*’);
1] Parameterized Manipulators:-
Manipulator -> Meaning
setw (int n) -> To set field width to n
setprecision (int p) -> The precision is fixed to p
setfill (Char f) -> To set the character to be filled
setiosflags (long l) -> Format flag is set to l
resetiosflags (long l) -> Removes the flags indicated by l
Setbase(int b) -> To set the base of the number to b
The manipulators hex, oct, and dec can change the basis of input and output numbers.
// Example:
#include <iostream>
#include <iomanip>
}
Output
Hex Value = 64
Octal Value= 144
Setbase Value= 144
Setbase Value= 64
2] Non-parameterized
Examples are endl, fixed, showpoint and flush.
• endl – Gives a new line
• ends – Adds null character to close an output string
• flush – Flushes the buffer stream
• ws – Omits the leading white spaces present before the first field
• hex, oct, dec – Displays the number in hexadecimal or octal or in decimal format
// Example: ws – Omits the leading white spaces present before the first field
#include<iostream>
using namespace std;
int main()
{
char name[125];
cout << "Enter your name" << endl;
cin >> ws;
cin.getline(name,125);
cout << name << endl;
return 0;
}
Output:
Enter your name
ram
ram
VECTORS
Vectors are the same as dynamic arrays with the ability to resize itself automatically
when an element is inserted or deleted, with their storage being handled automatically by
the container. Vector elements are placed in contiguous storage so that they can be
accessed and traversed using iterators. In vectors, data is inserted at the end. Inserting at
the end takes differential time, as sometimes the array may need to be extended.
Removing the last element takes only constant time because no resizing happens.
Inserting and erasing at the beginning or in the middle is linear in time.
int main()
{
vector<int> g1;
return 0;
}
Output:
Output of begin and end: 1 2 3 4 5
Output of cbegin and cend: 1 2 3 4 5
Output of rbegin and rend: 5 4 3 2 1
Output of crbegin and crend : 5 4 3 2 1
Capacity
size() – Returns the number of elements in the vector.
max_size() – Returns the maximum number of elements that the vector can hold.
capacity() – Returns the size of the storage space currently allocated to the vector
expressed as number of elements.
resize(n) – Resizes the container so that it contains ‘n’ elements.
empty() – Returns whether the container is empty.
shrink_to_fit() – Reduces the capacity of the container to fit its size and destroys all
elements beyond the capacity.
reserve() – Requests that the vector capacity be at least enough to contain n elements.
Output:
Size : 5
Capacity : 8
Max_Size : 4611686018427387903
Size : 4
Vector is not empty
Vector elements are: 1 2 3 4
Element access
reference operator [g] – Returns a reference to the element at position ‘g’ in the vector
at(g) – Returns a reference to the element at position ‘g’ in the vector
front() – Returns a reference to the first element in the vector
back() – Returns a reference to the last element in the vector
data() – Returns a direct pointer to the memory array used internally by the vector to
store its owned elements.
int main()
{
vector<int> g1;
Output:
Reference operator [g] : g1[2] = 30
at : g1.at(4) = 50
front() : g1.front() = 10
back() : g1.back() = 100
The first element is 10
Modifiers
assign() – It assigns new value to the vector elements by replacing old ones
push_back() – It push the elements into a vector from the back
pop_back() – It is used to pop or remove elements from a vector from the back.
insert() – It inserts new elements before the element at the specified position
erase() – It is used to remove elements from a container from the specified position or
range.
swap() – It is used to swap the contents of one vector with another vector of same type.
Sizes may differ.
clear() – It is used to remove all the elements of the vector container
emplace() – It extends the container by inserting new element at position
emplace_back() – It is used to insert a new element into the vector container, the new
element is added to the end of the vector
int main()
{
// Assign vector
vector<int> v;
// Swaps v1 and v2
v1.swap(v2);
Vector 1: 1 2
Vector 2: 3 4
After Swap
Vector 1: 3 4
Vector 2: 1 2