C
C
Performance Considerations
Since C++ code is compiled directly to machine code, it can take full
advantage of the hardware, leading to potentially better performance.
Optimizations performed by the compiler can significantly enhance
execution speed.
3. Garbage Collection:
C++ allows for low-level manipulation and direct memory access, which
can lead to more efficient code.
Java provides a rich set of high-level APIs and abstractions, which can
simplify development but may introduce performance overhead.
Real-World Performance
In practice, the performance difference between C++ and Java depends
on various factors, including the nature of the application, the efficiency of
the algorithms used, and the quality of the code.
For compute-intensive tasks and systems programming, C++ is often
preferred due to its potential for higher performance.
For applications where development speed, portability, and ease of
maintenance are more critical, Java might be chosen despite the possible
performance trade-offs.
Conclusion
While it is generally true that C++ can be faster than Java due to its direct
compilation to machine code, the actual performance difference can vary
significantly based on the context. Modern advancements in JVM
technology, particularly JIT compilation, have closed much of the
performance gap between Java and traditionally compiled languages like
C++. Therefore, the choice between C++ and Java should consider not
only raw performance but also factors such as development time,
maintainability, and the specific requirements of the application.
Certainly! Let's delve into the differences between bytecode and machine
code, as they are crucial concepts in understanding how various
programming languages, including Java, are executed.
Bytecode
1. Definition:
3. Portability:
4. Examples:
Java bytecode, produced by the javac compiler from Java source code.
Bytecode for other languages such as Python (Pyc files).
Machine Code
1. Definition:
2. Execution:
3. Portability:
4. Examples:
Key Differences
1. Level of Abstraction:
2. Portability:
3. Execution:
4. Use Case:
Summary
Bytecode: Intermediate, portable code that is executed by a virtual
machine.
Machine Code: Low-level, non-portable code that is executed directly by
the CPU.
Understanding these differences helps clarify how languages like Java
achieve portability and flexibility, whereas languages like C++ prioritize
performance and direct hardware interaction.
Before the main function is called, the C++ compiler performs several tasks, including:
Preprocessing: The C++ preprocessor processes all of the preprocessor directives in the
program, such as "#include" and "#define". These directives are replaced with the contents of
the included files or the defined constants.
Compiling: The C++ compiler translates the preprocessed source code into machine code,
which is a low-level code that can be executed by the computer's processor.
Linking: The linking process combines the compiled object files and any external libraries
used by the program into a single executable file.
Once the program has been compiled and linked, it can be run. The program will start
executing at the main function and will continue to execute the code within the main function
until it reaches the end. The main function must return an integer value to indicate the success
or failure of the program.
Basic Terminology:
7. Header Files: Header files in C++ are files that contain declarations
for functions, variables, and other constructs that can be included in a C+
+ program using the preprocessor directive "#include". These
declarations allow the program to use the functions and variables defined
in the header file without having to know their implementation details.
Objects package data and the operations on them so that only the
operations are publicly accessible and internal details of the data
structures are hidden. This information hiding made large-scale
programming easier by allowing a programmer to think about each part of
the program in isolation.
From bard::
Object-oriented programming (OOP) offers several significant advantages in C++ compared
to procedural programming, making it a powerful paradigm for structuring complex software:
Real-World Modeling: OOP allows you to create objects that represent real-world
entities, like Car, BankAccount, or Window. This makes code more intuitive and easier
to understand, especially for larger projects.
Modularization: Classes encapsulate data (properties) and the functions (methods)
that operate on that data. This modularity promotes better code organization and
reusability. Changes made to a class are less likely to affect other parts of your
program, improving maintainability.
Inheritance: OOP allows you to create new classes (subclasses) that inherit
properties and behaviors from existing classes (base classes). This eliminates the need
to rewrite common code, saving development time and effort. Imagine a base class
Vehicle with properties like color and speed, and derived classes Car and Truck
inheriting these while adding their specific functionalities.
Polymorphism: Objects of different classes (derived from a common base) can
respond differently to the same message (function call). This flexibility allows you to
write generic code that works with various object types without extensive
modifications. For instance, a printInfo function could handle both Car and Truck
objects appropriately.
Data Hiding: OOP promotes data hiding by restricting direct access to an object's
internal data (attributes) through private members. Public member functions
(methods) provide controlled access and modification of data, ensuring data integrity
and preventing accidental errors.
While C++ can be used procedurally, OOP offers a more structured and efficient
approach for building large-scale applications, especially those involving complex
interactions and data management.
Feature Scripting Language General-Purpose Language
Execution Interpreted Compiled (often)
Syntax Simple and easy to learn Complex, with rich features
Development
Speed Fast development and iteration Typically slower due to compilation
Performance Generally slower Optimized for high performance
Memory Manual or automatic depending on the
Management Automatic (garbage collection) language
Automation, web scripting, quick
Use Cases tasks System software, applications, games
Acts as glue code for other Can be integrated but often as the main
Integration systems system
Typical Languages Python, JavaScript, Ruby C++, Java, C#
Scripting Language
Definition: A scripting language is designed for integrating and
communicating with other programming languages and for automating
repetitive tasks. Scripts are usually interpreted rather than compiled.
Types of Variables
There are three types of variables based on the scope of variables in C++
Local Variables
Instance Variables
Static Variables
1. Local Variables: A variable defined within a block or method or constructor is called a local
variable.
o These variables are created when entered into the block or the function is called and
destroyed after exiting from the block or when the call returns from the function.
o The scope of these variables exists only within the block in which the variable is
declared. i.e. we can access this variable only within that block.
o Initialization of Local Variable is Mandatory.
2. Instance Variables: Instance variables are non-static variables and are declared in a class
outside any method, constructor, or block.
o As instance variables are declared in a class, these variables are created when an
object of the class is created and destroyed when the object is destroyed.
o Unlike local variables, we may use access specifiers for instance variables. If we do
not specify any access specifier then the default access specifier will be used.
o Initialization of Instance Variable is not Mandatory.
o Instance Variable can be accessed only by creating objects.
3. Static Variables: Static variables are also known as Class variables.
o These variables are declared similarly as instance variables, the difference is that
static variables are declared using the static keyword within a class outside any
method constructor or block.
o Unlike instance variables, we can only have one copy of a static variable per class
irrespective of how many objects we create.
o Static variables are created at the start of program execution and destroyed
automatically when execution ends.
o Initialization of Static Variable is not Mandatory. Its default value is 0
o If we access the static variable like the Instance variable (through an object), the
compiler will show the warning message and it won’t halt the program. The compiler
will replace the object name with the class name automatically.
o If we access the static variable without the class name, the Compiler will
automatically append the class name.
Instance Variable Vs Static Variable
Each object will have its own copy of the instance variable whereas We can only have one
copy of a static variable per class irrespective of how many objects we create.
Changes made in an instance variable using one object will not be reflected in other objects
as each object has its own copy of the instance variable. In the case of static, changes will be
reflected in other objects as static variables are common to all objects of a class.
We can access instance variables through object references and Static Variables can be
accessed directly using the class name.
The syntax for static and instance variables:
class Example
{
static int a; // static variable
int b; // instance variable
}
All variables use data type during declaration to restrict the type of data
to be stored. Therefore, we can say that data types are used to tell the
variables the type of data they can store. Whenever a variable is defined
in C++, the compiler allocates some memory for that variable based on
the data type with which it is declared. Every data type requires a
different amount of memory.
C++ supports a wide variety of data types and the programmer can select
the data type appropriate to the needs of the application. Data types
specify the size and types of values to be stored. However, storage
representation and machine instructions to manipulate each data type
differ from machine to machine, although C++ instructions are identical
on all machines.
Local Variables
Global Variables
Local variables do not exist outside the block in which they are declared,
i.e. they can not be accessed or used outside that block.
Global Variables - Global Variables can be accessed from any part of the
program.
They are declared at the top of the program outside all of the functions or
blocks.
Signed
Unsigned
Short
Long
Static keyword has different meanings when used with different types. We
can use static keyword with:
Output
0 1 2 3 4
You can see in the above program that the variable count is declared as
static. So, its value is carried through the function calls. The variable
count is not getting initialized for every time the function is called.
class GfG
{
int i;
public:
GfG()
{
i = 0;
cout << "Inside Constructor\n";
}
~GfG()
{
cout << "Inside Destructor\n";
}
};
int main()
{
int x = 0;
if (x==0)
{
GfG obj;
}
cout << "End of main\n";
}
Output:
Inside Constructor
Inside Destructor
End of main
In the above program the object is declared inside the if block as non-static. So,
the scope of variable is inside the if block only. So when the object is created the
constructor is invoked and soon as the control of if block gets over the destructor
is invoked as the scope of object is inside the if block only where it is declared.
Let us now see the change in output if we declare the object as static.
class GfG
{
int i = 0;
public:
GfG()
{
i = 0;
cout << "Inside Constructor\n";
}
~GfG()
{
cout << "Inside Destructor\n";
}
};
int main()
{
int x = 0;
if (x==0)
{
static GfG obj;
}
cout << "End of main\n";
}
Output
Inside Constructor
End of main
Inside Destructor
You can clearly see the change in output. Now the destructor is invoked after the
end of main. This happened because the scope of static object is through out the
life time of program.
Whenever const keyword is attached to any method(), variable, pointer variable,
and with the object of a class it prevents that specific object/method()/variable
from modifying its data items value.
There are a certain set of rules for the declaration and initialization of the
constant variables -:
The auto keyword specifies that the type of the variable that is being
declared will be automatically deducted from its initializer. In the case of
functions, if their return type is auto then that will be evaluated by return
type expression at runtime.
Note: The variable declared with auto keyword should be initialized at the
time of its declaration only or else there will be a compile-time error.
typeid(x).name() returns the data type of x, for example, it return ‘i’ for
integers, ‘d’ for doubles, ‘Pi’ for the pointer to integer etc. But the actual
name returned is mostly compiler dependent.
Literals are data used for representing fixed values. They can be used
directly in the code. For example: 1 , 2.5 , 'c' etc.
Here, 1 , 2.5 and 'c' are literals. Why? You cannot assign different values
to these terms.
In C++ programming language, we use literals to represent fixed values.
C++ supports various types of literals including integer literals, floating-
point literals, character literals, and string literals.
Literals are fundamental elements used to represent constant values used
in C++ programming language. These constants can include numbers,
characters, strings, and more. Understanding and using the literals is
essential in C++ for data assignment, calculations, and data
representation. They are generally present as the right operand in the
assignment operation.
A type cast is basically a conversion from one type to another. There are
two types of type conversion:
#include <iostream>
using namespace std;
int main()
{
float f = 3.5;
cout << b;
}
C++ comes with libraries that provide us with many ways for performing
input and output. In C++ input and output are performed in the form of a
sequence of bytes or more commonly known as streams.
The cin object C++ is used to accept the input from the standard input
device i.e., keyboard. it is the instance of the class istream. It is
associated with the standard C input stream stdin. The extraction
operator(>>) is used along with the object cin for reading inputs. The
extraction operator extracts the data from the object cin which is entered
using the keyboard.
The escape sequences are special non-printing characters that are used
to control the printing behavior of the output stream objects (such as
‘cout’). These characters are not displayed in the output. An escape
sequence is prefixed with a backslash (\) and a coded character is used to
control the printing behavior. The backslash (\) is called an escape
character. So the escape sequence looks like two characters.
The escape sequence is used inside a string constant or independently.
These are written in single or double-quotes. The escape sequence can be
inserted in any position of the string such as:
cout<<”\nWelcome”;
Note - The line continuation sequence (\ followed by a new-line character)
is not an escape sequence. It is used in character strings to indicate that
the current line of source code continues on the next line.
Manipulators are operators that are used to format the data display.
#include <iostream>
#include <iomanip>
int main() {
cout << left << setw(10) << setfill('-') << "Apple" << endl; //
Output: Apple-----
cout << right << setw(10) << setfill('-') << "Orange" << endl; //
Output: ----Orange
}
The left manipulator causes the output to be left-aligned within the
specified width. The right manipulator causes the output to be right-
aligned within the specified width.
The setw manipulator sets the width of the output field, and
the setfill manipulator sets the character that should be used to fill any
unused space in the output field.
In Scientific floating point we will use e. Ex - 1.2e + 04, 1.45e +06. std::scientific
ofstream in C++ stands for "output file stream" and is part of the <fstream> library. It is
used to create files and write data to them.
Operators in C++ are special symbols that perform specific operations on one, two, or three
operands, and return a result. They form the foundation of any programming
language. In C++, we have built-in operators to provide the required
functionality.
int c = a + b;
Here, ‘+’ is the addition operator. ‘a’ and ‘b’ are the operands that are
being ‘added’.
1. Arithmetic Operators
2. Relational Operators
3. Logical Operators
4. Bitwise Operators
5. Assignment Operators
6. Ternary or Conditional Operators
In C++, the division operator (/) is used to divide one operand by another. The result of the division
operation depends on the types of the operands involved (integers or floating-point numbers).
When one or both operands are floating-point numbers (e.g., float, double), the division operator
performs floating-point division, which retains the fractional part of the result.
Assignment operators are used to assigning value to a variable. The left side
operand of the assignment operator is a variable and right side operand of the
assignment operator is a value. The value on the right side must be of the same
data-type of the variable on the left side otherwise the compiler will raise an
error.
#include <iostream>
using namespace std;
int main()
{
// Assigning value 10 to a
// using "=" operator
int a = 10;
cout << "Value of a is "<<a<<"\n";
// Assigning value by adding 10 to a
// using "+=" operator
a += 10;
cout << "Value of a is "<<a<<"\n";
return 0;
}
Value of a is 10
Value of a is 20
Value of a is 10
Value of a is 100
Value of a is 10
Logical Operators
These 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.
#include <iostream>
using namespace std;
int main()
{
int a = 10, b = 4;
bool res = ((a != b) && cout << "GeeksQuiz");
return 0;
}
Output
GeeksQuiz
In the case of logical OR, the second operand is not evaluated if the first
operand is true. For example, program 1 below doesn’t print “GeeksQuiz” as the
first operand of logical OR itself is true
int a = 10, b = 4; auto result = a <=> b; cout << result; is there something wrong in this piece
of code
Yes, there is an issue with the code. The main problem is that the result of the spaceship operator
(<=>) is not directly printable using std::cout. The operator<=> returns a
std::strong_ordering object (or similar), which does not have a direct textual representation
that can be printed with std::cout.
Overflow occurs when a calculation produces a result that is outside the range that can be
represented with a given number of bits. In the context of integer arithmetic in programming,
overflow happens when an operation results in a value that is too large (positive overflow) or too
small (negative overflow) to be stored in the integer type.
// Efficient CPP program to find sum of first
// n natural numbers that avoids overflow if
// result is going to be within limits.
#include<iostream>
using namespace std;
// Driver code
int main()
{
int n = 5;
cout << findSum(n);
return 0;
}
Integer Division: Both operands are integers; the result is an integer with the fractional
part discarded.
Example: 10 / 3 results in 3.
Floating-Point Division: At least one operand is a floating-point number; the result is a
floating-point number with the fractional part retained.
Performing division (/) on floating-point numbers in C++ always results in a floating-point number,
regardless of the values involved. The precision of the result depends on the data type ( float or
double) used for the operands. This contrasts with integer division ( /) where the result is truncated
towards zero and is an integer.
cout is an instance of std::ostream, which is a part of the C++ Standard Library and is
used for outputting data to the standard output, typically the console. Its name stands for
"console output."
The role of cout, along with the stream insertion operator (<<), is to facilitate the easy and
formatted output of data. Here's a detailed explanation:
Role of cout
1. Standard Output Stream: cout is connected to the standard output stream (usually
the console or terminal). It is used to display information to the user.
2. Formatted Output: cout allows for formatted output, meaning you can output
various data types (integers, floating-point numbers, strings, etc.) in a readable format.
3. Chaining: cout supports chaining of multiple insertions in a single statement,
enabling the output of multiple pieces of data in one line of code.
The stream insertion operator (<<) is used to insert data into the output stream. Here's what it
does:
1. Insertion: It inserts the data on its right side into the stream on its left side.
2. Overloadable: The operator can be overloaded for user-defined types, allowing
custom objects to be output using cout.
3. Returns the Stream: It returns the stream after inserting the data, which allows
chaining of multiple << operations.
#include <iostream>
using namespace std;
int main()
{
int i = 20;
// Check if i is 10
if (i == 10)
cout << "i is 10";
// Since is not 10
// Then execute the else statement
else
cout << "i is 20\n";
return 0;
}
#include <iostream>
using namespace std;
int main()
{
int i = 20;
if (i == 10)
cout << "i is 10";
else if (i == 15)
cout << "i is 15";
else if (i == 20)
cout << "i is 20";
else
cout << "i is not present";
}
In a switch statement, the “case value” can be of “char” and “int” type.
Following are some of the rules while using the switch statement:
1. There can be one or N numbers of cases.
2. The values in the case must be unique.
3. Each statement of the case can have a break statement. It is optional.
// Tools
const int Pen{ 10 };
const int Marker{ 20 };
const int Eraser{ 30 };
const int Rectangle{ 40 };
const int Circle{ 50 };
const int Ellipse{ 60 };
int main(){
switch (tool)
{
case Pen : {
std::cout << "Active tool is Pen" << std::endl;
}
break;
case Marker : {
std::cout << "Active tool is Marker" << std::endl;
}
break;
case Eraser :
case Rectangle :
case Circle : {
std::cout << "Drawing Shapes" << std::endl;
}
break;
case Ellipse : {
std::cout << "Active tool is Ellipse" << std::endl;
}
break;
default: {
std::cout << "No match found" << std::endl;
}
break;
}
/*
// Condition can only be integer of enum (We'll learn about enums
later in the course)
std::string name {"John"};
switch (name) // Compiler error!
{
}
*/
return 0;
}
The function call stack is important because it allows the program to keep
track of which function is currently being executed and which functions
have been called. It also enables the program to return to the correct
location when a function finishes its execution.
#include <iostream>
using namespace std;
void printMessage() {
cout << "Hello, world!" << endl;
}
int main() {
int a = 3;
int b = 4;
int c = add(a, b); // c will be assigned the value 7
printMessage();
return 0;
}
Output
Hello, world!
When this program is executed, the following sequence of events occurs:
1. The main() function is called and its information is pushed onto the
top of the function call stack.
2. The add() function is called and its information is pushed onto the
top of the stack.
3. The add() function executes and returns a result, which is then
assigned to the variable c.
4. The add() function's information is popped off the top of the stack.
5. The printMessage() function is called and its information is pushed
onto the top of the stack.
6. The printMessage() function executes and prints a message to the
screen.
7. The printMessage() function's information is popped off the top of
the stack.
8. The main() function returns and its information is popped off the
top of the stack.
In this example, the function call stack is used to store information about
the main(), add(), and printMessage() functions as they are executed.
When a function is called, its information is pushed onto the top of the
stack, and when the function returns, its information is popped of fromf
the top of the stack. This allows the program to keep track of which
function is currently being executed and to return to the correct location
when a function finishes its execution.
The function call stack is managed by the compiler and is not visible to
the programmer. However, it is an important part of how functions works
in C++ and plays a crucial role in the execution of a program.
Function Declaration
A function declaration tells the compiler about the number of parameters
function takes data-types of parameters, and returns the type of function.
Putting parameter names in the function declaration is optional in the
function declaration, but it is necessary to put them in the definition.
Below are an example of function declarations. (parameter names are not
there in the below declarations)
User Defined Function
Library Function
Library functions are also called “builtin Functions“. These functions are
a part of a compiler package that is already defined and consists of a
special function with special and different meanings. Builtin Function gives
us an edge as we can directly use them without defining them whereas in
the user-defined function we have to declare and define a function before
using them.
For Example: sqrt(), setw(), strcat(), etc.
In C++, the terms "parameters" and "arguments" are related but distinct concepts in the
context of functions. Here’s a breakdown of the differences:
Parameters:
Definition: Parameters are variables listed in the function's definition.
Purpose: They act as placeholders for the values that will be passed to the function.
Scope: They are local to the function and exist only within the function's body.
Arguments:
Definition: Arguments are the actual values or expressions passed to the function
when it is called.
Purpose: They provide the data that the function will operate on.
Scope: They exist in the context where the function is called.
int main() {
return 0;
Key Differences:
1. Location in Code:
o Parameters appear in the function declaration and definition.
o Arguments appear in the function call.
2. Role:
o Parameters specify what kind of data the function expects.
o Arguments provide the actual data that is passed to the function.
3. Binding:
o When a function is called, arguments are passed to the function and assigned
to the corresponding parameters.
Inline functions::
When the program executes the function call instruction the CPU stores
the memory address of the instruction following the function call, copies
the arguments of the function on the stack and finally transfers control to
the specified function. The CPU then executes the function code, stores
the function return value in a predefined memory location/register and
returns control to the calling function. This can become overhead if the
execution time of function is less than the switching time from the caller
function to called function (callee). For functions that are large and/or
perform complex tasks, the overhead of the function call is usually
insignificant compared to the amount of time the function takes to run.
However, for small, commonly-used functions, the time needed to make
the function call is often a lot more than the time needed to actually
execute the function’s code. This overhead occurs for small functions
because execution time of small function is less than the switching time.
MACROS::
In C++, a macro is a preprocessor directive that defines a piece of code which can be reused
throughout the program. Macros are handled by the preprocessor, which processes the source code
before it is compiled by the compiler. They provide a way to define constants, functions, or code
snippets that can be inserted into the program wherever needed.
Types of Macros:
1. Object-like Macros:
o These are similar to constants and are used to define constant values or
expressions.
o Syntax: #define MACRO_NAME value
o Example:
cpp
Copy code
#define PI 3.14159
#include <iostream>
int main() {
std::cout << "Value of PI: " << PI << std::endl;
return 0;
}
2. Function-like Macros:
o These macros resemble functions and can take arguments. However, they are
not type-checked like functions.
o Syntax: #define MACRO_NAME(arguments) code
o Example:
cpp
Copy code
#define SQUARE(x) ((x) * (x))
#include <iostream>
int main() {
std::cout << "Square of 5: " << SQUARE(5) << std::endl;
return 0;
}
Preprocessor Directives:
Advantages of Macros:
1. Code Reusability: Macros can reduce code duplication by defining commonly used
code snippets or values.
2. Readability: They can improve code readability by using meaningful names instead
of hard-coded values or complex expressions.
Disadvantages of Macros:
1. No Type Checking: Macros are not type-checked, which can lead to errors that are
difficult to debug.
2. Debugging Difficulties: Since macros are expanded by the preprocessor, it can be
challenging to trace errors in the expanded code.
3. Code Bloat: Overuse of macros, especially function-like macros, can lead to code
bloat as the macro code is inserted each time it is used.
int main() {
int result = SQUARE(5 + 2); // This will expand to ((5 + 2) * (5 + 2))
which is incorrect
std::cout << "Result: " << result << std::endl; // Outputs 49 instead
of 49
return 0;
}
Best Practices:
Use macros for simple constants.
Prefer inline functions or const variables for more complex operations or values to
benefit from type safety and better debugging support.
Yes, the use of the macro INCREMENT_AND_SQUARE with an argument like y++ can lead to
unexpected behavior because the argument is evaluated multiple times. This can cause side
effects such as incrementing y more than once, which is often not the intended behavior.
Here's a detailed explanation:
When you use the macro INCREMENT_AND_SQUARE(y++), the macro expands as follows:
cpp
Copy code
#define INCREMENT_AND_SQUARE(x) ((x + 1) * (x + 1))
// Expansion
std::cout << ((y++ + 1) * (y++ + 1));
The parameters should follow any one or more than one of the following
conditions for Function overloading:
Loops:
Using Loops
In Loop, the statement needs to be written only once and the loop will be
executed 10 times as shown below. In computer programming, a loop is a
sequence of instructions that is repeated until a certain condition is
reached.
While studying for loop we have seen that the number of iterations
is known beforehand, i.e. the number of times the loop body is needed
to be executed is known to us. while loops are used in situations
where we do not know the exact number of iterations of the
loop beforehand. The loop execution is terminated on the basis of the
test conditions.
We have already stated that a loop mainly consists of three statements –
initialization expression, test expression, and update expression. The
syntax of the three loops – For, while, and do while mainly differs in the
placement of these three statements.
Syntax:
initialization expression;
while (test_expression)
{
// statements
update_expression;
}
A for loop is a repetition control structure that allows us to write a loop
that is executed a specific number of times. The loop enables us to
perform n number of steps together in one line.
Syntax:
for(auto element:arr)
{
//BODY
}
In for loop, a loop variable is used to control the loop. First, initialize this
loop variable to some value, then check whether this variable is less than
or greater than the counter value. If the statement is true, then the loop
body is executed and the loop variable gets updated. Steps are repeated
till the exit condition comes.
update_expression;
}
while (test_expression);
Note: Notice the semi – colon(“;”) in the end of loop.
The various parts of the do-while loop are:
break;
Basically break statements are used in the situations when we are not
sure about the actual number of iterations for the loop or we want to
terminate the loop based on some condition.
continue;
Array
// Driver code
int main()
{
int arr[] = {2, -1, 5, 6, 0, -3};
int n = sizeof(arr) / sizeof(arr[0]);
printArray(arr, n);
return 0;
}
// C++ program to traverse the
// array using range-based loop
#include <bits/stdc++.h>
#include <iostream>
using namespace std;
// Driver code
int main()
{
int arr[] = {2, -1, 5, 6, 0, -3};
Applications :
In C++, function parameters are variables that are used to pass data into
a function when it is called. Function parameters are defined in the
function's declaration and are used to receive the data that is passed to
the function when it is called.
Vectors in C++ are part of the Standard Template Library (STL) and provide a dynamic array
functionality, which can grow and shrink in size as needed. Here are some key aspects of
using vectors in C++:
You can declare a vector of a specific type using the std::vector template class.
cpp
Copy code
#include <iostream>
#include <vector>
int main() {
std::vector<int> numbers; // Declare a vector of integers
std::vector<int> numbers2(10); // Declare a vector of integers with
size 10, initialized to 0
std::vector<int> numbers3 = {1, 2, 3, 4, 5}; // Initialize with a list
of values
return 0;
}
2. Adding Elements
cpp
Copy code
#include <iostream>
#include <vector>
int main() {
std::vector<int> numbers;
numbers.push_back(1);
numbers.push_back(2);
numbers.push_back(3);
return 0;
}
3. Accessing Elements
You can access elements using the [] operator or the at() method.
cpp
Copy code
#include <iostream>
#include <vector>
int main() {
std::vector<int> numbers = {1, 2, 3, 4, 5};
return 0;
}
You can use a range-based for loop or iterators to iterate over the elements of a vector.
cpp
Copy code
#include <iostream>
#include <vector>
int main() {
std::vector<int> numbers = {1, 2, 3, 4, 5};
// Using iterators
for (std::vector<int>::iterator it = numbers.begin(); it !=
numbers.end(); ++it) {
std::cout << *it << " ";
}
return 0;
}
5. Removing Elements
You can remove elements using the pop_back() method, or using the erase() method for
more control.
cpp
Copy code
#include <iostream>
#include <vector>
int main() {
std::vector<int> numbers = {1, 2, 3, 4, 5};
return 0;
}
cpp
Copy code
#include <iostream>
#include <vector>
int main() {
std::vector<int> numbers = {1, 2, 3, 4, 5};
std::cout << "Size: " << numbers.size() << std::endl; // Get the
number of elements
std::cout << "Capacity: " << numbers.capacity() << std::endl; // Get
the capacity of the vector
std::cout << "Is empty: " << std::boolalpha << numbers.empty() <<
std::endl; // Check if the vector is empty
return 0;
}
7. Resizing a Vector
You can change the size of a vector using the resize() method.
cpp
Copy code
#include <iostream>
#include <vector>
int main() {
std::vector<int> numbers = {1, 2, 3, 4, 5};
return 0;
}
8. Clearing a Vector
cpp
Copy code
#include <iostream>
#include <vector>
int main() {
std::vector<int> numbers = {1, 2, 3, 4, 5};
std::cout << "Size after clearing: " << numbers.size() << std::endl;
return 0;
}
Summary
Vectors in C++ are versatile and powerful containers that handle dynamic arrays with ease.
They provide various methods to manipulate and access the data efficiently, making them an
essential tool in any C++ programmer's toolkit.
The #include <map> directive in C++ includes the map container from the Standard
Template Library (STL). A map is an associative container that stores key-value pairs, where
each key is unique and is used to associate with its corresponding value. Here are some key
aspects of using maps in C++:
You can declare a map by specifying the types for the key and the value.
cpp
Copy code
#include <iostream>
#include <map>
int main() {
std::map<int, std::string> idToName; // Declare a map with integer
keys and string values
std::map<std::string, int> wordCount = {{"hello", 1}, {"world",
2}}; // Initialize with a list of key-value pairs
return 0;
}
2. Adding Elements
Elements can be added to a map using the operator[] or the insert() method.
cpp
Copy code
#include <iostream>
#include <map>
int main() {
std::map<int, std::string> idToName;
idToName[1] = "Alice";
idToName[2] = "Bob";
return 0;
}
3. Accessing Elements
You can access elements using the operator[] or the at() method.
cpp
Copy code
#include <iostream>
#include <map>
int main() {
std::map<int, std::string> idToName = {{1, "Alice"}, {2, "Bob"}};
std::cout << idToName[1] << std::endl; // Access the value with key 1
std::cout << idToName.at(2) << std::endl; // Access the value with key
2 using at()
return 0;
}
You can use a range-based for loop or iterators to iterate over the elements of a map.
cpp
Copy code
#include <iostream>
#include <map>
int main() {
std::map<int, std::string> idToName = {{1, "Alice"}, {2, "Bob"}, {3,
"Charlie"}};
// Using iterators
for (std::map<int, std::string>::iterator it = idToName.begin(); it !=
idToName.end(); ++it) {
std::cout << it->first << ": " << it->second << std::endl;
}
return 0;
}
5. Removing Elements
cpp
Copy code
#include <iostream>
#include <map>
int main() {
std::map<int, std::string> idToName = {{1, "Alice"}, {2, "Bob"}, {3,
"Charlie"}};
return 0;
}
6. Finding Elements
You can find elements using the find() method, which returns an iterator to the element if
found, or to map::end if not.
cpp
Copy code
#include <iostream>
#include <map>
int main() {
std::map<int, std::string> idToName = {{1, "Alice"}, {2, "Bob"}, {3,
"Charlie"}};
auto it = idToName.find(2);
if (it != idToName.end()) {
std::cout << "Found: " << it->first << ": " << it->second <<
std::endl;
} else {
std::cout << "Not found" << std::endl;
}
return 0;
}
You can check the size of a map and whether it is empty using the size() and empty()
methods.
cpp
Copy code
#include <iostream>
#include <map>
int main() {
std::map<int, std::string> idToName = {{1, "Alice"}, {2, "Bob"}, {3,
"Charlie"}};
std::cout << "Size: " << idToName.size() << std::endl; // Get the
number of elements
std::cout << "Is empty: " << std::boolalpha << idToName.empty() <<
std::endl; // Check if the map is empty
return 0;
}
Summary
Maps in C++ are efficient for scenarios where you need to associate unique keys with
specific values. They provide various methods to manipulate and access the data, making
them a fundamental part of the STL for managing associative arrays.
C++ 17 or higher: Range-based loops can also be used with maps like
this:
#include <iostream>
using namespace std;
int main() {
printNumber(3);
return 0;
}
Lvalues
An lvalue (locator value) refers to an object that occupies some identifiable location in
memory (i.e., it has an address). Lvalues can appear on the left-hand side or the right-hand
side of an assignment.
Examples of Lvalues:
1. Variables:
cpp
Copy code
int x = 10; // x is an lvalue
x = 20; // Valid: x can be assigned a new value
2. Dereferenced Pointers:
cpp
Copy code
int x = 10;
int* p = &x;
*p = 20; // *p is an lvalue referring to x
3. Array Elements:
cpp
Copy code
int arr[3] = {1, 2, 3};
arr[0] = 10; // arr[0] is an lvalue
4. Class Members:
cpp
Copy code
struct Point {
int x;
int y;
};
Point p;
p.x = 10; // p.x is an lvalue
Rvalues
An rvalue (right value) is an expression that does not refer to a memory location and is
typically a temporary value that doesn't persist beyond the expression that uses it. Rvalues
can only appear on the right-hand side of an assignment.
Examples of Rvalues:
1. Literals:
cpp
Copy code
int x = 42; // 42 is an rvalue
2. Temporary Objects:
cpp
Copy code
int y = x + 1; // x + 1 is an rvalue
cpp
Copy code
int foo() {
return 42; // 42 is an rvalue
}
cpp
Copy code
int x = 10;
x = 20; // x is an lvalue on the left-hand side of the assignment
int y = x; // x is an lvalue on the right-hand side of the
assignment
cpp
Copy code
int x = 10;
int y = x + 5; // x + 5 is an rvalue
cpp
Copy code
int x = 10;
x = 20; // x is a modifiable lvalue
cpp
Copy code
const int x = 10;
// x = 20; // Error: x is a non-modifiable lvalue
cpp
Copy code
int x = 10;
int& ref = x; // ref is an lvalue reference to x
ref = 20;
cpp
Copy code
int&& rref = 10; // rref is an rvalue reference to the rvalue 10
Summary
Lvalue: An expression that refers to a memory location and can appear on both sides of an
assignment.
Rvalue: A temporary value that cannot appear on the left-hand side of an assignment.
Modifiable Lvalue: A lvalue that can be changed.
Non-modifiable Lvalue: A lvalue that cannot be changed (e.g., const variables).
Lvalue References: Used to refer to lvalues.
Rvalue References: Used to refer to rvalues (introduced in C++11).
Understanding the difference between lvalues and rvalues is crucial for mastering C++
programming, especially when dealing with advanced topics like move semantics and perfect
forwarding.
int x = 5; // initialize x to 5
int* ptr = &x; // take the address of x and store it in ptr
It's also a good idea to be mindful of the lifetime of variables when taking
their address. If a variable goes out of scope or is deallocated, its memory
address is no longer valid, and taking the address of the variable can lead
to undefined behavior. To avoid this problem, make sure that the variable
is still in scope and has not been deallocated before taking its address.
To avoid these problems, it's important to always make sure that a pointer
is properly initialized and points to a valid memory location before
dereferencing it. One way to do this is to initialize pointers to nullptr,
which is a special value that represents a null pointer:
The address of the variable you’re working with is assigned to the pointer
variable that points to the same data type (such as an int or string).
Features of Pointers:
1.
To pass arguments by reference
2.
For accessing array elements
3.
To return multiple values
4.
Dynamic memory allocation
5.
To implement data structures
6.
To do system-level programming where memory addresses are
useful
Drawbacks of Pointers:
#include <iostream>
void increment(int* x)
{
(*x)++; // increment the value stored at the memory address pointed to
by x
}
int main()
{
int x = 5;
std::cout << "x before increment: " << x << std::endl;
return 0;
}
Output
x before increment: 5
x after increment: 6
In C++, you can pass arrays as function parameters to modify the elements of
an array in a function.
#include <iostream>
int main()
{
int arr[] = { 1, 2, 3, 4, 5 };
int size = sizeof(arr) / sizeof(arr[0]);
increment(arr, size); // pass the array and the size of the array to
the increment function
return 0;
}
Output
2 3 4 5 6
the function increment takes an int array and an int as parameters, and
modifies the values stored at each element of the array. This has the
effect of modifying the elements of arr in the main function, even though
arr is not returned by the increment function.
The pointer can be used to access the array elements, accessing the
whole array using pointer arithmetic, makes the accessing faster. The
main difference between Array and Pointers is the fixed size of the
memory block. When Arrays are created the fixed size of the memory
block is allocated. But with Pointers the memory is dynamically allocated.
There are some other differences between an array and a pointer which
are discussed below in the table.
In C++, the distinction between compile-time and run-time initialization is important for
understanding how variables are set up and used in your programs. Here’s a breakdown of
what is initialized at compile-time versus run-time:
Compile-Time Initialization
Compile-time initialization occurs when the value of a variable is determined at the time the
code is compiled. This typically applies to:
1. Constant Expressions: Values that are known at compile time, such as literals or
expressions that can be evaluated by the compiler.
cpp
Copy code
const int x = 10; // Initialized at compile time
constexpr int y = x + 5; // Also initialized at compile time
2. Global and Static Variables: These variables are initialized before the main program
starts executing.
cpp
Copy code
int globalVar = 100; // Initialized at compile time
static int staticVar = 200; // Initialized at compile time
Run-Time Initialization
Run-time initialization occurs when the value of a variable is determined during the
execution of the program. This includes:
1. Local Variables: Variables declared inside a function and initialized with values
computed at run-time.
cpp
Copy code
int main() {
int a = 5; // Initialized at run time
int b = a * 2; // Initialized at run time
return 0;
}
cpp
Copy code
int* ptr = new int(10); // Initialized at run time
3. User Input: Values that are obtained from user input or external sources during the
execution of the program.
cpp
Copy code
int main() {
int userInput;
std::cin >> userInput; // Initialized at run time
return 0;
}
1. Compile-Time Example:
cpp
Copy code
#include <iostream>
int main() {
constexpr int a = compileTimeValue(); // Compile-time
initialization
std::cout << "Value of a: " << a << std::endl;
return 0;
}
2. Run-Time Example:
cpp
Copy code
#include <iostream>
int runTimeValue() {
return 20;
}
int main() {
int b = runTimeValue(); // Run-time initialization
std::cout << "Value of b: " << b << std::endl;
return 0;
}
Summary
Understanding this distinction helps in optimizing programs and ensuring correct behavior,
especially in performance-critical applications.
In C++, the NULL macro represents a null pointer, which is a special pointer value that
indicates that the pointer does not point to a valid memory location. A null pointer does not
point to any object or memory location, and dereferencing a null pointer can cause a runtime
error or crash.
C++
#include <iostream>
int main()
{
int* ptr = NULL; // initialize ptr to NULL
return 0;
}
Output
ptr is a null pointer
the NULL macro is used to initialize the pointer ptr to a null pointer, and to check if ptr is a
null pointer.
It's important to be careful when working with null pointers in C++, as dereferencing a null
pointer can cause a runtime error or crash. Make sure to always check for null pointers before
dereferencing them to avoid these problems.
In C++11 and later, the nullptr keyword is preferred over the NULL macro for representing
null pointers. The nullptr keyword is a null pointer literal that is guaranteed to be of
type nullptr_t, which is a distinct type that is not implicitly convertible to any other type.
This helps avoid problems with the NULL macro, which is often defined as a constant
integer value, and can be accidentally converted to an integer type in certain contexts.
There are several common use cases for the NULL macro in C++:
Initializing pointers to null: The NULL macro is often used to initialize pointers to a
null pointer value, indicating that the pointer does not point to a valid memory
location. This can be useful to prevent dereferencing uninitialized pointers, which can
lead to runtime errors or crashes.
Checking for null pointers: The NULL macro is often used to check if a pointer is a
null pointer, which can be useful to avoid dereferencing null pointers and causing
runtime errors or crashes.
Providing a default value for pointers: The NULL macro is sometimes used as a
default value for pointers, indicating that the pointer does not point to a valid memory
location. This can be useful in situations where a pointer may not always be
initialized, such as in function arguments or class member variables.
Representing the absence of an object: In some cases, the NULL macro is used to
represent the absence of an object, such as in linked lists or trees where a null pointer
indicates the end of the list or the absence of a child node.
The value of the NULL macro is implementation-defined and may vary depending on the
platform and compiler being used. In most cases, the NULL macro is defined as a constant
integer value that is equal to zero, such as #define NULL 0.
// This program compiles
#include<iostream>
using namespace std;
int main()
{
int *ptr = nullptr;
incremented ( ++ )
decremented ( — )
an integer may be added to a pointer ( + or += )
an integer may be subtracted from a pointer ( – or -= )
difference between two pointers (p1-p2)
(Note: Pointer arithmetic is meaningless unless performed on an array.)
// Driver program
int main() { geeks(); }
For normal variables like “int a”, “char str[10]”, etc, memory is
automatically allocated and deallocated. For dynamically allocated
memory like “int *p = new int[10]”, it is the programmer’s responsibility
to deallocate memory when no longer needed. If the programmer doesn’t
deallocate memory, it causes a memory leak (memory is not deallocated
until the program terminates).
new operator
The new operator denotes a request for memory allocation on the Free
Store. If sufficient memory is available, a new operator initializes the
memory and returns the address of the newly allocated and initialized
memory to the pointer variable.
delete p;
delete q;
To free the dynamically allocated array pointed by pointer variable, use the following form
of delete:
Example:
int main ()
{
// Pointer initialization to null
int* p = NULL;
if (!q)
cout << "allocation of memory failed\n";
else
{
for (int i = 0; i < n; i++)
q[i] = i+1;
return 0;
}
Output
Value of p: 29
Value of r: 75.25
Value store in block of memory: 1 2 3 4 5
Marked as Read
Report An Issue
The functions cin and getline are both used in C++ for input operations but serve different
purposes and have distinct behaviors.
cin
Purpose: Used to read input from the standard input (usually the keyboard).
Type: Reads formatted input.
Usage:
cpp
Copy code
int age;
std::cin >> age;
Behavior:
o Stops reading at the first whitespace character (space, tab, newline).
o Suitable for reading single word or number inputs.
o Ignores leading whitespace characters.
getline
Purpose: Used to read an entire line of text from the standard input.
Type: Reads unformatted input.
Usage:
cpp
Copy code
std::string name;
std::getline(std::cin, name);
Behavior:
o Reads characters until a newline character is encountered.
o Includes leading and trailing whitespace characters in the input.
o More suitable for reading strings that may contain spaces, like full names or
sentences.
Key Differences
1. Stopping Condition:
o cin stops reading at the first whitespace.
o getline stops reading at a newline character.
2. Handling Whitespace:
o cin skips leading whitespace and stops at the first whitespace after the input.
o getline includes all whitespace characters in the input.
3. Use Case:
o cin is ideal for simple, single-value inputs (like an integer or a single word).
o getline is ideal for capturing entire lines of text (like a full sentence or
multiple words).
cpp
Copy code
#include <iostream>
#include <string>
int main() {
int age;
std::string name;
std::cout << "Name: " << name << ", Age: " << age << std::endl;
return 0;
}
In this example:
Understanding these differences allows you to choose the appropriate function based on your
input needs in a C++ program.
The std::flush manipulator in C++ is used to flush the output buffer of a stream. When you
use std::flush, it forces the buffer associated with the stream to be flushed to its
destination. This is particularly useful in the following scenarios:
literals are "directly represented in code," we mean that these values are explicitly written out in the
source code rather than being stored in variables or derived from expressions. These are the raw,
fixed values that the program uses.
1. Integer Literals: When you write 42 in your code, it is directly understood by the
compiler as the integer value 42. There's no need for computation or variable
assignment; the number 42 is exactly what it appears to be.
cpp
Copy code
int a = 42; // Here, 42 is a literal.
cpp
Copy code
double pi = 3.14; // Here, 3.14 is a literal.
3. Character Literals: Writing 'a' in your code directly represents the character a.
This is not a variable or an expression; it’s the character a itself.
cpp
Copy code
char letter = 'a'; // Here, 'a' is a literal.
4. String Literals: When you write "Hello, World!", it directly represents a sequence
of characters. The entire string is embedded in the code as is.
cpp
Copy code
const char* greeting = "Hello, World!"; // Here, "Hello, World!" is
a literal.
5. Boolean Literals: The values true and false are directly understood by the
compiler as boolean values.
cpp
Copy code
bool isTrue = true; // Here, true is a literal.
6. Null Pointer Literal: Writing nullptr directly represents a null pointer value.
cpp
Copy code
int* ptr = nullptr; // Here, nullptr is a literal.
Practical Implications
Example to Illustrate
cpp
Copy code
#include <iostream>
int main() {
int age = 25; // 25 is an integer literal
double pi = 3.14159; // 3.14159 is a floating-point literal
char grade = 'A'; // 'A' is a character literal
const char* message = "Hello"; // "Hello" is a string literal
bool isValid = true; // true is a boolean literal
int* ptr = nullptr; // nullptr is a null pointer literal
return 0;
}
In this example:
These values are embedded directly in the source code, representing specific fixed values the
program uses. They do not need to be computed or retrieved from somewhere else—they are
the exact values as written.
The constexpr keyword in C++ is used to declare that a variable, function, or constructor
can be evaluated at compile time. This allows the compiler to compute values during
compilation, which can improve performance and enable certain optimizations. Here’s a
detailed look at constexpr:
constexpr Variables
Definition:
constexpr variables are constants whose values are known and computed at compile
time.
Characteristics:
cpp
Copy code
constexpr int maxSize = 100; // Must be a compile-time constant
constexpr Functions
Definition:
Functions declared with constexpr are functions that can be evaluated at compile
time if all their arguments are compile-time constants.
Characteristics:
Example:
cpp
Copy code
constexpr int square(int x) {
return x * x;
}
constexpr Constructors
Definition:
Characteristics:
Example:
cpp
Copy code
class MyClass {
public:
constexpr MyClass(int x) : value(x) {}
constexpr int getValue() const { return value; }
private:
int value;
};
constexpr MyClass obj(10); // Object is created and initialized at compile
time
constexpr int val = obj.getValue(); // Value is evaluated at compile time
Example Comparison
const Example:
cpp
Copy code
const int runtimeValue = getRuntimeValue(); // Initialized at runtime
constexpr Example:
cpp
Copy code
constexpr int compileTimeValue = 42; // Evaluated at compile time
constexpr int squaredValue = square(6); // Evaluated at compile time
Summary
constexpr Variables: Must be initialized with constant expressions; their values are
determined at compile time.
constexpr Functions: Can be evaluated at compile time when given constant
arguments; useful for compile-time computations.
constexpr Constructors: Allow objects to be initialized at compile time if the
constructor and its parameters are constant expressions.
constexpr enhances performance and flexibility by ensuring that certain values and
computations are resolved during compilation, leading to more efficient and potentially more
optimized code.
Definition:
Purpose:
The primary purpose of constinit is to ensure that a variable is initialized before any
dynamic initialization happens (i.e., before the program’s execution starts), but unlike
constexpr, it does not require the variable to be immutable or used in constant
expressions.
Key Characteristics
Example
Using constinit:
cpp
Copy code
constinit int myValue = 10; // Ensures initialization at compile time, but
'myValue' can be modified
In this example, myValue will be initialized at compile time, but unlike constexpr, it can be
changed later in the code.
const:
o Ensures immutability after initialization but does not require compile-time
initialization.
o Example: const int x = 5; (x can be initialized at runtime.)
constexpr:
o Requires compile-time evaluation and ensures immutability.
o Example: constexpr int y = 10; (y must be initialized with a compile-time
constant.)
constinit:
o Ensures compile-time initialization but does not imply immutability.
o Example: constinit int z = 20; (z is initialized at compile time and can
be modified.)
Summary
constinit is particularly useful for variables that must be initialized at compile time but
where immutability is not a requirement. It provides flexibility for cases where compile-time
initialization is crucial, but runtime modification of the variable’s value is still needed.
Static Storage Duration: Variables with static storage duration are initialized once and exist for the
lifetime of the program. constinit can be used with these variables to ensure they are initialized
at compile time.
The std namespace is a special namespace that contains all the classes, objects, and functions of the
C++ Standard Library.
If you want to use std::getline with an std::string, use the standard getline function.
Here’s how you can use both methods correctly:
int main() {
std::string line;
std::cout << "Enter a line of text: ";
std::getline(std::cin, line); // Read the entire line as a string
std::cout << "You entered: " << line << std::endl;
return 0;
}
If you want to limit the number of characters read, you need to use a character array:
cpp
Copy code
#include <iostream>
int main() {
char line[6]; // Create a character array with a size of 6
std::cout << "Enter a line of text: ";
std::cin.getline(line, 6); // Read up to 5 characters (6th is for null
terminator)
std::cout << "You entered: " << line << std::endl;
return 0;
}
Explanation
cpp
Copy code
#include <iostream>
#include <string>
int main() {
std::string line;
std::cout << "Enter a line of text: ";
std::getline(std::cin, line); // Use std::getline with std::string
std::cout << "You entered: " << line << std::endl;
return 0;
}
Or, if you want to limit the input to a certain number of characters and use a C-style character
array:
cpp
Copy code
#include <iostream>
int main() {
char line[6]; // Define a character array with a size of 6
std::cout << "Enter a line of text (max 5 characters): ";
std::cin.getline(line, 6); // Read up to 5 characters
std::cout << "You entered: " << line << std::endl;
return 0;
}
Use std::getline for dynamic strings with no fixed length, and std::cin.getline for
fixed-size character arrays with a specified limit.
When you work with arrays in C++, p and &p can indeed produce similar addresses but
represent different types. Here’s what happens in your code snippet:
cpp
Copy code
#include<iostream>
using namespace std;
int main() {
int p[] = {1, 2, 3};
cout << p << endl;
cout << &p << endl;
}
In this particular case, p and &p will usually print the same address, because p decays to a
pointer to the first element, and &p provides the address of the array. However, the types of
these expressions are different:
C++
// C++ program to demonstrate
// Strings using C style
#include <iostream>
using namespace std;
int main()
{
// Print string
cout << str;
return 0;
}
Output
Geeks
Standard String representation and String Class: In C++, one can
directly store the collection of characters or text in a string variable,
surrounded by double-quotes. C++ provides string class, which supports
various operations like copying strings, concatenating strings etc.
C++
// C++ program to demonstrate String
// using Standard String representation
#include <iostream>
#include <string>
using namespace std;
int main()
{
// Print string
cout << str1 << endl << str2;
return 0;
}
Output
Welcome to GeeksforGeeks!
A Computer Science Portal
1. Finding the length of a string: To find the length of a string, you can
use the length member function of the std::string class.
The length function returns the number of characters in the string,
not including the null terminator.
For example:
C++
#include <iostream>
#include <string>
int main()
{
std::string str = "hello";
std::cout << "The length of the string is " << str.length() << "
characters." << std::endl;
return 0;
}
Output
The length of the string is 5 characters.
2.Extracting a substring: To extract a substring from a string, you can
use the substr member function of the std::string class.
The substr function takes two arguments: the starting position
of the substring, and the length of the substring.
For example:
C++
#include <iostream>
#include <string>
int main()
{
std::string str = "hello world";
std::cout << "The substring is: " << sub << std::endl;
return 0;
}
Output
The substring is: world
Searching for a substring: To search for a substring within a string, you
can use the find member function of the std::string class. The find function
searches for a given substring within a string and returns the position of
the first occurrence of the substring, or `std::string::n
The find function takes two arguments: the substring to search for, and
the starting position of the search. By default, the search starts at the
beginning of the string, but you can specify a different starting position if
you want to search only a portion of the string.
C++
#include <iostream>
#include <string>
int main()
{
std::string str = "hello world";
std::size_t pos = str.find("l", 3); // search for the first occurrence
of 'l' starting at position 3
return 0;
}
Output
The substring was found at position 3
Note that the find function is case-sensitive, so it will only find substrings
that match the case of the search string. If you want to perform a case-
insensitive search, you can use the find function in combination with
the tolower or toupper functions from the cctype library.
C++
#include <cctype> // include the cctype library for tolower and toupper
#include <iostream>
#include <string>
int main()
{
std::string str = "Hello World";
Output
The substring was not found.
Reading a string with spaces in C++ can be a bit tricky, as the standard cin function and
the getline() function behave differently when it comes to handling whitespace characters. In
this article, we'll discuss two common methods for reading strings with spaces in C++:
using getline() and using cin with the ignore() function.
Method 1: Using getline() The getline() function is specifically designed to read a line of
text, including any whitespace characters that may be present. It takes two parameters: an
input stream (such as cin) and a string variable to store the input. Here's an example of how
to use getline() to read a string with spaces:
C++
#include <iostream>
#include <string>
using namespace std;
int main() {
string input;
cout << "Enter a string with spaces: ";
getline(cin, input);
cout << "You entered: " << input << endl;
return 0;
}
C++
#include <iostream>
#include <string>
using namespace std;
int main() {
string input;
cout << "Enter a string with spaces: ";
cin >> input;
cin.ignore(numeric_limits<streamsize>::max(), '\n');
getline(cin, input);
cout << "You entered: " << input << endl;
return 0;
}
Both of the above methods will allow you to read a string with spaces in C++. However, it's
important to note that using the cin with ignore() will ignore the characters in the input
stream until the given number of characters, specified in the second parameter
of ignore() function, or until a newline character ('\n') is encountered.
When using getline(), you should be careful to remember that the newline character at the
end of the input will be included in the string, which may cause problems if you're expecting
the string not to contain a newline character.
In conclusion, both method can be used for reading strings with spaces in C++ but the
appropriate method should be chosen according to the requirement of the problem.
A modifier function (or mutator function) is a type of function that alters the state or
content of an object. In the context of the std::string class in C++, modifier functions are
those that change the actual content, size, or properties of a string object.
1. Change the State: They modify the data members (like the characters in a string) or
properties of the object they are called on.
2. Non-const: Since they modify the object, they are not marked as const and cannot be
called on const objects.
3. Return Type: Some modifier functions return the modified object itself (to allow for
method chaining), while others may return void or other relevant data.
Examples in std::string:
cpp
Copy code
std::string str = "Hello";
str.append(" World"); // str becomes "Hello World"
cpp
Copy code
std::string str = "Hello!";
str.insert(5, ", World"); // str becomes "Hello, World!"
cpp
Copy code
std::string str = "Hello, World!";
str.erase(5, 7); // str becomes "Hello"
cpp
Copy code
std::string str = "Hello, World!";
str.replace(7, 5, "Universe"); // str becomes "Hello, Universe!"
cpp
Copy code
std::string str = "Hello!";
str.clear(); // str becomes ""
cpp
Copy code
std::string str = "Hello";
str.resize(10, '!'); // str becomes "Hello!!!!!"
cpp
Copy code
std::string str = "Hello";
str.push_back('!'); // str becomes "Hello!"
cpp
Copy code
std::string str = "Hello!";
str.pop_back(); // str becomes "Hello"
In C++, a macro is a preprocessor directive that allows you to define a piece of code that can
be reused throughout your program. Macros are created using the #define directive, and they
essentially perform a text substitution before the compilation process begins.
cpp
Copy code
#define PI 3.14159
In this case, every occurrence of PI in the code will be replaced by 3.14159 during
preprocessing.
2. Function-like macros: These resemble function calls but perform simple textual
replacements and do not involve any actual function call overhead. For example:
cpp
Copy code
#define SQUARE(x) ((x) * (x))
This macro takes an argument x and replaces SQUARE(x) with ((x) * (x)) wherever
it appears in the code.
No Type Checking: Macros do not perform type checking, which means they can
lead to errors if not used carefully. For example, SQUARE(1 + 2) will be replaced by
((1 + 2) * (1 + 2)), which expands to ((3) * (3)), giving the correct result.
However, without careful use of parentheses in the macro definition, such as #define
SQUARE(x) x * x, the expansion would result in 1 + 2 * 1 + 2, which evaluates
incorrectly due to operator precedence.
Scope and Lifetime: Macros are not affected by scopes in the same way as variables
and functions. Once defined, they are globally replaced throughout the entire code file
until they are explicitly undefined using #undef.
Use in Conditional Compilation: Macros are often used for conditional compilation
using preprocessor directives like #ifdef, #ifndef, #if, #else, #elif, and #endif.
This allows certain parts of the code to be compiled or ignored based on whether a
macro is defined.
Preprocessor: Since macros are processed by the preprocessor, they are not seen by
the compiler in their macro form. The preprocessor replaces the macro with its
defined text, and the compiler only sees the resulting code after substitution.
cpp
Copy code
#include <iostream>
int main() {
double radius = 5.0;
std::cout << "Area of the circle: " << PI * SQUARE(radius) <<
std::endl;
return 0;
}
In this example:
The use of macros should be approached with caution in modern C++ programming due to
potential pitfalls with debugging, type safety, and readability. In many cases, inline functions
or constants (const or constexpr) are preferred over macros for better safety and clarity.
The process of building a C++ program with multiple files involves several steps:
preprocessing, compilation, and linking. Here’s a detailed explanation of each step:
1. Preprocessing
The preprocessing step handles all the preprocessor directives, which are lines in your code
that start with the # symbol. The main tasks performed during preprocessing include:
Macro Expansion: This replaces defined macros with their corresponding values or
code snippets.
File Inclusion: The #include directive includes the content of a specified file,
usually a header file, directly into the source file. This is essential for separating
declarations (.h or .hpp files) from definitions (.cpp files).
Conditional Compilation: Directives like #ifdef, #ifndef, #if, #else, #elif, and
#endif control the compilation of parts of the code based on conditions.
Removing Comments: The preprocessor strips out all comments from the code, as
these are not needed for the actual compilation process.
The result of preprocessing is an expanded source file without any preprocessor directives.
2. Compilation
After preprocessing, the next step is compilation. This phase converts the preprocessed code
(which is still in human-readable C++ form) into machine code, but only for individual
source files. The compiler performs several tasks here:
Syntax Checking: The compiler checks for syntax errors in the source code. If there
are any syntax errors, the compilation process stops, and the compiler outputs an error
message.
Semantic Analysis: The compiler also checks for semantic errors, like using a
variable that hasn’t been declared.
Intermediate Code Generation: The compiler translates the source code into an
intermediate code (like assembly language).
Optimization: Depending on the compiler settings, optimizations can be applied to
make the code run faster or use less memory.
Machine Code Generation: The intermediate code is then converted into machine
code (binary code), producing an object file (usually with a .o or .obj extension).
3. Linking
Linking is the final step in creating an executable program from multiple object files. The
linker takes all the compiled object files and combines them into a single executable file. This
process involves:
Resolving Symbol References: Object files may reference symbols (like variables or
functions) that are defined in other object files or libraries. The linker matches these
symbols with their definitions, ensuring that all references are properly resolved.
Combining Object Files: The linker combines the machine code from all the object
files into a single executable or library. If there are any missing symbols (e.g., calling
a function that wasn’t defined in any of the object files or linked libraries), the linker
will throw an error.
Address Binding: The linker assigns final memory addresses to code and data in the
executable, deciding where everything will reside in memory.
For a C++ program with multiple files, the process looks something like this:
Example
Let's say you have a simple program with two files: main.cpp and utils.cpp, and a header
file utils.h.
1. Preprocessing:
o main.cpp and utils.cpp are preprocessed, expanding any macros and
including header files like utils.h.
2. Compilation:
o main.cpp is compiled to main.o.
o utils.cpp is compiled to utils.o.
3. Linking:
o main.o and utils.o are linked together to create the final executable (e.g.,
program.exe on Windows or program on Linux).
Understanding these steps is crucial for working with larger projects where source code is
split across multiple files for modularity and reuse.
An object file is a file produced by a compiler after it processes a source code file. It contains
machine code, but it is not yet a complete executable program. Object files are an
intermediate step between source code and the final executable.
1. Machine Code: Object files contain machine code generated by the compiler from
your source code. This machine code is specific to the target CPU architecture and
operating system but not yet complete for execution.
2. Symbol Table: Object files include a symbol table, which lists all the symbols (such
as function and variable names) defined or used in the source code. Some symbols are
defined within the object file, while others are referenced but not defined (like
external functions or variables from other files or libraries).
3. Relocation Information: Since object files are not yet linked, they often contain
references to addresses that are not yet final. The relocation information helps the
linker adjust addresses when combining multiple object files into a single executable,
ensuring all references are correct.
4. Sections: Object files are typically divided into several sections:
o .text: This section contains the actual compiled machine code for functions
and instructions.
o .data: This section contains initialized global and static variables.
o .bss: This section contains uninitialized global and static variables (variables
that are zero-initialized by default).
o .rodata: This section holds read-only data, such as string literals and constant
values.
5. Not Executable: By itself, an object file cannot be executed. It lacks the necessary
startup code and linkages between all parts of a program. It's a piece of a larger puzzle
that the linker later completes.
Object files serve as building blocks for creating the final executable program. Here’s how
they fit into the compilation process:
1. Compiling Source Code: Each source code file (.cpp or .c) is compiled into its own
object file (.o or .obj depending on the operating system).
2. Linking: The linker takes all object files generated from the different source files and
combines them into a single executable or library. During this process, the linker:
o Resolves references between object files (e.g., functions or variables declared
in one file and used in another).
o Resolves references to libraries and standard library functions.
o Adjusts addresses based on the final layout of the executable in memory.
Example
Imagine a program split into two source files: main.cpp and utils.cpp.
cpp
Copy code
#include "utils.h"
int main() {
printHello();
return 0;
}
cpp
Copy code
#include <iostream>
void printHello() {
std::cout << "Hello, world!" << std::endl;
}
When compiled:
Neither main.o nor utils.o is executable on its own. The linker combines these two object
files, resolving the reference to printHello in main.o with its definition in utils.o, and
produces the final executable.
Sure! Let's break down the #include <iostream> directive in C++ by explaining each
component:
1. # (Hash Symbol)
Include Directive: The include keyword tells the preprocessor to include the
contents of a specified file into the source code at the point where the directive
appears.
Functionality: It's essentially a way to copy the contents of one file (usually header
files containing declarations) into another, allowing you to use the functions and
classes defined in that file.
3. <iostream>
Header File: iostream is a standard header file in C++ that stands for Input/Output
Stream.
Purpose: It contains definitions for objects like std::cin, std::cout, std::cerr,
and std::clog, which are used for input and output operations.
Angle Brackets < >: The use of angle brackets indicates that the file is a system or
standard library header file. If you were including your own header file, you would
use double quotes, like #include "myheader.h".
Example Usage:
cpp
Copy code
#include <iostream>
int main() {
std::cout << "Hello, World!" << std::endl; // Uses std::cout from
<iostream>
return 0;
}
In this example, std::cout and std::endl are defined in the iostream header file,
enabling console output.
Summary:
By using class templates, you define a blueprint for the class with a placeholder for the data
type, which is then replaced when the class is instantiated.
Syntax:
cpp
Copy code
template <typename T> // or use 'class T'
class ClassName {
T data; // Use 'T' as a placeholder for the type
public:
ClassName(T d) : data(d) {} // Constructor
T getData() { return data; } // Method using 'T'
};
Example:
cpp
Copy code
#include <iostream>
using namespace std;
int main() {
Box<int> intBox(123); // Box holding an integer
Box<double> doubleBox(45.67); // Box holding a double
return 0;
}
Output:
sql
Copy code
intBox contains: 123
doubleBox contains: 45.67
Key Points:
1. Template Keyword: The template <typename T> or template <class T> at the
beginning defines a template. Here, T is a placeholder for the actual data type that will
be passed when an object of the class is created.
2. Generic Data Members: Inside the class, you can declare variables of type T or
methods that use T. In the example above, value is a variable of type T.
3. Instantiation: When you create an object of the template class, you specify the data
type. For example, Box<int> creates a Box that holds an integer, and Box<double>
creates a Box that holds a double.
4. Reusability: The same class template can handle different types, making your code
more flexible and reusable.
cpp
Copy code
template <typename T1, typename T2>
class Pair {
T1 first;
T2 second;
public:
Pair(T1 f, T2 s) : first(f), second(s) {}
void display() {
cout << "First: " << first << ", Second: " << second << endl;
}
};
int main() {
Pair<int, double> p(1, 2.5);
p.display();
return 0;
}
Output:
sql
Copy code
First: 1, Second: 2.5
cpp
Copy code
template <typename T>
class MyClass {
public:
void display() { cout << "Generic template" << endl; }
};
Class templates in C++ are a powerful feature for writing generic and reusable code.
The STL (Standard Template Library) is a powerful part of the C++ Standard Library that
provides a set of common data structures and algorithms. It allows developers to work with
collections of data using templates, offering flexibility and efficiency. STL helps in managing
collections such as arrays, lists, stacks, and queues, while also providing algorithms for
sorting, searching, and manipulating these data structures.
1. Containers:
o These are used to store data. STL provides several types of containers,
including:
Sequence Containers (e.g., vector, deque, list)
Associative Containers (e.g., set, map, multimap)
Unordered Containers (e.g., unordered_set, unordered_map)
Container Adapters (e.g., stack, queue, priority_queue)
2. Algorithms:
o STL offers a rich set of algorithms for performing operations on the data
stored in containers. Examples include sorting (sort), searching (find), and
transforming data (transform). These algorithms work seamlessly with the
iterators provided by the containers.
3. Iterators:
o Iterators are objects that point to elements inside containers. They act like
pointers and provide a way to traverse elements within a container. STL
iterators come in different types, such as:
Input Iterators
Output Iterators
Forward Iterators
Bidirectional Iterators
Random Access Iterators
Together, these three components make STL a flexible and powerful tool for handling data
and performing operations efficiently in C++.
In C++, std::array, std::vector, and normal arrays (like int x[]) are all used to store
collections of elements, but they have distinct characteristics in terms of memory
management, flexibility, and safety. Here's a comparison:
Example:
cpp
Copy code
int x[] = {1, 2, 3};
Size: The size of a normal array is fixed and cannot be changed after its declaration.
Memory Management: Normal arrays are stored contiguously in memory, and their
memory is either allocated on the stack (for automatic storage) or the heap (if
dynamically allocated).
Bounds Checking: There is no bounds checking. Accessing out-of-bounds elements
can lead to undefined behavior.
Performance: They have minimal overhead and can be slightly faster than other
containers, but the lack of safety features like bounds checking can be problematic.
Type: Arrays decay to pointers when passed to functions, losing information about
their size.
Example:
cpp
Copy code
#include <array>
std::array<int, 3> arr = {1, 2, 3};
Size: The size is fixed at compile-time and must be provided as a template parameter
(e.g., std::array<int, 3>). It cannot change at runtime.
Memory Management: It is stored contiguously in memory, similar to a C-style
array, but encapsulated in a safer and more feature-rich wrapper.
Bounds Checking: Provides bounds checking with the .at() method, which throws
an exception if accessed out-of-bounds. Regular access with [] doesn't provide
bounds checking.
Performance: Comparable to normal arrays in terms of performance.
Type: Does not decay to a pointer when passed to functions, preserving its size and
type information.
Additional Features: Provides member functions like .size(), .at(), .front(),
.back(), and works well with STL algorithms.
Example:
cpp
Copy code
#include <vector>
std::vector<int> vec = {1, 2, 3};
Size: A std::vector is dynamic and can grow or shrink in size at runtime using
methods like .push_back() or .resize(). Its size can be modified easily after
initialization.
Memory Management: Memory is allocated dynamically, and std::vector
manages this allocation, resizing, and freeing when the vector is destroyed. It
allocates more memory than necessary to avoid frequent reallocations.
Bounds Checking: Provides bounds checking with the .at() method, and [] access
does not provide bounds checking.
Performance: There is a slight overhead due to dynamic memory management, but
it's generally very efficient. Inserting or deleting elements may cause reallocations,
which can be expensive.
Type: Does not decay to a pointer when passed to functions, preserving its size and
type information.
Additional Features: Provides a rich set of member functions like .push_back(),
.resize(), .size(), .capacity(), and supports various operations like insertion,
deletion, and iteration.
Summary of Differences:
In general:
Use normal arrays for simple, fixed-size cases where performance is critical, but be
cautious about safety.
Use std::array when you need fixed-size arrays but want the additional safety and
STL compatibility.
Use std::vector when you need dynamic size and more functionality, like resizing
or adding/removing elements.
4o
The terms static and dynamic refer to different aspects of memory management, storage
duration, and behavior in programming, especially in C++. Here's a comparison of static and
dynamic in these contexts:
1. Memory Allocation
Example:
cpp
Copy code
int arr[10]; // Static array, size fixed at compile-time
Example:
cpp
Copy code
int* arr = new int[10]; // Dynamically allocated array
delete[] arr; // Freeing dynamically allocated memory
Static:
o Static variables (including global variables) have static storage duration.
o They are allocated once and persist for the entire duration of the program.
o Local static variables retain their value between function calls and are initialized
only once.
Example:
cpp
Copy code
void func() {
static int count = 0; // Static local variable
count++; // Retains value across calls
}
Dynamic:
o Dynamically allocated variables have dynamic storage duration.
o Their lifetime is controlled by the programmer. They remain in memory until
explicitly deallocated using delete or free.
o If not deallocated, dynamically allocated memory can cause memory leaks.
Example:
cpp
Copy code
int* ptr = new int(5); // Dynamically allocated integer
delete ptr; // Freeing dynamically allocated memory
3. Size
Static:
o The size of statically allocated memory is fixed and known at compile-time.
o It cannot be changed during runtime.
Example:
cpp
Copy code
int arr[5]; // Fixed size array (5 elements)
Dynamic:
o The size of dynamically allocated memory can be determined and changed at
runtime.
o You can allocate memory based on user input or other runtime conditions, and
resize if necessary.
Example:
cpp
Copy code
int size;
std::cin >> size; // Get size from user input
int* arr = new int[size]; // Allocate array dynamically based on
size
4. Usage
Static Memory Allocation:
o Used for variables whose size and lifetime are fixed and known ahead of time.
o Ideal for small, fixed-size data structures like small arrays and global or static
variables.
Advantages:
Disadvantages:
Advantages:
Disadvantages:
5. Examples
cpp
Copy code
int arr[5]; // Static array, fixed size
cpp
Copy code
int size;
std::cin >> size; // Size determined at runtime
int* arr = new int[size]; // Dynamically allocated array
delete[] arr; // Don't forget to free memory
Summary of Differences:
Feature Static Allocation Dynamic Allocation
In essence, static allocation is fixed and efficient but inflexible, while dynamic allocation
provides flexibility at the cost of more complex memory management.
4o
You said:
is c++ static or dynamic language
ChatGPT said:
ChatGPT
C++ is considered a statically-typed language but supports both static and dynamic
memory management.
1. Statically-Typed Language
Type checking occurs at compile-time. You must declare the type of a variable before you
use it, and the type of the variable cannot change.
Once a variable is declared with a type (e.g., int, double, std::string), the compiler
enforces that the variable can only store data of that type.
Example:
cpp
Copy code
int x = 10; // Statically typed: x is always an int
Attempting to assign a different type (e.g., x = "hello") will result in a compile-time error.
Example:
cpp
Copy code
int x = 10; // Statically allocated memory (on the stack)
Example:
cpp
Copy code
int* p = new int(10); // Dynamically allocated memory (on the heap)
delete p; // Memory must be manually freed
Summary:
Statically-Typed: C++ is statically typed because variable types are determined and enforced
at compile-time.
Memory Management: C++ supports both static and dynamic memory allocation, providing
flexibility for different memory management needs.
Thus, while C++ is statically-typed, it provides robust support for dynamic memory
management.
In C++, a structure works the same way as a class, except for just two
small differences. The most important of them is hiding
implementation details. A structure will by default not hide its
implementation details from whoever uses it in code, while a class by
default hides all its implementation details and will therefore by default
prevent the programmer from accessing them. The following table
summarizes all of the fundamental differences.
Class
Structure
It is declared using the class keyword. It is declared using the struct keyword.
Class
Structure
In C++, a union is a special data structure that allows storing different data types in the same
memory location. Unlike a struct, where each member has its own memory, all members of
a union share the same memory space. This means that a union can hold only one of its
members at any given time, and the size of the union is determined by the size of its largest
member.
cpp
Copy code
#include <iostream>
using namespace std;
union Data {
int i;
float f;
char str[20];
};
int main() {
Data data;
data.i = 10;
cout << "data.i: " << data.i << endl;
data.f = 220.5;
cout << "data.f: " << data.f << endl;
// Since the members share memory, changing one member affects the
others
strcpy(data.str, "C++ Union");
cout << "data.str: " << data.str << endl;
return 0;
}
In a struct, each member has its own memory location, while in a union, all
members share the same memory space.
The size of a struct is the sum of the sizes of its members, whereas the size of a
union is the size of its largest member.
int** x = new int*[3]; for(int i = 0; i < 3; i++){ x[i] = new int[4]; for(int j = 0; j < 4; j++){ cin >> x[i][j]; } }
ChatGPT said:
ChatGPT
This C++ code dynamically allocates a 2D array and takes user input for each element of the
array. Here's a breakdown of the code:
Breakdown:
Visualization:
After running this code, you'll have a 3x4 matrix where each element is dynamically
allocated, and the user will input 12 values in total.
Example Input:
Copy code
1 2 3 4
5 6 7 8
9 10 11 12
css
Copy code
x[0] = {1, 2, 3, 4}
x[1] = {5, 6, 7, 8}
x[2] = {9, 10, 11, 12}
Important Note:
Don't forget to free the dynamically allocated memory when you're done using the array to
avoid memory leaks. Here's how you can deallocate it:
cpp
Copy code
for (int i = 0; i < 3; i++) {
delete[] x[i]; // Deallocate memory for each row
}
delete[] x; // Deallocate memory for the array of pointers
4o
You said:
int ** x what is this
ChatGPT said:
ChatGPT
The declaration int **x in C++ is a pointer to a pointer to an integer, often used to represent
a dynamically allocated 2D array.
What it means:
int *x: This would be a pointer to an integer, meaning x can store the address of an
integer.
int **x: This is a pointer to a pointer to an integer. In other words, it can store the address
of a pointer to an integer.
This structure is commonly used for dynamic 2D arrays in C++ because a 2D array can be
viewed as an array of arrays.
Example:
cpp
Copy code
int **x = new int*[3]; // Step 1: Allocate an array of 3 int pointers
Here, x is a pointer to a pointer to an integer (int **). The line dynamically allocates an
array of 3 pointers (each element of x is of type int *).
Visualization:
x is a pointer to an array of 3 pointers (int*[3]).
Each element of x (i.e., x[i]) is a pointer to an array of integers, such as int[4].
arduino
Copy code
x --> [ ptr1 ] --> [ int int int int ]
[ ptr2 ] --> [ int int int int ]
[ ptr3 ] --> [ int int int int ]
Each ptr1, ptr2, and ptr3 holds the address of the dynamically allocated array of integers.
Usage:
This structure allows you to create and manage a 2D array dynamically, where both the
number of rows and columns can be specified at runtime, as opposed to a fixed-size array.
C++
#include<iostream>
using namespace std;
processArr(arr);
return 0;
}
Output
element at index 1,1 is 3
C++ does not allow you to pass an entire array directly as an argument to a function by
value. When you pass an array to a function, what is actually passed is a pointer to the first
element of the array, not the entire array itself. This is due to the fact that arrays in C++ decay
into pointers when passed to functions.
Explanation:
cpp
Copy code
int arr[5];
cpp
Copy code
void myFunction(int arr[]);
myFunction(arr);
What is actually passed to myFunction is a pointer to the first element of arr (not the entire
array), so arr inside the function is treated as int*.
Example:
int main() {
int arr[5] = {1, 2, 3, 4, 5};
printArray(arr, 5); // Passing the array and its size
return 0;
}
2. Passing the Array Using a Pointer
cpp
Copy code
void printArray(int* arr, int size) {
for (int i = 0; i < size; i++) {
cout << arr[i] << " ";
}
cout << endl;
}
Both approaches (using int arr[] or int* arr) are essentially the same in C++ because the
array decays into a pointer when passed to the function.
Important Note:
Size Information: When passing an array, the size information is not passed along with it.
You must pass the size of the array as a separate argument (like int size in the example).
Multidimensional Arrays: For multidimensional arrays, you need to pass the sizes of all but
the first dimension.
If you want to pass an entire array without it decaying into a pointer (i.e., if you want to
preserve size information and pass it by value), you can use std::array or std::vector
from the C++ Standard Library:
cpp
Copy code
#include <iostream>
#include <array>
int main() {
std::array<int, 5> arr = {1, 2, 3, 4, 5};
printArray(arr); // Pass by value
return 0;
}
cpp
Copy code
#include <iostream>
#include <vector>
int main() {
std::vector<int> vec = {1, 2, 3, 4, 5};
printVector(vec); // Pass by value
return 0;
}
Summary:
Arrays decay into pointers when passed to functions, so the array's size is not passed.
If you need to pass the array and its size, you should pass the size separately.
Use std::array or std::vector if you want to pass an entire array without losing size
information.
4o
Code Explanation:
cpp
Copy code
#include<iostream>
using namespace std;
Output:
csharp
Copy code
element at index 1,1 is 3
How it works:
3. Function Access:
o Inside the function, you can access the elements of the array using normal array
indexing: a[1][1] accesses the element in the second row and second column
(which is 3).
Important Notes:
If you have an array with a different number of columns (e.g., int arr[2][3];), you would
need to modify the function signature to reflect the new column size, like this:
cpp
Copy code
void processArr(int a[][3]) { // Array with 3 columns
// Code to process array
}
For dynamically sized arrays, you can use pointers, std::vector, or pass additional
information (e.g., the size of the rows and columns) to handle arrays of arbitrary sizes.
4o
You said:
print all elements
ChatGPT said:
ChatGPT
To print all elements of a 2D array in C++, you can use nested loops to iterate through each
row and column. Below is an updated version of your code that prints all elements of the 2D
array:
Updated Code:
cpp
Copy code
#include<iostream>
using namespace std;
int main() {
int arr[2][2]; // Declare a 2x2 array
arr[0][0] = 0;
arr[0][1] = 1;
arr[1][0] = 2;
arr[1][1] = 3;
Explanation:
1. Nested Loops: The outer loop iterates over the rows, and the inner loop iterates over the
columns. In this case, the number of columns is fixed at 2.
2. Printing Elements: The cout statement prints the value of each element along with its
index.
Output:
less
Copy code
Element at index [0][0] is: 0
Element at index [0][1] is: 1
Element at index [1][0] is: 2
Element at index [1][1] is: 3
This will print all the elements of the 2D array along with their indices.
Generalization:
If you want to handle arrays of arbitrary size, you can modify the function to accept both the
number of rows and columns:
cpp
Copy code
void processArr(int a[][N], int rows, int cols) { // Where N is a constant
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
cout << "Element at index [" << i << "][" << j << "] is: " <<
a[i][j] << endl;
}
}
}
For dynamically sized arrays where the column size is not fixed, use pointers or
std::vector for a more flexible approach.
4o
The approach for matrix multiplication in C++ is to use nested loops to iterate through the
elements of the matrices and perform the multiplication operations.
C++
#include <iostream>
using namespace std;
int main() {
int aRows, aCols, bRows, bCols;
cout << "Enter the number of rows and columns for matrix A: ";
cin >> aRows >> aCols;
cout << "Enter the number of rows and columns for matrix B: ";
cin >> bRows >> bCols;
if (aCols != bRows) {
cout << "Error: The number of columns in matrix A must match the
number of rows in matrix B" << endl;
return 1;
}
return 0;
}
The approach is to iterate through the elements of the matrices and perform the multiplication
operations.
The process begins by creating two matrices A and B and reading the number of rows and
columns of these matrices from the user. Matrix multiplication is only possible if the number
of columns of A equals the number of rows of B. This is checked and if not met, the program
exits with an error message.
After that, the program creates a new matrix C of size (A.rows x B.cols) and initializes it with
zero.
Then, the program uses a nested for loop to perform the matrix multiplication:
It repeats this for every element of the matrices and produces a new matrix C which is the
product of A and B.
In the end, the result matrix C is displayed on the screen for the user to see.
cpp
Copy code
class Printer {
public:
void print(int i) {
std::cout << "Printing int: " << i << std::endl;
}
void print(double f) {
std::cout << "Printing float: " << f << std::endl;
}
void print(std::string s) {
std::cout << "Printing string: " << s << std::endl;
}
};
cpp
Copy code
class Complex {
public:
int real, imag;
Complex operator + (const Complex& obj) {
Complex temp;
temp.real = real + obj.real;
temp.imag = imag + obj.imag;
return temp;
}
};
cpp
Copy code
class Base {
public:
virtual void display() {
std::cout << "Base class display function" << std::endl;
}
};
int main() {
Base* basePtr;
Derived d;
basePtr = &d;
basePtr->display(); // Calls Derived class function
return 0;
}
In this example, even though basePtr is of type Base*, it points to an object of type
Derived, so the Derived class's display() method is called due to runtime polymorphism.
Summary:
4o
You said:
i was taught in a course tha tpolymorphism is a set up we can do in our c++ programs to use a base
pointer to mangae derived objects
ChatGPT said:
ChatGPT
You're absolutely correct! The concept you're referring to is runtime polymorphism in C++,
where a base class pointer (or reference) is used to manage objects of derived classes. This
is typically achieved through the use of virtual functions.
In C++, runtime polymorphism allows you to use a pointer or reference to the base class
to call methods that are specific to the derived class objects, even if you're interacting with
them via the base class pointer. This is made possible through virtual functions in the base
class, which are overridden in the derived classes. The actual method that gets called is
determined at runtime based on the type of the object that the pointer or reference is pointing
to.
// Base class
class Animal {
public:
// Virtual function to enable polymorphism
virtual void sound() {
std::cout << "Animal makes a sound" << std::endl;
}
};
// Derived class
class Dog : public Animal {
public:
// Overriding the sound method
void sound() override {
std::cout << "Dog barks" << std::endl;
}
};
int main() {
// Base class pointer
Animal* animalPtr;
return 0;
}
Output:
Copy code
Dog barks
Cat meows
Key Points:
1. Base class pointer (Animal*) is used to point to objects of derived classes (Dog, Cat).
2. The method sound() is declared as virtual in the base class Animal. This enables
polymorphism, allowing the appropriate version of sound() (from Dog or Cat) to be called,
even when using a base class pointer.
3. The derived classes (Dog and Cat) override the base class method sound() with their own
implementations.
4. At runtime, the correct method is invoked based on the type of the object that the pointer
refers to, not the type of the pointer itself.
So, when your course described polymorphism as a setup where a base pointer is used to
manage derived objects, it was referring to this dynamic, runtime polymorphism enabled by
virtual functions!
4o
You said:
this is dynamic binding right can you give example of both static and dynamic binding
ChatGPT said:
ChatGPT
Yes, exactly! What you described is dynamic binding, also known as late binding, where
the method to be invoked is determined at runtime based on the object type, not the pointer
or reference type.
To clarify the concept further, let's look at both static (early) and dynamic (late) binding
with examples.
In static binding, the method to be called is resolved at compile time. This occurs in
situations like function overloading or operator overloading, where the compiler knows
exactly which function to call based on the function signature or operator usage.
class Printer {
public:
// Function overloading (compile-time binding)
void print(int i) {
std::cout << "Printing integer: " << i << std::endl;
}
void print(double d) {
std::cout << "Printing double: " << d << std::endl;
}
void print(std::string s) {
std::cout << "Printing string: " << s << std::endl;
}
};
int main() {
Printer printer;
// Calls the appropriate function based on the argument type (resolved
at compile time)
printer.print(10); // Calls print(int)
printer.print(3.14); // Calls print(double)
printer.print("Hello!"); // Calls print(string)
return 0;
}
Output:
php
Copy code
Printing integer: 10
Printing double: 3.14
Printing string: Hello!
In this example, the function overloading allows multiple print functions to exist with
different parameter types. The compiler determines which one to call at compile time based
on the arguments passed.
In dynamic binding, the decision about which function to call is made at runtime. This
happens with virtual functions in polymorphism, where the base class pointer or reference
can invoke derived class methods. The function invoked depends on the actual type of the
object, not the type of the pointer or reference.
class Animal {
public:
// Virtual function to enable dynamic binding
virtual void sound() {
std::cout << "Animal makes a sound" << std::endl;
}
};
Dog dog;
Cat cat;
return 0;
}
Output:
Copy code
Dog barks
Cat meows
In this example:
Summary:
Static binding is used in cases where the compiler can determine at compile time which
function to call.
Dynamic binding is used in scenarios where polymorphism is needed, and the function call is
determined at runtime based on the actual object type, typically achieved using virtual
functions.
4o
You said:
in earlier case if we dont make the method virtual and do the same process what happens
ChatGPT said:
ChatGPT
If you don't mark the method as virtual in the base class and attempt the same process,
static binding (also known as early binding) will occur. This means that the method to be
called will be determined at compile time, based on the type of the pointer or reference,
rather than the actual object that the pointer or reference is pointing to.
In simpler terms, without the virtual keyword, the base class method will be called, even if
the pointer is pointing to a derived class object.
class Animal {
public:
// No 'virtual' keyword
void sound() {
std::cout << "Animal makes a sound" << std::endl;
}
};
int main() {
Animal* animalPtr;
Dog dog;
Cat cat;
Output:
css
Copy code
Animal makes a sound
Animal makes a sound
What Happened:
Since the sound() method in the base class Animal is not marked as virtual, the
method call is statically bound at compile time.
When you invoke animalPtr->sound(), the type of the pointer (Animal*) is used to
determine which method to call, not the actual object (Dog or Cat) that animalPtr is
pointing to.
Therefore, the base class (Animal) method is called in both cases, and you don't get the
derived class (Dog or Cat) method being invoked.
Key Takeaways:
Without the virtual keyword, static binding occurs. The method to be called is resolved
based on the type of the pointer or reference, rather than the actual type of the object it
points to.
The virtual keyword is necessary to enable dynamic binding or runtime polymorphism,
allowing the program to call the correct overridden method in the derived class.
In summary, without virtual, the derived class methods won't be called, and you'll always
get the base class method, regardless of which derived object the pointer is pointing to.
4o
You said:
Objects of different classes can be treated as objects of a common superclass how this statement
holds true for polymorphism in context to discussion we had.
ChatGPT said:
ChatGPT
Key Concepts:
1. Inheritance: Inheritance allows you to create a new class (derived class) based on an
existing class (base class or superclass). The derived class inherits the properties and
behaviors (methods) of the base class.
2. Polymorphism: Polymorphism allows objects of different derived classes to be
treated as if they are objects of a common superclass. This means that you can write
code that works with references or pointers to the base class, while the actual object
may belong to any class that derives from the base class.
How It Works:
You can treat objects of the derived classes (Dog and Cat) as objects of the base class
(Animal) due to inheritance. This becomes particularly useful in the context of
polymorphism because it allows you to write flexible and reusable code.
In C++, this is achieved through the use of base class pointers or references to handle
objects of the derived class, and virtual functions to allow the appropriate derived class
method to be called at runtime.
// Derived class 1
class Dog : public Animal {
public:
// Overriding the sound method
void sound() override {
std::cout << "Dog barks" << std::endl;
}
};
// Derived class 2
class Cat : public Animal {
public:
// Overriding the sound method
void sound() override {
std::cout << "Cat meows" << std::endl;
}
};
int main() {
// Base class pointer (or reference) can point to objects of derived
classes
Animal* animalPtr;
Dog dog;
Cat cat;
return 0;
}
Output:
Copy code
Dog barks
Cat meows
1. Objects of Different Classes: The objects dog (of class Dog) and cat (of class Cat)
are objects of different classes, but both are derived from the common superclass
Animal.
2. Treated as Objects of a Common Superclass: By using a base class pointer
(Animal* animalPtr), we are able to treat both dog and cat as objects of the
common superclass Animal.
3. Polymorphism in Action: Through dynamic binding (enabled by the virtual
function sound() in the base class Animal), the actual object type (whether it's a Dog
or Cat) is determined at runtime. This allows the appropriate sound() method (from
Dog or Cat) to be called, even though we are using a pointer to the base class Animal.
Code Reusability: You can write functions, methods, or collections that work with a
base class (Animal), but those same functions can also handle any derived class (Dog,
Cat, etc.) without modification.
Flexibility: By treating objects of derived classes as objects of their superclass, you
can manage different types of objects through a common interface (the base class).
For instance, you could have a function that takes an Animal* argument, and you could pass
a Dog*, Cat*, or any future class derived from Animal to that function without needing to
rewrite the function.
cpp
Copy code
void makeAnimalSound(Animal* animal) {
animal->sound(); // Polymorphism at work, calls the correct sound()
method
}
int main() {
Dog dog;
Cat cat;
Output:
Copy code
Dog barks
Cat meows
Conclusion:
4o
Class
Sub Class: The class that inherits properties from another class is
called Sub class or Derived Class.
Super Class: The class whose properties are inherited by sub class
is called Base Class or Super class.
Reusability: Inheritance supports the concept of “reusability”, i.e.
when we want to create a new class and there is already a class
that includes some of the code that we want, we can derive our new
class from the existing class. By doing this, we are reusing the fields
and methods of the existing class.
Example : Dog, Cat, and Cow can the be derived classes of Animal base
class.
The word polymorphism means having many forms. In simple words, we can
define polymorphism as the ability of a message to be displayed in more than
one form.
A real-life example of polymorphism is that a person at the same time can have
different characteristics. Like a man at the same time can be a father, a
husband, and an employee. So the same person posses different behavior in
different situations. This is called polymorphism.
Example: Suppose we have to write a function to add some integers, some
times there are 2 integers and some times there are 3 integers. We can write the
Addition Method with the same name having different parameters, the
concerned method will be called according to parameters.
Consider a real-life example of a man driving a car. The man only knows
that pressing the accelerators will increase the speed of the car or
applying the brakes will stop the car, but he does not know about how on
pressing the accelerator the speed is actually increasing, he does not
know about the inner mechanism of the car or the implementation of
accelerator, brakes, etc in the car. This is what abstraction is.
Advantages of Abstraction
Encapsulation
Advantages of Encapsulation:
Data Hiding: The user will have no idea about the inner
implementation of the class. It will not be visible to the user how the
class is storing values in the variables. He only knows that we are
passing the values to a setter method and that the variables are
getting initialized with that value.
Increased Flexibility: We can make the variables of the class as
read-only or write-only, depending on our requirements. If we wish
to make the variables as read-only then we have to omit the setter
methods such as setName(), setAge() etc. or if we wish to make the
variables as write-only then we have to omit the get methods such
as getName(), getAge(), etc.
Reusability: Encapsulation also improves the re-usability and
makes the code easy to change with new requirements.
Testing code is easy: Encapsulated code is easy to test for unit
testing.
1. Conceptual Focus
2. Purpose
3. Implementation
cpp
Copy code
class Vehicle {
public:
virtual void start() = 0; // Pure virtual function
};
cpp
Copy code
class Account {
private:
double balance; // Private member cannot be accessed directly
public:
void setBalance(double amount) {
if(amount >= 0) {
balance = amount; // Setting balance through a public
method
}
}
double getBalance() {
return balance; // Accessing balance through a public method
}
};
4. Level of Visibility
Abstraction: In abstraction, the inner workings of an object are not exposed to the
outside world. Only the essential operations that an object can perform are visible to
the user.
o Analogy: Think of an ATM. As a user, you care about depositing,
withdrawing, or checking your balance. You don't need to know the intricate
details of the bank's internal system or how the ATM communicates with the
bank servers.
Encapsulation: Encapsulation controls the visibility of the data and how the data can
be accessed or modified. It creates a protective layer around the object, where data
can be manipulated only through predefined methods.
o Analogy: Consider a pill bottle. The medication (data) inside is encapsulated
by the bottle. You can only access it by opening the cap (using methods), and
the bottle ensures that you don’t tamper with the contents unnecessarily.
5. Real-World Examples
Abstraction:
o In a car, abstraction is represented by how you interact with it—steering,
braking, or accelerating without knowing the mechanical complexities of the
engine, transmission, or braking system.
oSmartphone apps are another example of abstraction. Users interact with the
apps without knowing the complex algorithms or the code running behind the
scenes.
Encapsulation:
o Medical records: In a hospital’s database, patient information is encapsulated,
with access restricted to authorized personnel. Doctors can view and update
medical records, but unauthorized individuals cannot access this sensitive
information.
o Banking systems: When you perform a transaction, your bank account
balance is encapsulated. You can only modify it through certain predefined
actions, like making deposits or withdrawals, but you cannot directly alter the
account balance by accessing the internal workings of the banking system.
#include <bits/stdc++.h>
using namespace std;
class Employee { //Class Declaration
public:
string id, name;
int years; //experience (in years)
void work() {
cout << "Employee: " << this->id << " is working\n";
}
};
int main()
{
//Class Instantiation (Direct)
Employee emp("GFG123", "John", 3);
emp.work();
cout << endl;
emp_ptr->work();
return 0;
}
#include <bits/stdc++.h>
using namespace std;
//Outside-class definition
void Employee::work() {
cout << "Employee: " << this->id << " is working\n";
}
int main()
{
//Class Instantiation (Direct)
Employee emp("GFG123","John",3);
emp.work();
return 0;
}
Constructor Overloading
CPP
#include <bits/stdc++.h>
using namespace std;
class Employee {
public: // public access-modifier
string id, name;
int years;
Employee()
{
id = "";
name = "";
years = 0;
}
// Overloaded constructor
Employee(string id, string name, int years)
{
this->id = id;
this->name = name;
this->years = years;
}
// Overloaded constructor
Employee(string id, string name)
{
this->id = id;
this->name = name;
years = 0;
}
void getDetails()
{
cout << "ID: " << id << ", Name: " << name
<< ", Experience: " << years << endl;
}
};
int main()
{
// 1st constructor is called
Employee emp1;
emp1.getDetails();
emp2.getDetails();
fresher.getDetails();
return 0;
}
Output:
class Employee {
public: // public access-modifier
string id, name;
int years;
void getDetails()
{
cout << "ID: " << id << ", Name: " << name
<< ", Experience: " << years << endl;
}
};
int main()
{
Employee emp("GFG123", "John", 4);
emp.getDetails();
return 0;
}
Output:
CPP
#include <bits/stdc++.h>
using namespace std;
class Employee {
public: // public access-modifier
string id, name;
int years;
Employee(string id)
: id(id)
{
}
void getDetails()
{
cout << "ID: " << id << ", Name: " << name
<< ", Experience: " << years << endl;
}
};
int main()
{
Employee emp("GFG123", "John", 4);
emp.getDetails();
return 0;
}
Output:
Destructors
CPP
#include <bits/stdc++.h>
using namespace std;
char* p_chr;
class String {
public:
char* s;
int size;
String(char* c)
{
size = strlen(c);
s = new char[size + 1];
p_chr = s; // assign to global variable
strcpy(s, c);
}
};
void func()
{
String str("Hello World");
}
int main()
{
func();
cout << p_chr << endl;
return 0;
}
Output:
Hello World
CPP
#include <bits/stdc++.h>
using namespace std;
char* p_chr;
class String {
public:
char* s;
int size;
String(char* c)
{
size = strlen(c);
s = new char[size + 1];
p_chr = s;
strcpy(s, c);
}
// Destructor
~String()
{
delete[] s;
}
};
void func()
{
String str("Hello World");
}
int main()
{
func();
cout << p_chr << endl;
return 0;
}
The above code prints nothing because p_chr reference is gone due to
destructor call.
Copy Constructor
CPP
#include <bits/stdc++.h>
using namespace std;
class Array {
public:
int n;
int* ref;
Array(int n)
: n(n)
{
ref = new int[n];
int main()
{
Array arr1(10);
cout << "n-value of 1st instance: " << arr1.n << endl;
cout << "Array values of 1st instance:\n";
for (int i = 0; i < 10; i++)
cout << *(arr1.ref + i) << " ";
cout << endl;
return 0;
}
Output:
In the above code, we find the value of member n for t1 not modified as it
is not a pointer value. Thus, upon copying new instance of n got created
for t2. Any change to n in t2 didn't change t1.n. However, t1.ref is a
pointer. So, upon copy-constructor call, the address value got copied
to t2.ref, and thus, any change at t2.ref (as we here are multiplying by 2),
gets reflected at t1.ref also because both of them are pointing to the
same array. This is an example of a shallow-copy. To fix this, we write our
custom copy-constructor:
CPP
#include <bits/stdc++.h>
using namespace std;
class Array {
public:
int n;
int* ref;
Array(int n)
: n(n)
{
ref = new int[n];
// copy-contructor definition
Array(const Array& obj)
: n(obj.n)
{
ref = new int[n];
int main()
{
Array arr1(10);
cout << "n-value of 1st instance: " << arr1.n << endl;
cout << "Array values of 1st instance:\n";
for (int i = 0; i < 10; i++)
cout << *(arr1.ref + i) << " ";
cout << endl;
return 0;
}
Output:
To understand 'this' pointer, it is important to know how objects look at functions and data
members of a class.
Meaning each object gets its own copy of data members and all objects share a single copy of
member functions.
Then now question is that if only one copy of each member function exists and is used by
multiple objects, how are the proper data members are accessed and updated?
The compiler supplies an implicit pointer along with the names of the functions as 'this'.
The 'this' pointer is passed as a hidden argument to all nonstatic member function calls and is
available as a local variable within the body of all nonstatic functions. 'this' pointer is a
constant pointer that holds the memory address of the current object. 'this' pointer is not
available in static member functions as static member functions can be called without any
object (with class name).
For class X, the type of this pointer is 'X* const'. Also, if a member function of X is declared
as const, then the type of 'this' pointer is 'const X *const'.
CPP
#include <bits/stdc++.h>
using namespace std;
class Employee {
public:
string id, name;
int years;
int main()
{
Employee emp("GFG123", "John", 4);
emp.printDetails();
return 0;
}
Output:
For constructors, initializer list can also be used when parameter name is same as member's
name.
CPP
/* Reference to the calling object can be returned */
Test& Test::func ()
{
// Some processing
return *this;
}
When a reference to a local object is returned, the returned reference can be used to chain
function calls on a single object.
CPP
#include <bits/stdc++.h>
using namespace std;
class Employee {
private:
string id, name;
int years;
public:
Employee setId(string id) {
this->id = id;
return *this;
}
void printDetails() {
cout << "ID: " << id
<< ", Name: " << name
<< ", Experience: " << years;
}
};
int main()
{
Employee emp;
emp.setId("GFG123").setName("John").setYears(4).printDetails();
return 0;
}
Output:
In the above code, each time we call the setter methods, the employee instance we are
referring to is returned using the 'this' pointer. We can thus re-use this instance to chain more
method calls.
Mark as Read
Report An Issue