OOPs Unit-4 and Unit-5 Notes
OOPs Unit-4 and Unit-5 Notes
class Example {
public:
const int x; // Constant data member
int main() {
Example obj(5);
obj.show();
// obj.x = 10; // This will cause a compile-time error
return 0;
}
Explanation:
• x is a constant data member.
• It is initialized through the constructor Example(int val).
• Attempting to modify x (e.g., obj.x = 10;) will result in a compilation error.
class Example {
public:
int y;
int main() {
Example obj(5);
obj.show(); // Allowed as show() is a constant function
obj.increment();
obj.show(); // Outputs incremented value
return 0;
}
Explanation:
• show() is a constant member function because it is marked with const after the function definition.
• It cannot modify any data members, so attempting to increment y inside show() results in an error.
• increment() is a non-const member function and can modify the y value.
Summary
• Constant Data Member: Cannot be modified after initialization.
• Constant Member Function: Ensures that it cannot modify any data members of the class.
class BankAccount {
public:
static int totalAccounts; // Static data member
BankAccount() {
totalAccounts++; // Increment whenever a new account is created
}
};
int main() {
BankAccount acc1;
BankAccount acc2;
cout << "Total accounts: " << BankAccount::totalAccounts << endl; // Output: 2
return 0;
}
Here, totalAccounts is a static data member, and it keeps track of the total number of BankAccount objects
created. Since it is static, it is shared among all instances (acc1 and acc2).
Example: Extending the BankAccount class with a static function to get the total accounts:
class BankAccount {
public:
static int totalAccounts;
BankAccount() {
totalAccounts++;
}
C. Real-life Analogy:
Think of a static data member like a common counter in a bank that records the number of accounts. Every
time a new account is opened (an object is created), the counter increases, but there’s only one counter for all.
A static member function is like a public display that shows the number of accounts without needing to access
individual accounts.
3. Polymorphism
Polymorphism in C++ is a fundamental concept in object-oriented programming (OOP) that allows a function
or an object to take on many forms. It enables a single interface to represent different underlying forms (data
types). Polymorphism is key to flexibility and extensibility in OOP and is broadly classified into compile-time
(static) and runtime (dynamic) polymorphism.
A. 1. Compile-time Polymorphism:
Compile-time polymorphism is achieved through function overloading and operator overloading. It is
resolved during the compilation of the program.
1) Function Overloading:
This allows multiple functions with the same name but different parameters. The compiler determines which
function to call based on the arguments used during the function call.
Example: Function Overloading
#include <iostream>
Prepared By: - Akshay Arya, Assistant Professor of AIDS Department
using namespace std;
class Printer {
public:
void print(int num) {
cout << "Printing an integer: " << num << endl;
}
void print(double num) {
cout << "Printing a double: " << num << endl;
}
void print(string str) {
cout << "Printing a string: " << str << endl;
}
};
int main() {
Printer p;
p.print(5); // Calls the version with an integer parameter
p.print(5.5); // Calls the version with a double parameter
p.print("Hello"); // Calls the version with a string parameter
return 0;
}
In this example, the print function is overloaded with different parameter types. Depending on the argument
type, the appropriate version of print is called.
2) Operator Overloading:
This allows existing C++ operators to be redefined to work with user-defined data types.
For example, the + operator can be overloaded to add two complex numbers.
Example: Operator Overloading
#include <iostream>
using namespace std;
class Complex {
public:
int real, imag;
Complex(int r = 0, int i = 0) : real(r), imag(i) {}
int main() {
Complex c1(5, 3), c2(2, 4);
Complex c3 = c1 + c2; // Uses overloaded '+' operator
cout << "Result: " << c3.real << " + " << c3.imag << "i" << endl;
return 0;
}
In this example, the + operator is overloaded to add two Complex numbers, which results in compile-time
polymorphism.
B. Runtime Polymorphism:
Runtime polymorphism is achieved through inheritance and virtual functions, where a base class pointer or
reference is used to refer to derived class objects. It is resolved during program execution.
#include <iostream>
using namespace std;
class Animal {
public:
virtual void sound() {
cout << "Animal makes a sound" << endl;
}
};
int main() {
Animal* a;
Dog d;
Cat c;
a = &d;
a->sound(); // Outputs: Dog barks
a = &c;
a->sound(); // Outputs: Cat meows
return 0;
}
In this example, the sound() function is declared as virtual in the Animal base class. When a base class pointer
a points to Dog and Cat objects, the overridden sound() function of the derived class is called. This is
dynamic polymorphism.
C. Real-life Analogy:
Think of polymorphism like a remote control that can operate multiple devices. For instance, a TV remote can
control a TV, but if it has universal functions, it can also operate a sound system or a DVD player. The remote
remains the same (interface), but the way it functions (underlying behavior) changes based on the device it is
controlling. In C++, the base class (Animal) acts like the remote, and derived classes (Dog, Cat) are like
different devices. The sound() function behaves differently depending on the actual object being referenced.
4. Operator overloading
Operator overloading is an essential feature in C++ that allows operators to be redefined to perform
specific tasks for user-defined data types. It essentially enables operators to work with custom
classes and structures in a way similar to primitive types, enhancing code readability, flexibility, and
modularity. Operator overloading is an integral part of Object-Oriented Programming in C++, as it
allows operators to be used naturally with objects while adhering to principles of encapsulation and
abstraction.
• Arithmetic Operators: +, -, *, /, %
• Relational Operators: ==, !=, <, >, <=, >=
• Logical Operators: &&, ||, !
• Bitwise Operators: &, |, ^, ~, <<, >>
• Assignment Operators: =, +=, -=, *=, /=, %= etc.
• Increment/Decrement Operators: ++, --
#include <iostream>
using namespace std;
class Check
{
private:
int i;
public:
Check(): i(0) { }
void operator ++()
{ ++i; }
void Display()
{ cout << "i=" << i << endl; }
};
int main()
{
Check obj;
#include <iostream>
using namespace std;
class Check
{
private:
int i;
public:
Check(): i(0) { }
Check operator ++ ()
{
Check temp;
temp.i = ++i;
return temp;
}
void Display()
{ cout << "i = "<< i <<endl; }
};
int main()
{
Check obj, obj1;
obj.Display();
obj1.Display();
return 0;
}
Output
#include <iostream>
using namespace std;
class Number {
private:
int value;
public:
// Constructor
Number(int v = 0) : value(v) {}
int main() {
Number num(5);
// Prefix increment
++num;
#include <iostream>
using namespace std;
class Number {
private:
int value;
public:
// Constructor
Number(int v = 0) : value(v) {}
int main() {
Number num(5);
// Postfix increment
Number temp = num++;
return 0;
}
Output
Initial Value: 5
Prepared By: - Akshay Arya, Assistant Professor of AIDS Department
After postfix increment, original Value: 6
Value of temp (before increment) Value: 5
#include <iostream>
using namespace std;
class Number {
private:
int value;
public:
// Constructor
Number(int v = 0) : value(v) {}
// Overload + operator
Number operator+(const Number &obj) {
return Number(value + obj.value); // Add the values of two objects
}
int main() {
Number num1(10), num2(20);
return 0;
}
Output
Number 1: Value: 10
Number 2: Value: 20
Sum: Value: 30
class String {
private:
string value;
public:
// Constructor
String(const string &v = "") : value(v) {}
int main() {
String str1("Hello"), str2("World");
return 0;
}
Output
First String: Hello
Second String: World
Concatenated String: Hello World
class Number {
private:
int value;
public:
// Constructor
Number(int v = 0) : value(v) {}
int main() {
Number num1(15), num2(25);
return 0;
}
Output
Number 1: Value: 15
Number 2: Value: 25
Sum: Value: 40
Dynamic binding is essential when you want to work with derived classes through base class
pointers or references, and you want the program to automatically select the overridden function in
the derived class.
class Base {
public:
virtual void show() {
cout << "Base class show function called" << endl;
}
};
In this example, show is a virtual function in the Base class, and derived classes can override it.
When called through a pointer or reference of the base type, the derived class version will be
invoked, thanks to dynamic binding.
#include <iostream>
using namespace std;
class Base {
public:
// Virtual function
virtual void display() {
cout << "Display from Base class" << endl;
}
// Non-virtual function
void show() {
cout << "Show from Base class" << endl;
}
// Non-virtual function
void show() {
cout << "Show from Derived class" << endl;
}
};
int main() {
Base *basePtr; // Base class pointer
Derived derivedObj; // Derived class object
basePtr = &derivedObj;
return 0;
}
Output
Using virtual function:
Display from Derived class
Using non-virtual function:
Show from Base class
When a virtual function is called through a base class pointer, C++ looks up the function in the
vtable of the actual object and calls the appropriate function.
1. vtable: An array of function pointers where each entry points to a virtual function that can be called
by the class.
2. vptr: A hidden pointer in each object that points to the vtable of the object’s class.
class Animal {
public:
virtual void sound() = 0; // Pure virtual function
};
// Abstract class
class Shape {
public:
// Pure virtual function
virtual void area() const = 0;
public:
Rectangle(double l, double w) : length(l), width(w) {}
public:
Circle(double r) : radius(r) {}
int main() {
Shape* shapePtr; // Pointer to the abstract class
// Rectangle
Rectangle rect(10.5, 5.5);
shapePtr = ▭
cout << "Rectangle details:" << endl;
shapePtr->area();
// Circle
Circle circ(7.0);
shapePtr = ˆ
cout << "\nCircle details:" << endl;
shapePtr->area();
return 0;
}
Output
Rectangle details:
Area of Rectangle: 57.75
Circle details:
Area of Circle: 153.938
In this example:
I. Virtual Destructors
When you delete a derived class object through a base class pointer, C++ requires a virtual
destructor to ensure that the derived class’s destructor is called. If the base class destructor is not
virtual, only the base class’s destructor will be called, leading to resource leaks.
#include <iostream>
using namespace std;
class Base {
public:
Base() {
cout << "Base constructor called." << endl;
}
virtual ~Base() {
cout << "Base destructor called." << endl;
}
};
~Derived() override {
cout << "Derived destructor called." << endl;
}
};
int main() {
// Base class pointer pointing to a Derived class object
Base* basePtr = new Derived();
return 0;
}
Output-
Base constructor called.
Derived constructor called.
Derived destructor called.
In conclusion, dynamic binding and virtual functions enable C++ to implement polymorphism
effectively, supporting flexibility and modularity in code. They make it possible for a single function
call to operate on objects of various derived types, making programs easier to extend and maintain
while also improving code reusability.
A. Exception:
An exception is an event that occurs during the execution of a program and disrupts the normal flow of
instructions. For example, division by zero, invalid memory access, or file not found.
catch (exception) {
// code to handle exception
}
Here, we have placed the code that might generate an exception inside the try block. Every try block is
followed by the catch block.
When an exception occurs, the throw statement throws an exception, which is caught by the catch block.
The catch block cannot be used without the try block.
#include <iostream>
using namespace std;
int main() {
try {
return 0;
}
Output 1
Enter numerator: 72
Prepared By: - Akshay Arya, Assistant Professor of AIDS Department
Enter denominator: 0
Error: Cannot divide by 0
Output 2
Enter numerator: 72
Enter denominator: 3
72 / 3 = 24
Explanation
The above program divides two numbers and displays the result. But an exception occurs if the denominator
is 0.
To handle the exception, we have put the code divide = numerator / denominator; inside the try block. Now,
when an exception occurs, the rest of the code inside the try block is skipped.
The catch block catches the thrown exception and executes the statements inside it.
If none of the statements in the try block generates an exception, the catch block is skipped.
int main() {
try {
throw "Unknown exception";
}
catch (const char* msg) {
cout << "Caught string exception: " << msg << endl;
}
catch (...) {
cout << "Caught an unknown exception" << endl;
}
return 0;
}
Output
Caught string exception: Unknown exception
• C++ Multiple catch Statements
In C++, we can use multiple catch statements for different kinds of exceptions that can result from a single
block of code.
try {
// code
}
catch (exception1) {
// code
}
catch (exception2) {
// code
}
catch (...) {
// code
}
Here, our program catches exception1 if that exception occurs. If not, it will catch exception2 if it occurs.
If there is an error that is neither exception1 nor exception2, then the code inside of catch (...) {} is executed.
Notes:
• catch (...) {} should always be the final block in our try...catch statement. This is because this block
catches all possible exceptions and acts as the default catch block.
• It is not compulsory to include the default catch block in our code.
int main() {
try {
return 0;
}
Output 1
Enter array index: 5
Error: Array out of bounds!
Explanation
Here, the array arr only has 4 elements. So, index cannot be greater than 3.
In this case, index is 5. So we throw a string literal "Error: Array out of bounds!". This exception is caught by
the first catch block.
int main() {
try {
throw MyException();
}
catch (const MyException& e) {
cout << e.what() << endl;
}
return 0;
}
Output
Custom exception occurred!
1) Syntax of Rethrowing
catch (exception_type &e) {
// Handle the exception partially (logging, cleanup, etc.)
void function1() {
try {
// Simulate an error
throw runtime_error("Error occurred in function1");
} catch (const runtime_error& e) {
cout << "Caught in function1: " << e.what() << endl;
void function2() {
try {
function1();
} catch (const runtime_error& e) {
cout << "Caught in function2: " << e.what() << endl;
}
int main() {
try {
function2();
} catch (const runtime_error& e) {
cout << "Caught in main: " << e.what() << endl;
}
return 0;
}
Output
Caught in function1: Error occurred in function1
Caught in function2: Error occurred in function1
Caught in main: Error occurred in function1
Explanation of the Example
1. function1:
o In function1(), we simulate an error by throwing a runtime_error with a message "Error
occurred in function1".
o The exception is caught in the catch block of function1, where we log the message "Caught in
function1".
o After handling the error partially, we use throw; to rethrow the exception to the next level.
2. function2:
o function2() calls function1() inside a try block.
o When function1() rethrows the exception, it is caught again in the catch block of function2.
o The message "Caught in function2" is printed.
3. main:
o The main() function calls function2().
o When the exception is rethrown from function2(), it is finally caught in the main() function's
catch block.
o The message "Caught in main" is printed.
3) Limitations of Rethrowing
1. No Exception Type Change: The exception that is rethrown must be of the same type as the one that
was caught. You cannot change the type of an exception after it has been caught.
2. Handling Multiple Exceptions: If multiple exceptions are thrown and caught, it can sometimes
become complex to rethrow and handle each one in a structured way.
Prepared By: - Akshay Arya, Assistant Professor of AIDS Department
I. Limitations of Exception Handling
1. Performance Overhead:
o Exception handling can introduce a slight performance cost due to runtime checks and stack
unwinding.
2. Hard to Debug:
o Excessive use of exceptions can make the code hard to follow and debug.
3. Not Suitable for All Errors:
o Exceptions are meant for exceptional cases, not regular control flow.
4. Compiler Dependency:
o Exception handling behavior may vary slightly across compilers.
2. Template
Templates in C++ allow writing generic functions and classes, meaning that the same code can work with
different data types. The C++ template mechanism is a powerful tool for writing reusable code, enabling the
creation of functions and classes that can operate with any data type without having to rewrite the code for
each type.
A. Types of Templates
1. Function Template: A function template allows the definition of a generic function that can operate
on different data types. The function is created once and can handle multiple types, determined at
compile-time.
2. Class Template: A class template allows the creation of a generic class that can work with different
data types. Like function templates, class templates are compiled for specific types when used.
B. Syntax of Templates
1) Function Template Syntax
template <typename T>
T add(T a, T b) {
return a + b;
}
• template <typename T> is the declaration of the template, where T is a placeholder for the type.
• The function add is created to add two values of type T.
int main() {
int intResult = add(10, 20); // Using template with int
double doubleResult = add(10.5, 20.5); // Using template with double
return 0;
}
Output:
Sum of integers: 30
Sum of doubles: 31
In this example, the function template add() is used to add two integers and two doubles. The compiler
generates the appropriate code for each type.
int main() {
Box<int> intBox(10); // Creating a Box of integers
Box<double> doubleBox(3.14); // Creating a Box of doubles
cout << "Box containing integer: " << intBox.getValue() << endl;
cout << "Box containing double: " << doubleBox.getValue() << endl;
return 0;
}
Output:
Box containing integer: 10
Box containing double: 3.14
int main() {
int intArr[] = {10, 2, 7, 5};
double doubleArr[] = {3.14, 1.59, 2.65, 4.22};
sortArray(intArr, intSize);
sortArray(doubleArr, doubleSize);
return 0;
Prepared By: - Akshay Arya, Assistant Professor of AIDS Department
}
Output:
Sorted integer array: 2 5 7 10
Sorted double array: 1.59 2.65 3.14 4.22
This example shows how a generic sorting algorithm can be written using templates. The sortArray function
works with any data type (like int or double) by utilizing the template mechanism.
3. Stream class
In C++, a stream refers to a sequence of characters that are transferred between the program and input/output
(I/O) devices. Stream classes in C++ facilitate input and output operations on files and other I/O devices.
These classes have specific features to handle program input and output, making it easier to write portable
code that can be used across multiple platforms.
Prepared By: - Akshay Arya, Assistant Professor of AIDS Department
To use streams in C++, you need to include the appropriate header file. For instance, to use input/output
streams, you would include the iostream header file. This library provides the necessary functions and classes
to work with streams, enabling you to read and write data to and from files and other I/O devices.
In this blog, we’ll be exploring four essential C++ stream classes:
• istream
• ostream
• ifstream
• ofstream
Object-oriented programming, such as C++, relies heavily on the concept of inheritance. Inheritance allows a
class to inherit properties from another class that has already been defined. Descendant classes can then add
their own unique properties, making them specializations of their parent class.
Take the stream class hierarchy as an example. In the diagram below (only a portion is shown), we can see
that ifstream is a specialization of istream. This means that an ifstream object is an istream object and inherits
all the properties of the istream class. Additionally, it adds some additional properties of its own.
The C++
stream class hierarchy consists of a number of classes that define and provide different flows for objects in the
class. The hierarchy is structured in a way that starts with the top class, which is the ios class, followed by
other classes such as the istream, ostream, iostream, istream_withassign, and ostream_withassign classes.
The ios class is the parent class in the hierarchy and both the istream and ostream classes inherit from it.
These two classes together form the ios class, which is the highest level of the entire C++ stream class
hierarchy.
Other classes in the hierarchy provide functions for various operations, including assignment operations, such
as the _withassign classes.
int main() {
int entered_number;
return 0;
}
Output
Enter an integer: 3
You entered: 3
In this example, cin represents the standard input stream that reads data from the keyboard. We use
the >> operator to extract the user's input and store it in the number variable.
int main() {
char ch;
return 0;
}
Output
Enter a sentence: Hello World
Hello World
Here, we have used cin.get() to read a single character from the user.
Notice this part of the code:
while (cin.get(ch) && ch != '\n') {
cout << ch;
}
The loop reads characters one by one from standard input until a newline character is encountered. In each
iteration, it prints the character immediately as it is read.
int main() {
string line;
return 0;
}
Output
Enter a line of text: Hello
You entered: Hello
int main() {
char ch;
return 0;
}
Output
Enter a line of text: 12345678
Next character after ignoring 5 characters: 6
Here, cin.ignore(5); is instructing the input stream to ignore the next five characters that are entered by the
user.
After this, the if condition cin.get(ch) attempts to read the next character from the input stream after the
previous five have been ignored. If successful, it stores them in the ch variable.
int main() {
char name[50];
cout << "Your name is: " << name << endl;
return 0;
}
Output:
Enter your full name: John Doe
Your name is: John Doe
In this example, cin.getline() reads the entire line of input (including spaces), storing it in the name character
array. The size 50 ensures that the input is safely stored without overflowing the buffer.
Example with Input Overflow Handling
If the input exceeds the specified size, cin.getline() will discard the extra characters and set the failbit.
#include <iostream>
using namespace std;
int main() {
char name[10];
if (cin.fail()) {
cout << "Input is too long!" << endl;
} else {
cout << "Your name is: " << name << endl;
}
return 0;
}
Output (if input exceeds the size):
Enter your name: Johnathan
Input is too long!
int main() {
int entered_number;
return 0;
}
Output
Hello World!
int main() {
char ch = 'A';
cout<< endl ;
return 0;
}
Output
A
C
int main() {
// create a C-string
const char* str = "Hello, World!";
return 0;
}
Output
Hello, World!
Here, we have written a C-string Hello, World to the output stream using the write() function.
4. File handling
File handling in C++ allows a program to read from and write to files on the disk. It involves using file
streams to interact with files, where data is stored permanently. File handling makes it possible to manage
data between the program and external storage systems, such as text files, binary files, and even databases.
B. What is a Stream?
In C++, a stream is an abstraction that represents input and output data. It acts as a flow of data between the
program and external storage. There are two main types of streams used in file handling:
1. Input stream (ifstream): Used for reading from a file.
2. Output stream (ofstream): Used for writing to a file.
3. Input/Output stream (fstream): Used for both reading from and writing to a file.
Streams abstract the underlying mechanisms of reading and writing to devices like disks, terminals, and
networks.
Prepared By: - Akshay Arya, Assistant Professor of AIDS Department
C. Purpose of File Handling
The primary purposes of file handling are:
1. Persistent Data Storage: Files provide a way to save program data between executions, such as
saving user preferences, logs, or configurations.
2. Data Transfer: Files allow sharing data between different programs, users, and systems.
3. Large Data Management: Files are used to handle large datasets that cannot be stored entirely in
memory.
#include <iostream>
#include <fstream>
using namespace std;
string line;
while (getline(inFile, line)) {
cout << line << endl; // Print each line from the file
}
int main() {
// Create an ifstream object (no file opened yet)
ifstream inFile;
string line;
while (getline(inFile, line)) {
cout << line << endl; // Print each line from the file
}
Method of The file is opened when the file The file is opened explicitly by calling the open()
Opening stream object is created. function on an existing object.
Less flexible since the file is opened More flexible, as you can open the file at any
Flexibility
during object creation. point in the program after the object is created.
Re-opening the Can’t re-open the file after the object Can re-open a file multiple times by calling open()
File is created. again.
Access Mode Access modes are specified in the Access modes are specified as arguments to the
Specification constructor. open() function.
I. Closing Files
To release the file resources, it is essential to close the file once operations are complete using the close()
method:
inFile.close(); // Close input file
outFile.close(); // Close output file
file.close(); // Close input/output file
ifstream inFile("input.txt");
int x;
inFile >> x;
2. Using get(): This method reads a single character at a time from the file.
string line;
getline(inFile, line); // Read a full line
4. Using read(): This reads raw binary data into a buffer.
char buffer[100];
inFile.read(buffer, sizeof(buffer)); // Read binary data
ofstream outFile("output.txt");
outFile << "Hello, World!" << endl;
2. Using put(): This method writes a single character to the file.
1) tellg() Method
• Purpose: The tellg() method is used to get the current position of the get pointer (input pointer) in a
file. This pointer is used during reading operations.
• Usage: It returns the current position of the get pointer as an integer, which is the byte offset from
the beginning of the file.
• Return Value: It returns a stream position indicator (streampos) that represents the position of the get
pointer.
• Applies to: Input file streams (ifstream and fstream in input mode).
2) tellp() Method
• Purpose: The tellp() method is used to get the current position of the put pointer (output pointer) in
a file. This pointer is used during writing operations.
3) seekg() Method
• Purpose: The seekg() method is used to move the get pointer (input pointer) to a specific position in
the file. It allows random access while reading a file.
• Usage: You can specify the position to move to from the beginning of the file, from the current
position, or from the end of the file.
• Arguments:
o seekg(offset, direction):
▪ offset: The number of bytes to move the pointer.
▪ direction: This can be one of the following constants:
▪ ios::beg: From the beginning of the file.
▪ ios::cur: From the current position.
▪ ios::end: From the end of the file.
• Return Value: seekg() returns the file stream itself, allowing for chaining of operations.
4) seekp() Method
• Purpose: The seekp() method is used to move the put pointer (output pointer) to a specific position
in the file. It allows random access while writing a file.
• Usage: You can specify the position to move to from the beginning of the file, from the current
position, or from the end of the file.
• Arguments:
o seekp(offset, direction):
▪ offset: The number of bytes to move the pointer.
▪ direction: This can be one of the following constants:
▪ ios::beg: From the beginning of the file.
▪ ios::cur: From the current position.
▪ ios::end: From the end of the file.
• Return Value: seekp() returns the file stream itself, allowing for chaining of operations.
Example:
#include <iostream>
#include <fstream>
using namespace std;
int main() {
// Create and open a file in output mode to write data
ofstream outFile("sample.txt");
Prepared By: - Akshay Arya, Assistant Professor of AIDS Department
// Check if file is opened successfully
if (!outFile) {
cout << "Error opening file for writing!" << endl;
return 1;
}
// Use tellg() to get the current position of the get pointer (before reading)
streampos posBefore = inFile.tellg();
cout << "Initial get pointer position (tellg()): " << posBefore << endl;
// Use seekg() to move the get pointer back to the beginning of the file
inFile.seekg(0, ios::beg);
cout << "Get pointer moved back to the beginning using seekg()." << endl;
// Open the file in output mode again to write data at a specific position
ofstream outFile2("sample.txt", ios::in | ios::out); // Open file in both input and output mode
if (!outFile2) {
cout << "Error opening file for reading and writing!" << endl;
return 1;
}
// Move the put pointer to position 13 from the beginning using seekp()
outFile2.seekp(13, ios::beg);
cout << "Put pointer moved to position 13 using seekp()." << endl;
outFile2.close();
inFile.close();
return 0;
}
Summary of Methods:
Method Purpose Applies to
tellg() Gets the current position of the get pointer (reading) Input streams (ifstream)
tellp() Gets the current position of the put pointer (writing) Output streams (ofstream)
seekg() Moves the get pointer to a specified position (reading) Input streams (ifstream)
seekp() Moves the put pointer to a specified position (writing) Output streams (ofstream)
ifstream inFile("example.txt");
if (inFile.eof()) {
cout << "End of file reached." << endl;
}
if (rename("oldfile.txt", "newfile.txt") != 0) {
perror("Error renaming file");
}
2. Deleting a File:
if (remove("file.txt") != 0) {
perror("Error deleting file");
}
3. Getting File Properties: You can use stat() function to get file properties.
#include <iostream>
#include <fstream>
using namespace std;
// Constructor
Person(const char* n = "", int a = 0) {
strncpy(name, n, sizeof(name)-1);
name[sizeof(name)-1] = '\0';
age = a;
}
int main() {
// Create an object of Person
Person p1("Alice", 30);
return 0;
}
Output:
Object written to file successfully.
Object read from file successfully.
Name: Alice, Age: 30
Explanation of the Code:
1. Class Definition (Person):
o The Person class contains two members: name (a character array) and age (an integer).
o The constructor initializes the name and age with default values or the ones passed during
object creation.
2. Writing Object to File (write()):
o An object of Person (p1) is created with the name "Alice" and age 30.
o We open the file "person.dat" in binary mode (ios::binary) for writing.
o The write() function is used to write the binary data of the object to the file. We use
reinterpret_cast<char*>(&p1) to treat the Person object as a char* pointer to the raw memory,
and sizeof(p1) ensures that the entire object is written.
o After writing, the file is closed.
3. Reading Object from File (read()):
o Another object p2 is created to store the data read from the file.
o The file "person.dat" is opened again in binary mode, but this time for reading.
o
The read() function is used to read the binary data from the file and store it into the p2 object.
We use reinterpret_cast<char*>(&p2) to treat the p2 object as a char* pointer to receive the
raw data.
Prepared By: - Akshay Arya, Assistant Professor of AIDS Department
o After reading, the file is closed.
4. Displaying the Object:
o The display() function of p2 is called to show the name and age of the person that was read
from the file.
5. Key Points:
• The write() and read() functions are used to perform binary I/O operations.
• These functions work with raw memory, so they are more efficient than other file operations, but they
require careful handling (especially for objects with complex structures or pointers).
• write() takes the raw data of the object and writes it to a file, while read() extracts this data back into
an object.
• For object serialization, we use reinterpret_cast to treat the object as a sequence of bytes.
6. Limitations and Considerations:
1. No Human Readable Format: Data written in binary format is not human-readable. This is fine for
machine-to-machine communication, but you need specific programs to read binary files.
2. Endianness: The byte order (big-endian vs. little-endian) of binary files may be different on different
platforms, so files may not be portable.
3. Complex Objects: If your objects contain pointers or dynamically allocated memory, you need to
handle serialization and deserialization of that memory manually. Simple objects (like the one in the
example) do not face this issue.
/*File Searching*/
#include<iostream>
#include<fstream>
using namespace std;
class Student
{
public:
int Roll;
char Name[20];
void input()
{
cout<<"\nEnter Roll No ";
cin>>Roll;
cout<<"Enter Name ";
cin>>Name;
}
void output()
{
cout<<"\nRoll No. is "<<Roll;
cout<<"\tName is "<<Name;
}
};
int main()
{
Student A;
ifstream file1("stu.txt");
int N,C=0;
cout<<"\nEnter Roll No.";
cin>>N;
while(!file1.eof())
{
file1.read((char*)&A, sizeof(A));
if(A.Roll==N)
{
A.output();
C=1;
}
}
if(C==0)
{