C++
Introduction
C++ is a general-purpose programming language developed by Bjarne Stroustrup as an extension of C.
C++ is a programming language known for its fast speed, low level memory management and is often taught as first programming language.
Syntax similarity with C, Java, and C# makes it easier to switch languages.
C++ is machine-independent at the source code level, but machine-dependent at the compiled executable level.
C++ provides low-level access to system resources
Direct Memory Manipulation: C++ uses pointers, which are variables that store the exact physical memory addresses of data. This allows developers to read from, write to, or manipulate specific locations in the RAM directly.
Manual Memory Management: Unlike languages with automatic garbage collection (like Java or Python), C++ requires developers to explicitly allocate and free memory (using new and delete). This gives precise control over when resources are consumed and released.
The below C++ code shows the basic structure of a program.
#include <iostream>
using namespace std;
int main() {
cout << "Hello, World!";
return 0;
}
- Header File: #include <iostream> adds input/output objects (cin, cout, etc.) via the preprocessor. Common headers: fstream (files), string (strings), vector (STL), bits/stdc++.h (all-in-one).
- Namespace Declaration: using namespace std; allows direct use of standard names like "cout" without std::
- Main Function: int main() is the program’s entry point; execution starts here and returns an integer with return 0 mean successful execution
Comments: // for single line, /*....*/ for multi-line are ignored by the compiler and used only for code documentation.
Data types
1. Built-in (Primitive) Data Types
These are the fundamental data types native to the C++ compiler.
- Integer (
int): Stores whole numbers without decimals. Typically requires 4 bytes of memory. Example:int age = 25; - Character (
char): Stores a single character and requires 1 byte of memory. Characters are enclosed in single quotes. Example:char grade = 'A'; - Boolean (
bool): Stores logical values, taking 1 byte of memory. It can only holdtrue(1) orfalse(0). Example:bool is_valid = true; - Floating-Point (
float): Stores single-precision decimal numbers. Typically requires 4 bytes of memory. Example:float pi = 3.14f; - Double Floating-Point (
double): Stores double-precision decimal numbers for higher accuracy. Typically requires 8 bytes of memory. Example:double precise_pi = 3.14159265; - Void (
void): Represents an absence of a value. It is primarily used for functions that do not return any data.
Any decimal literal without a suffix (e.g.,
3.14) is treated as adoubleby the compiler.Suffixes: To explicitly make a literal a
float, you must append anforF(e.g.,3.14f).Note on Modifiers: C++ allows you to modify the size or range of these primitive types using type modifiers:
signed,unsigned,short, andlong. For instance, anunsigned intonly holds positive numbers, effectively doubling its positive range, while along longincreases the storage capacity for massive integers.
2. Derived Data Types:
These types are constructed by modifying or grouping built-in data types.
- Arrays: A collection of elements of the same data type stored in contiguous memory locations. Example:
int scores[5] = {90, 85, 92, 88, 95}; - Pointers: Variables that store the physical memory address of another variable rather than a direct value. Example:
int* ptr = &age; - References: An alias or alternative name for an already existing variable. Example:
int& ref = age; - Functions: A block of code that performs a specific task and returns a specific data type (or
void).
3. User-Defined Data Types
These are custom data types defined by the programmer to group disparate data together or create complex architectural structures.
- Structures (
struct): Groups variables of different data types under a single name. Elements are accessed via dot notation. - Classes (
class): The foundation of C++ object-oriented programming. Similar to astruct, but it encapsulates both data variables and member functions, with data typically hidden via access modifiers (private,public,protected). - Unions (
union): Similar to astruct, but all members share the exact same memory location. The size of the union is determined by its largest member, and it can only hold one member's value at a time. - Enumerations (
enum): Creates a customized data type consisting of a set of named integer constants, improving code readability. Example:enum Color { RED, GREEN, BLUE }; - Typedef (
typedef): Allows you to create a new alias or nickname for an existing data type. Example:typedef unsigned long ulong;
Data Type Modifiers
short: Specifies a smaller integer type when supported.
long: Specifies an integer type with at least as much storage as int.
signed: Allows an integer type to represent both positive and negative values.
unsigned: Allows an integer type to represent only non-negative values and provides a larger positive range
Rules For Naming Variable
The names given to a variable are called identifiers. There are some rules for creating these identifiers (names):
- A name can only contain letters (A-Z or a-z), digits (0-9), and underscores (_).
- It should start with a letter or an underscore only.
- It is case sensitive.
- The name of the variable should not contain any whitespace and special characters (i.e. #, $, %, *, etc).
- We cannot use C++ keyword (e.g. float, double, class) as a variable name.
C++ Operator Types
C++ operators are classified into 6 types on the basis of type of operation they perform:
Arithmetic Operators
Arithmetic operators are used to perform arithmetic or mathematical operations on the operands. For example, '+' is used for addition.
- Addition, +, Adds two operands
- Subtraction, -, Subtracts second operand from the first
- Multiplication, *, Multiplies two operands
- Division, /, Divides first operand by the second operand
- Modulo Operation, %, Returns the remainder of an integer division
- Increment, ++, Increases the value of operand by 1
- Decrement, --, Decreases the value of operand by 1
++a and a++, both are increment operators, however, both are slightly different. In ++a, the value of the variable is incremented first and then it is used in the program. In a++, the value of the variable is assigned first and then it is incremented. Similarly happens for the decrement operator.
Relational Operators
Relational operators are used for the comparison of the values of two operands. For example, '>' check right operand is greater.
- Is Equal To, ==, Checks if both operands are equal
- Greater Than, >, Checks if the first operand is greater than the second operand
- Greater Than or Equal To, >=, Checks if the first operand is greater than or equal to the second operand
- Less Than, <, Checks if the first operand is lesser than the second operand
- Less Than or Equal To, <=, Checks if the first operand is lesser than or equal to the second operand
- Not Equal To, !=, Checks if both operands are not equal
Logical Operators
Logical operators are used to combine two or more conditions or constraints or to complement the evaluation of the original condition in consideration. The result returns a Boolean value, i.e., true or false.
- Logical AND, &&, Returns true only if all the operands are true or non-zero
- Logical OR, ||, Returns true if either of the operands is true or non-zero
- Logical NOT, !, Returns true if the operand is false or zero
Bitwise Operators
Bitwise operators are works on bit-level. So, compiler first converted to bit-level and then the calculation is performed on the operands.
- Binary AND, &, Copies a bit to the result if it exists in both operands
- Binary OR, |, Copies a bit to the result if it exists in any of the operands
- Binary XOR, ^, Copies the bit to the result if it is present in either of the operands but not both
- Left Shift, <<, Shifts the value to the left by the number of bits specified by the right operand
- Right Shift, >>, Shifts the value to the right by the number of bits specified by the right operand
- One's Complement,
~, Changes binary digits 1 to 0 and 0 to 1
Note: Only char and int data types can be used with Bitwise Operators.
Assignment Operators
Assignment operators are used to assign value to a variable. We assign the value of right operand into left operand according to which assignment operator we use.
- Assignment, =, Assigns the value on the right to the variable on the left
- Add and Assignment, +=, Adds the right operand to the left operand and assigns the result to the left operand
- Subtract and Assignment, -=, Subtracts the right operand from the left operand and assigns the result to the left operand
- Multiply and Assignment, *=, Multiplies the left operand by the right operand and assigns the result to the left operand
- Divide and Assignment, /=, Divides the left operand by the right operand and assigns the result to the left operand
Ternary or Conditional Operators
Conditional operator returns the value, based on the condition. This operator takes three operands, therefore it is known as a Ternary Operator.
Expression1 ? Expression2 : Expression3
In the above statement:
- The ternary operator ? determines the answer on the basis of the evaluation of Expression1.
- If Expression1 is true, then Expression2 gets evaluated.
- If Expression1 is false, then Expression3 gets evaluated.
Miscellaneous Operators
Scope Resolution Operator (::) in C++ is used to identify and specify the context (or scope) to which a variable, function, or class belongs. It directs the compiler to look for an identifier within a specific namespace, class, or the global scope.
- Accessing Global Variables
int x = 100; // Global variable
void printX() {
int x = 10; // Local variable
cout << x; // Prints 10 (local)
cout << ::x; // Prints 100 (global)
}
- Defining Class Methods Externally
class Rectangle {
public:
int area(int w, int h); // Declaration only
};
// Definition outside the class
int Rectangle::area(int w, int h) {
return w * h;
}
- Accessing Static Class Members
Static variables and functions belong to the class itself rather than a specific object instance. They are accessed using the class name and the scope resolution operator.
class Counter {
public:
static int count;
};
// Initializing the static variable outside the class
int Counter::count = 0;
sizeof operator is a unary operator used to compute the size of its operand or variable in bytes. For example,
sizeof (char);
sizeof (var_name);
Comma operator is a binary operator that is used for multiple purposes. It is used as a separator or used to evaluate its first operand and discards the result; it then evaluates the second operand and returns this value (and type).
int n = (m+1, m-2, m+5);
int a, b, c;
Addressof operator is used to find the memory address in which a particular variable is stored. In C++, it is also used to create a reference.
&var_name;
* is used for dereferencing a pointer
Dot operator is used to access members of structure variables or class objects using their object names.
obj . member;
Casting operators are used to convert the value of one data type to another data type. For example, for an integer value x:
(float)x
static_cast<float>(x)
Operator Precedence in C++
1. Scope Resolution
- Operator:
:: - Description: Global scope, namespace, and class scope resolution (Left-to-right).
2. Postfix (Unary) Operators
- Operators:
++,--(post-increment/decrement),(),[](function call and array subscript),.,->(member access),typeid, and casts (static_cast, etc.). - Associativity: Left-to-right.
3. Prefix (Unary) Operators
- Operators:
++,--(pre-increment/decrement),+,-(unary plus/minus),!,~(logical/bitwise NOT),*,&(dereference/address-of),sizeof,alignof,new,delete. - Associativity: Right-to-left.
4. Pointer-to-Member Operators
- Operators:
.*,->* - Associativity: Left-to-right.
5. Multiplicative Operators
- Operators:
*,/,%(multiplication, division, modulo) - Associativity: Left-to-right.
6. Additive Operators
- Operators:
+,-(addition and subtraction) - Associativity: Left-to-right.
7. Bitwise Shift Operators
- Operators:
<<,>>(shift left and shift right) - Associativity: Left-to-right.
8. Three-Way Comparison Operator
- Operator:
<=>(Spaceship operator, introduced in C++20) - Associativity: Left-to-right.
9. Relational Operators
- Operators:
<,<=,>,>=(less than, less-or-equal, greater than, greater-or-equal) - Associativity: Left-to-right.
10. Equality Operators
- Operators:
==,!=(equal to and not equal to) - Associativity: Left-to-right.
11. Bitwise AND
- Operator:
& - Associativity: Left-to-right.
12. Bitwise XOR (Exclusive OR)
- Operator:
^ - Associativity: Left-to-right.
13. Bitwise OR (Inclusive OR)
- Operator:
| - Associativity: Left-to-right.
14. Logical AND
- Operator:
&& - Associativity: Left-to-right.
15. Logical OR
- Operator:
|| - Associativity: Left-to-right.
16. Conditional (Ternary) Operator
- Operator:
?: - Associativity: Right-to-left.
17. Assignment Operators
- Operators:
=,+=,-=,*=,/=,%=,<<=,>>=,&=,^=,|= - Associativity: Right-to-left.
18. Throw Operator
- Operator:
throw(for exception handling) - Associativity: Right-to-left.
19. Comma Operator
- Operator:
,(separates expressions evaluated sequentially) - Associativity: Left-to-right.
Set Precision in C++
The setprecision(int n) function from the <iomanip> header acts as a stream manipulator to control the precision of floating-point values in output streams like cout. By default, without any other formatting flags, n determines the total number of significant digits displayed. However, when combined with the fixed manipulator (cout << fixed;), n changes behavior to specify the exact number of digits displayed after the decimal point.
Example Code
C++
#include <iostream>
#include <iomanip>
using namespace std;
int main() {
double num = 3.142857142857;
// Default behavior: specifies total significant digits
cout << "Total significant digits (5): " << setprecision(5) << num << endl;
// With fixed: specifies digits after the decimal point
cout << fixed;
cout << "Decimal places fixed (4): " << setprecision(4) << num << endl;
return 0;
}
//OUTPUT
Total significant digits (5): 3.1429
Decimal places fixed (4): 3.1429
Loop Control Statements
Generally, the normal flow of any loop is that it will repeat till its test condition is true. In each repetition, it will execute the given set of statements. But C++ provides ways to modify this normal flow. This can be done by using loop control statements shown below:
The break statement when encountered, terminates the loop regardless of its test condition. This is useful when the execution of loop is dependent on multiple conditions.
#include <iostream>
using namespace std;
int main() {
for (int i = 0; i < 5; i++) {
// Terminating before reaching i = 4
if (i == 2) break;
cout << "Hi" << endl;
}
return 0;
}
The continue statement, when encountered, skips the remaining code inside the current iteration of the loop and proceeds to the next iteration. This is useful when you want to skip specific iterations based on certain conditions but still want to continue the loop execution.
#include <iostream>
using namespace std;
int main() {
for (int i = 0; i < 5; i++) {
// Skipping when i equals 2
if (i == 2) continue;
cout << "Hi" << endl;
}
return 0;
}
In C++, the goto statement is a jump statement that provides an unconditional transfer of execution control from the goto statement to a labeled statement within the same function.
#include <iostream>
using namespace std;
int main() {
int count = 1;
// The jump destination
loopStart:
if (count > 3) {
goto endProgram; // Jumps out of the loop logic
}
cout << "Count is: " << count << endl;
count++;
goto loopStart; // Jumps back to the label
endProgram:
cout << "Program finished!" << endl;
return 0;
}
The only scenario where experienced C++ developers occasionally use
gotois to break out of deeply nested loops. Standardbreakonly exits the innermost loop, whereas agotocan instantly escape multiple layers of nested loops to an error-handling cleanup block at the bottom of a function.
Vectors in C++
A vector in C++ is a dynamic sequence container provided by the Standard Template Library (STL).
Using Initializer List
We can initialize a vector with the list of values enclosed in curly braces {} known as initializer list.
vector<type> v = {val1, val2, val3, ...};
One by One Initialization
Vector can be initialized by pushing value one by one. In this method, an empty vector is created, and elements are added to it one by one using the vector::push_back() method
v.push_back(val);
With a Single Value
We can initialize all the elements of the vector to a single value. We create a vector of a specified size and initialize all elements to the same value using vector constructor.
vector<type> v(n, val);
Where:
n -> Size of vector
val -> Value assigned to all elements
From Another Vector
We can also initialize a newly created vector from an already created vector if they are of same type.
vector<type> v2(v1.begin(), v1.end());
From Any STL Container
Vectors are flexible containers that can be initialized by any other already existing containers such as set, multiset, map, etc. if they are of same type.
vector<type> v(first, last);
A list of popular vector functions can be found in the table below.
| Function | Description | Example | Importance | |
|---|---|---|---|---|
| back() | Returns the last element of a vector | int b = vec.back(); | ||
| at() | Returns the character at a specified index, with bounds checking | |||
| clear() | Removes all of the contents of a vector | vec.clear(); | ||
| front() | Returns the first element of a vector | int f = vec.front(); | ||
| push_back() | Adds an element to the end of a vector | vec.push_back(10); | *** | |
| pop_back() | Removes the last element of a vector | vec.pop_back(); | * | |
| swap() | Swaps the contents of one vector with another | vec1.swap(vec2); | * | |
| size() | Returns the number of elements currently in the vector. | int s = vec.size(); | *** | |
| empty() | Checks whether the vector is empty (returns true or false). | bool isEmpty = vec.empty(); |
The v.begin() functions points to the first element of the vector whereas the v.end() points to the location after the last elements of the vector
Maps in C++
A map stores elements in "key/value" pairs.
Elements in a map are:
Accessible by keys (not index), and each key is unique.
Automatically sorted in ascending order by their keys.
Maps store the pair of values in the sorted order on account of the first value however memory allocation is not sequential
//SYNTAX:
map<key's data type, value's data type>map name;
When accessing elements using the [] operator, if the key does not already exist, it is inserted into the map with a default-constructed value. For primitive types like int, this default construction results in zero initialization. Therefore, if you access map[key] for a non-existent key, the map will create a new entry with that key and a value of 0. If the key exists, the current value will be returned.
A list of popular vector functions can be found in the table below.
| Function | Description | Example | Importance | |
|---|---|---|---|---|
| map::insert() | Insert elements with a particular key in the map container –> O(log n) | Map.insert({key,value}) | ||
| erase() | Removes elements by key, by iterator, or a range of iterators. | myMap.erase("apple"); | ||
| swap() | Swaps the contents of this map with another map. | myMap.swap(otherMap); | ||
| size() | Returns the number of key-value pairs currently in the map. | int s = myMap.size(); | ||
| lower_bound() | Returns an iterator to the first element whose key is not less than (>=) the given key. | auto it = myMap.lower_bound(10); | ||
| upper_bound() | Returns an iterator to the first element whose key is strictly greater than (>) the given key. | auto it = myMap.upper_bound(10); | ||
| find() | Returns an iterator pointing to the key-value pair, or map::end if the key is not found. | auto it = myMap.find("apple"); | ||
| count() | Returns the number of elements matching the specific key. For standard maps/sets, it only returns 1 (exists) or 0 (does not exist). | if (myMap.count("apple")) { /* key exists */ } | *** | |
| map::clear() | Removes all the elements from the map. | |||
Unordered Map
An std::unordered_map in C++ is an associative container from the Standard Template Library (STL) that stores data in key-value pairs. It is implemented internally using a hash table.
Time Complexity: It offers an average-case time complexity of O(1) for insertions, deletions, and lookups. This makes it significantly faster than a standard
std::map(which takes O(log N) time) for large datasets.
Sets in C++
A std::set in C++ is an associative container that stores unique elements in a specifically sorted order (ascending by default). It is implemented internally as a balanced binary search tree (typically a Red-Black Tree), which means its core operations—insertion, deletion, and search execute in O(log N) time.
std::set<int> numbers;
| Function | Description | Example | Importance | |
|---|---|---|---|---|
| insert() | Adds a new element to the set and automatically sorts it. | mySet.insert(15); | ||
| erase() | Removes a specific element by value or via an iterator. | mySet.erase(15); | ||
| find() | Returns an iterator to the element if found, or set::end if not. | auto it = mySet.find(10); | ||
| count() | Returns 1 if the element exists in the set, and 0 if it does not. | if (mySet.count(10)) { } | ** | |
| size() | Returns the total number of elements currently in the set | int s = mySet.size(); | ||
| empty() | Checks whether the set is empty (returns true or false). | bool isEmpty = mySet.empty(); | ||
| clear() | Removes all elements from the set. | mySet.clear(); | ||
| lower_bound() | Returns an iterator to the first element strictly greater than the given value. | auto it = mySet.lower_bound(10); | ||
| upper_bound() | returns the same element's index if it exists in the array otherwise it returns the index of the next greater element | auto it = mySet.upper_bound(10); |
Strings in C++
The <string> library has many functions that allow you to perform tasks on strings.
| Function | Description | Example | Importance | |
|---|---|---|---|---|
| append() | Adds characters or another string to the end of the current string | str.append("!"); | ||
| at() | Returns the character at a specified index, with bounds checking | |||
| length() | Returns the number of characters in the string | |||
| push_back() | Adds a single character to the end of the string | |||
| pop_back() | Removes the last character from the string | |||
| size() | Alias of length(); returns the string's length | |||
| substr() | Returns a portion of the string, starting at a given index and length | |||
| swap() | Exchanges the contents of two strings | |||
| reverse() | The standard way to reverse data structures in C++ is by using std::reverse() | reverse(str.begin(), str.end()); |
Stack in C++
A Stack is a linear data structure that strictly follows the Last-In, First-Out (LIFO) principle. The last element added to the stack is always the first one to be removed.
Time & Space Complexity:
-
Push (Insert): O(1)
-
Pop (Remove): O(1)
-
Peek / Top (Access): O(1)
Function Description Example Importance push() Pushes an element onto the stack represented by this deque. stack.push(10); pop() Removes the top element. Note: It does not return the element. st.pop(); top() Returns a reference to the top element without removing it. int val = st.top(); empty() Checks if the stack is empty (returns trueorfalse).bool isEmpty = st.empty(); size() Returns the number of elements in the stack. int s = st.size();
The
toupper()andtolower()functions are standard library functions in C++ (found in the<cctype>header file) used to convert individual alphabet characters between uppercase and lowercase ASCII values. Both functions take anintrepresentation of a character and return its converted ASCII value—or the original character unchanged if it is already in the target case, a digit, or a special symbol. You can easily use them inside a range-based loop to transform entire strings, as demonstrated below:cpp#include <iostream> #include <string> #include <cctype> using namespace std; int main() { string text = "GeeksforGeeks 123!"; for (char &c : text) { c = toupper(c); // Converts alphabetical characters to uppercase } cout << "Uppercase: " << text << endl; for (char &c : text) { c = tolower(c); // Converts alphabetical characters to lowercase } cout << "Lowercase: " << text << endl; return 0; }
New and Delete Operations in C++
In C++, dynamic memory allocation allows programs to allocate and release memory during runtime. The new operator allocates memory from the free store (heap) and returns its address, while the delete operator releases the allocated memory when it is no longer needed.
Applications of Dynamic Memory
Dynamic memory allocation is commonly used when memory requirements cannot be determined at compile time.
- Allocating memory whose size is known only during program execution.
- Implementing dynamic data structures such as linked lists, trees, graphs, and dynamic arrays.
- Creating objects whose lifetime must extend beyond the scope of the function in which they are created.
Exception Handling in C++
Exception Handling in C++ is a mechanism used to handle runtime errors and abnormal conditions, allowing a program to continue execution smoothly even in the presence of errors.\
Basic try-catch Example
The try block contains code that might throw an exception, while the catch block handles the exception if it occurs.
#include <iostream>
using namespace std;
int main()
{
int n = 10;
int m = 0;
try {
if (m == 0)
throw "Division by zero";
cout << "Answer: " << n / m;
}
catch (const char* msg) {
cout << "Error: " << msg;
}
return 0;
}
Internal Working of try-catch Block
When an exception occurs:
- The runtime executes code inside the try block.
- If an exception is thrown, the remaining code inside the try block is skipped.
- The runtime searches for a matching catch block.
- If found, the exception is handled.
- If no matching handler is found, terminate() is called.
- During this process, stack unwinding occurs and local objects are destroyed automatically.
File Handling in C++
File handling in C++ refers to reading data from and writing data to files such as .txt, .csv, and binary files. It allows programs to store and retrieve data from secondary storage even after the program terminates.
- Data stored only in a program's memory is lost when the program ends, while data written to a file can persist on secondary storage such as an HDD or SSD.
- C++ provides file stream classes such as ifstream, ofstream, and fstream for performing file operations.
- These classes are provided by the <fstream> header.
Opening a File
Before reading from or writing to a file, we need to open it. Opening a file creates a connection between the program and the file; it does not load the entire file into memory. Data is transferred when read or write operations are performed.
In C++, a file can be opened by creating a file stream object using ifstream, ofstream, or fstream.
fstream file("filename.ext", mode);
where,
file is the name of the file stream object.
filename.ext specifies the file to be opened.
mode specifies how the file will be accessed.
File Opening Modes
| Mode | Description | |
|---|---|---|
| ios::in | File open for reading. If file does not exists, the open operation fails. | |
| ios::out | File open for writing: the internal stream buffer supports output operations. | |
| ios::binary | Operations are performed in binary mode rather than text. | |
| ios::ate | The output position starts at the end of the file. | |
| ios::app | All output operations happen at the end of the file, appending to its existing contents. | |
| ios::trunc | Any contents that existed in the file before it is open are discarded. |
If the file opened in write mode does not exists, a new file is created. But if the file opened in read mode doesn't exists, then no new file is created, and an exception is thrown
Other File Streams
C++ provides three commonly used file stream classes:
- ifstream: Used for input operations, such as reading data from a file.
- ofstream: Used for output operations, such as writing data to a file.
- fstream: Used for both input and output operations.
Write Data to File
Once the file is opened in the write mode using either fstream or ofstream, we can perform the write operation in similar way as with cout using << operator.
#include <bits/stdc++.h>
using namespace std;
int main() {
// Open a file
ofstream file("GFG.txt");
// Write the string to the file
file << "Welcome to GeeksforGeeks.";
return 0;
}
Read Data from File
Once the file is opened in read mode using either fstream or ifstream, we can perform the read operation in a similar way as with cin using the >> operator.
#include <bits/stdc++.h>
using namespace std;
int main()
{
// Open a file in read mode
ifstream file("GFG.txt");
string s;
// Read string from the file
file >> s;
cout << "Read String: " << s;
return 0;
}
This has same problem as cin. The input is only taken until the first whitespace character. To avoid this, we can use the getline()
#include <bits/stdc++.h>
using namespace std;
int main()
{
// Open a file in read mode
ifstream file("GFG.txt");
string s;
// Read string from the file
getline(file, s);
cout << "Read String: " << s;
return 0;
}
Closing the File
Closing the file means closing the associated stream and free the resources that we being used. It is important to close the file after you are done with it, especially in the long running programs to avoid memory leaks, data loss, etc.
In C++, the files are closed using the close() member function that is present in all file streams.
#include <bits/stdc++.h>
using namespace std;
int main()
{
// Open a file in read mode
ifstream file("GFG.txt");
string s;
// Read string from the file
getline(file, s);
cout << "Read String: " << s;
// Close the file
file.close();
return 0;
}
Multithreading in C++
Multithreading is a programming technique in which a program is divided into multiple threads that can run concurrently. It helps improve performance and responsiveness by allowing multiple tasks to execute simultaneously while sharing the same memory space.
- Threads allow multiple tasks to run independently within the same program.
- Multithreading improves CPU utilization and application performance.
- The <thread> header provides functions to create, manage, and synchronize threads in C++.
#include <iostream>
#include <thread>
using namespace std;
void task() {
cout << "Hello from the thread!" << endl;
}
int main() {
thread t(task);
t.join();
cout << "Main thread finished.";
return 0;
}
Explanation:
- thread t(task) creates a new thread that executes the task() function.
- t.join() makes the main thread wait until t finishes.
- After the thread completes, the main thread prints its message.
thread thread_name(callable);
where,
- thread_name: It is an object of thread class.
- callable: It is a callable object like function pointer, function object.
Joining a Thread
The join() function waits for a thread to finish its execution. It is commonly used when the calling thread must wait for the completion of another thread.
thread_name.join();
- The calling thread remains blocked until the specified thread finishes.
- A thread should be joined only when it is in a joinable state.
- Calling join() on a non-joinable thread throws a std::system_error.
Checking if a Thread is Joinable
The joinable() function checks whether a thread object represents a thread that can be joined.
thread_name.joinable();
- Returns true if the thread is joinable.
- Returns false if the thread is not associated with a joinable thread.
Detaching a thread
The detach() function separates a thread from its std::thread object, allowing it to execute independently.
thread_name.detach();
Getting Thread ID
In Multithreading in C++ each thread has a unique ID which can be obtained by using the get_id() function.
thread_name.get_id();
Callables in Multithreading
A callable is an object that can be invoked like a function. C++ threads can execute different types of callables, including functions, lambda expressions, function objects, and member functions.
- Arguments can be passed to a callable when creating a thread.
- The callable starts executing when the thread is created.
Problems with Multithreading
Multithreading improves the performance and utilization of CPU, but it also introduces various problems:
- Deadlock: A deadlock occurs when two or more threads are blocked forever because they are each waiting for shared resources that the other threads hold. This creates a cycle of waiting, and none of the threads can proceed.
- Race Condition: A race condition occurs when multiple threads access shared data simultaneously and at least one thread modifies it, causing unpredictable or incorrect results.
- Starvation: Starvation occurs when a thread is continuously unable to access shared resources because other threads keep getting priority, preventing it from executing and making progress.
Thread Synchronization
Thread synchronization is the process of controlling multiple threads when accessing shared resources to avoid data inconsistency and race conditions. It ensures that only one thread accesses a critical resource at a time.
- Synchronization helps maintain data accuracy and consistency in multithreaded programs.
- Common synchronization tools in C++ include mutexes, locks, and condition variables.
Context switch in multithreading
A context switch occurs when the CPU stops executing one thread and starts executing another thread. The state of the current thread is saved so that execution can continue later from the same point.
- It enables multiple threads to share CPU time efficiently.
- Frequent context switching may reduce performance due to overhead.