DECAP202_Object Oriented Programming
DECAP202_Object Oriented Programming
C++ is a powerful, high-level programming language that supports both procedural and object-oriented
programming paradigms. Its basic concepts include the following:
Variables and Data Types: C++ supports various data types such as integers, floating-point numbers,
characters, and booleans. Variables are used to store data, and their types determine the kind of data
they can hold.
Functions: Functions are blocks of code that perform specific tasks and can return values. C++
supports both standard and user-defined functions.
Control Structures: C++ includes essential control structures like loops (for, while, do-while) and
conditional statements (if, switch) to control the flow of execution.
Object-Oriented Concepts: C++ supports object-oriented programming, allowing the creation of
classes, objects, inheritance, polymorphism, encapsulation, and abstraction.
Pointers and References: C++ allows direct memory manipulation through pointers, which store
memory addresses. References provide an alias to an object, allowing for more efficient manipulation
of large data structures.
File Handling: C++ provides classes for file input and output operations, such as ifstream and
ofstream, which are used to read and write data from files.
Memory Management: C++ gives developers control over memory allocation and deallocation, using
operators like new and delete.
1
Templates: C++ supports generic programming through templates, allowing functions and classes to
work with different data types.
Overall, C++ combines low-level memory manipulation and high-level abstractions, making it a
versatile and efficient language for system-level programming and application development.
2. Inheritance is the Process by Which Objects of One Class Acquire the Properties of Objects of
Another Class. Analyze.
Inheritance is one of the core principles of object-oriented programming (OOP) in C++. It enables a
class (called the derived class) to inherit attributes and behaviors (data members and methods) from
another class (called the base class). This relationship models a hierarchical structure, where the
derived class is a more specialized version of the base class.
In C++, inheritance can be classified into different types: single inheritance (where a derived class
inherits from a single base class), multiple inheritance (where a derived class inherits from more than
one base class), and multilevel inheritance (where a class derives from another derived class).
The main benefits of inheritance include code reuse (as the derived class inherits functionalities from
the base class), extendibility (new functionality can be added to the derived class without modifying
the base class), and polymorphism (which allows derived classes to override base class methods for
specialized behavior).
For example, if we have a base class Animal with methods like eat() and sleep(), a derived class Dog
can inherit these methods but also add its own methods like bark() to extend the behavior of the Animal
class.
Modularity: OOP divides the program into smaller, manageable objects. This modular structure makes
it easier to understand, maintain, and debug.
Code Reusability: Through inheritance and polymorphism, OOP allows for code reuse. Once a class is
created, it can be reused in multiple programs or contexts.
Maintainability: OOP promotes clear and organized code, making it easier to update or extend.
Changes to one part of the code do not usually affect other parts of the program.
Scalability: OOP helps in building scalable applications. Classes can be easily extended to
accommodate new requirements without affecting the existing code.
Encapsulation: By keeping data and functions together in a class, OOP ensures that data is hidden
from outside interference. This leads to safer and more secure code.
Polymorphism: Polymorphism allows for the use of one interface to represent different underlying
forms (e.g., objects of different classes can be treated as objects of a common superclass).
These benefits make OOP a popular and efficient approach for building complex and large-scale
software applications.
4. Compare What You Look for in the Problem Description When Applying Object-Oriented
Approach in Contrast to the Procedural Approach. Illustrate with Some Practical Examples.
2
When applying an object-oriented approach to problem-solving, the focus is on identifying the objects
(entities) in the problem domain and the relationships between them. The goal is to model real-world
entities and their interactions. In contrast, the procedural approach focuses more on the sequence of
tasks or steps required to solve the problem.
Object-Oriented Approach: Look for entities that have both attributes (data) and behaviors
(methods). For example, in a banking system, objects like Account, Customer, and Transaction might
be identified. Each of these objects would have methods and attributes relevant to their roles (e.g.,
Account might have a balance attribute and methods like deposit() and withdraw()).
Procedural Approach: Look for a clear sequence of actions that need to be performed. In the same
banking system, you would focus on functions or procedures that handle specific tasks like calculating
interest, processing transactions, and printing account statements. The structure would be more linear,
with functions calling one another in a predefined sequence.
In summary, OOP emphasizes real-world entities and their interactions, while procedural programming
focuses on a step-by-step procedure to accomplish tasks.
Encapsulation refers to bundling the data and the methods that operate on that data into a single unit or
class, restricting access to some of the object's components.
Inheritance allows a new class to inherit properties and behaviors from an existing class, facilitating
code reuse and extending functionality.
Polymorphism enables different objects to respond to the same method call in different ways.
Abstraction hides the complex implementation details and exposes only essential features of the
object.
Software Development: Most modern software applications are built using OOP, including enterprise
software, games, mobile apps, and web applications.
Simulation and Modeling: OOP is particularly useful in simulations (like flight simulators) or when
modeling complex systems such as financial systems, inventory management, or manufacturing
systems.
GUI Applications: OOP is ideal for developing graphical user interfaces (GUIs) as each component
can be treated as an object (e.g., buttons, text boxes).
Artificial Intelligence and Machine Learning: OOP provides a framework to represent various
elements in AI systems, such as agents and environments.
Procedural Programming and Object-Oriented Programming are two distinct programming paradigms,
each with its own approach to problem-solving.
3
Procedural Programming: This paradigm focuses on a sequence of instructions or procedures to be
executed step by step. It organizes code into functions, and the primary focus is on functions or
methods that operate on data. The data is often separate from the functions.
Object-Oriented Programming (OOP): OOP focuses on the concept of objects, which encapsulate
both data and behaviors. Instead of focusing on procedures, OOP emphasizes modeling real-world
entities and their interactions. OOP promotes code reuse through inheritance, flexibility through
polymorphism, and data protection through encapsulation.
Key Differences:
Approach: Procedural programming is centered around functions and procedure calls, while OOP is
centered around objects that combine data and methods.
Code Organization: In procedural programming, code is typically written in a sequence of procedures.
In OOP, code is organized into classes and objects.
Maintainability: OOP allows for easier code maintenance and extension due to the use of objects and
inheritance, whereas procedural programming can become cumbersome as the project grows.
Here’s a simple C++ program demonstrating the working of classes and objects:
cpp
Copy
#include <iostream>
using namespace std;
int main() {
// Create an object of class Car
Car car1;
4
return 0;
}
This program defines a Car class with attributes brand and year, and a method displayInfo() to print the
details of the car. In the main() function, an object car1 is created, its attributes are set, and the method
is called to display the information.
The unit then walks through the structure of a simple C++ program, illustrating elements such as comments,
preprocessor directives (e.g., #include <iostream>), the main() function, and standard output using cout.
The process of compiling and linking C++ programs is explained, detailing how source code is preprocessed,
compiled into assembly code, and linked to generate an executable file.
A significant portion of the unit is devoted to fundamental programming constructs in C++, including tokens
(the smallest units in a program), keywords (reserved words with special meanings), identifiers (names for
variables and functions), constants (fixed values that cannot be modified), and strings (sequences of characters
ending with a null terminator \0). Operators, which perform operations on operands, are classified into
different types such as arithmetic, assignment, relational, logical, and bitwise operators.
The unit also covers data types, which define the type of data a variable can hold. These include primitive data
types like int, char, float, and bool, as well as derived data types such as arrays, pointers, and references.
Reference variables, a feature unique to C++, provide an alias for an existing variable, facilitating efficient
parameter passing in functions.
The unit concludes with a summary emphasizing the key components of C++ syntax and structure, laying the
groundwork for more advanced topics in object-oriented programming. It also includes self-assessment
questions and exercises to reinforce understanding.
In C++, data types define the type of data a variable can store and the operations that can be performed
on that data. C++ provides a wide variety of data types, which can be broadly categorized into
primitive (built-in) data types and derived data types.
5
o char: Represents a single character, such as 'a', '1', or '$'. The size of a char is typically one byte, and it
stores values from 0 to 255.
o float: Represents floating-point numbers (decimal values). It typically takes 4 bytes of memory. For
example, float f = 3.14;.
o double: Represents double-precision floating-point numbers, which are more precise than float and
take 8 bytes of memory. For example, double d = 3.14159265;.
o bool: Represents boolean values, which can only be true or false. For example, bool b = true;.
Derived Data Types: These are built from basic data types and include:
o Array: A collection of variables of the same type, stored in contiguous memory locations.
o Pointer: A variable that holds the memory address of another variable.
o Reference: An alias for another variable.
User-defined Data Types: These include:
o Structures (struct): A user-defined type that groups together different data types under a single name.
For example, struct Person { string name; int age; };.
o Classes: Classes are used in object-oriented programming to define objects with attributes and
behaviors.
o Unions: A type that allows storing different data types in the same memory location, but only one at a
time.
o Enumerations (enum): A user-defined type that allows you to define a set of named integer constants.
C++ also supports the concept of void, a special data type that indicates the absence of any value,
typically used for functions that don't return a value.
2. What is a Constant?
In C++, a constant is a variable whose value cannot be changed after it has been initialized. Constants
are often used to make the code more readable, maintainable, and less prone to errors. The const
keyword is used to define a constant, ensuring that the value it holds remains fixed throughout the
program’s execution.
For example:
cpp
Copy
const int MAX_SIZE = 100;
In this example, MAX_SIZE is a constant and its value cannot be altered after it is assigned. Constants
are commonly used for values that are logically meant to remain unchanged, like mathematical
constants (e.g., pi), configuration settings, or limits (e.g., the size of an array). Using constants instead
of hardcoding literal values in multiple places makes the program easier to update and less error-prone.
Keywords in programming are reserved words that have a predefined meaning and cannot be used as
identifiers (such as variable or function names). Keywords are fundamental to programming languages
like C++, as they define the syntax and structure of the language. They help to distinguish between
different elements and actions that the program can perform, ensuring that the program behaves as
expected.
6
In C++, keywords like if, else, while, for, return, class, and int help structure the program by indicating
control flow, data types, and class definitions. For example, if is used to make conditional decisions,
while return is used to exit a function and return a value.
Without keywords, a programming language would lack consistency and clarity, and it would become
difficult for the compiler to interpret the programmer’s intentions. They are an essential part of the
language syntax and help define the rules of the language.
In C++, a reference is an alias for an existing variable. It allows you to access or modify the original
variable using the reference. Here’s a simple program demonstrating reference operations:
cpp
Copy
#include <iostream>
using namespace std;
int main() {
int a = 10;
int &b = a; // b is a reference to a
return 0;
}
In this example, b is a reference to a. Any changes made to b will directly affect a because b is not a
separate variable but an alias for a. Initially, both a and b hold the value 10. After assigning 20 to b,
both a and b reflect the updated value.
In C++, operators are special symbols used to perform operations on variables and values. They are
classified into several categories based on their function:
Arithmetic Operators: These operators are used for performing basic mathematical operations.
o + (Addition): Adds two operands.
o - (Subtraction): Subtracts the second operand from the first.
o * (Multiplication): Multiplies two operands.
o / (Division): Divides the first operand by the second.
o % (Modulus): Returns the remainder of the division of the first operand by the second.
Relational Operators: These operators are used to compare two values.
o == (Equal to): Returns true if the two operands are equal.
7
o != (Not equal to): Returns true if the two operands are not equal.
o > (Greater than): Returns true if the first operand is greater than the second.
o < (Less than): Returns true if the first operand is less than the second.
o >= (Greater than or equal to): Returns true if the first operand is greater than or equal to the second.
o <= (Less than or equal to): Returns true if the first operand is less than or equal to the second.
Logical Operators: These are used to perform logical operations, typically in control structures.
o && (Logical AND): Returns true if both operands are true.
o || (Logical OR): Returns true if at least one operand is true.
o ! (Logical NOT): Returns true if the operand is false.
Assignment Operators: These operators are used to assign values to variables.
o =: Assigns the value on the right-hand side to the variable on the left-hand side.
o +=: Adds the right operand to the left operand and assigns the result to the left operand.
o -=: Subtracts the right operand from the left operand and assigns the result to the left operand.
Increment and Decrement Operators: These operators are used to increase or decrease the value of a
variable by 1.
o ++: Increments the value of a variable by 1.
o --: Decrements the value of a variable by 1.
Bitwise Operators: These operators perform operations on binary representations of numbers.
o &: Bitwise AND.
o |: Bitwise OR.
o ^: Bitwise XOR.
o ~: Bitwise NOT.
o <<: Left shift.
o >>: Right shift.
Ternary (Conditional) Operator: This is a shorthand way to write an if-else statement.
o ? : (Conditional): condition ? expr1 : expr2; evaluates expr1 if the condition is true, otherwise it
evaluates expr2.
Special Operators:
o sizeof: Returns the size of a data type or variable in bytes.
o , (Comma): Separates expressions in a statement, allowing multiple operations in a single line.
o ->: Used to access members of a structure or class through a pointer.
o []: Used to declare or access array elements.
These operators form the foundation of expression evaluation in C++ programs, allowing developers to
manipulate data effectively.
8
loss. On the other hand, explicit type conversion, also known as type casting, requires the programmer's
intervention using casting operators to convert one type to another intentionally. The unit highlights the
importance of conversion functions, especially in the context of class and basic type conversions, and the role
of overloaded casting operators for converting class types to basic types. It also discusses the conversion
between different class types, which can be achieved through either a constructor or a conversion function,
depending on whether the conversion is initiated in the source or destination class.
Through this unit, students are expected to gain a clear understanding of operators and how type conversion
works in C++, allowing them to write more efficient and flexible programs.
1. What Do You Mean by Type Casting? Explain the Difference Between Implicit and Explicit
Type Casting in Detail.
Type casting in C++ refers to the process of converting a variable from one data type to another. This is
typically done to ensure that variables of different data types can be used together in expressions, or
when one type of data needs to be converted to another type for the purpose of storing or processing the
data.
Implicit Type Casting (Automatic Type Conversion): Implicit type casting is done automatically by
the compiler. When the conversion is safe and does not lead to loss of data or precision, the compiler
performs the conversion without requiring explicit instructions from the programmer. For example,
when an integer is assigned to a float variable, C++ automatically converts the integer to a float.
Example:
cpp
Copy
int a = 5;
float b = a; // Implicit type casting
In this case, a (an integer) is automatically converted to a float without any explicit action from the
programmer.
Explicit Type Casting (Manual Type Conversion): Explicit type casting is performed when the
programmer explicitly requests the conversion. This is used when a type conversion might lose data or
require more careful handling. It is done using a cast operator or by using the static_cast in C++.
Example:
cpp
Copy
double a = 9.57;
int b = (int) a; // Explicit type casting
9
Type conversion in C++ occurs when an operand of one data type is assigned or passed to a variable of
a different data type. Type conversion can be done either implicitly or explicitly, depending on whether
the compiler automatically handles the conversion or the programmer explicitly instructs it.
cpp
Copy
int a = 10;
float b = 5.5;
double c;
In this case, the integer a is automatically converted to a float for the addition, and then the result
(which is a float) is assigned to the double variable c. This type conversion occurs because C++
performs an implicit promotion of the integer to a floating-point number and then promotes the result to
double precision to fit the variable c.
cpp
Copy
#include <iostream>
using namespace std;
int main() {
double a = 12.345;
int b;
return 0;
}
In this program, the double value a is explicitly cast to an integer using (int). The fractional part of a is
discarded during the conversion, and the resulting integer is stored in b.
4. The Assignment Operations Cause Automatic Type Conversion Between the Operand as Per
Certain Rules. Describe.
In C++, assignment operations can cause automatic type conversions, also known as implicit type
casting, according to certain rules defined by the language. These rules determine when a type
conversion should occur based on the compatibility between the data types of the operands involved.
10
If an integer is assigned to a floating-point variable, C++ automatically converts the integer to a float or
double to avoid truncation.
If a char is assigned to an int, C++ promotes the char to an integer.
In cases where an operation involves a combination of data types (e.g., an integer and a float), C++
automatically promotes the lower precision type to the higher precision type (e.g., an integer to a float)
to avoid data loss.
Example:
cpp
Copy
int a = 10;
float b = 5.5;
b = a; // Implicit type conversion, integer 'a' is automatically converted to float
cpp
Copy
#include <iostream>
using namespace std;
int main() {
int a = 5;
float b = 4.5;
double result;
cout << "Result after implicit type conversion: " << result << endl;
return 0;
}
In this example, the integer a is implicitly converted to a float before being added to b. The result is
stored in the double variable result, which is able to store the final value without any explicit type
conversion.
Class to class type conversion, also known as user-defined type conversion, is the process of
converting an object of one class type into an object of another class type. This can be achieved using
constructor-based type conversion or operator overloading.
11
In C++, class-to-class type conversion typically involves defining a constructor in the destination class
that accepts an object of the source class as an argument. Alternatively, a type conversion operator can
be defined to perform the conversion.
For example, if you have two classes A and B, you can define a constructor in B that takes an object of
A and converts it into an object of type B.
7. List the Situations in Which We Need Class Type to Basic Type Conversion.
1. Object to Fundamental Data Type: When you need to extract a primitive value from an object, such
as converting a class object to an integer, float, or string for further processing.
o Example: Extracting the value of a double from a class that encapsulates a floating-point value.
2. Returning Values from Member Functions: If a member function of a class needs to return a
primitive type, such as an integer or float, to the calling function.
3. Input/Output Operations: When objects are used in input or output operations and need to be
converted into a basic type, like when you need to display an object’s attributes as basic types (integers
or floats) on the console.
8. How to Convert One Data Type to Another Data Type in C++? Explain in Detail.
In C++, you can convert one data type to another in two main ways:
1. Implicit Type Conversion (Automatic Conversion): C++ automatically converts a lower precision
data type (such as int) to a higher precision type (such as float or double) when required in expressions.
o Example: Assigning an integer to a float.
cpp
Copy
int a = 10;
float b = a; // Implicit conversion from int to float
2. Explicit Type Conversion (Manual Conversion): Explicit conversion is done using type casting. In
C++, you can use either the C-style cast or the static_cast operator for explicit type conversion.
o Example: Converting a float to an integer explicitly.
cpp
Copy
float a = 5.75;
int b = (int) a; // Explicit type conversion (C-style)
3. Using Constructor-based Conversion: A class can have a constructor that takes a basic data type as
an argument and converts it into an object of the class.
o Example: Converting an integer to a class object.
cpp
Copy
class MyClass {
public:
12
MyClass(int x) { /* Convert int to MyClass object */ }
};
9. Write a Program Which Demonstrates the Conversion of Class Type to Basic Type
Conversion.
Here’s a C++ program demonstrating the conversion of a class type to a basic type:
cpp
Copy
#include <iostream>
using namespace std;
class MyClass {
private:
int num;
public:
MyClass(int n) : num(n) {} // Constructor that initializes 'num'
int main() {
MyClass obj(100);
int x = obj; // Implicit conversion from MyClass to int
In this example, the operator int() function is defined to convert an object of type MyClass to an int.
The conversion happens implicitly when we assign the object obj to the integer variable x.
10. There Are Three Types of Situations That Arise Where Data Conversion Occurs Between
Incompatible Types. What Are Three Situations? Explain Briefly.
The three types of situations where data conversion occurs between incompatible types are:
1. Implicit Conversion (Automatic Promotion): This occurs when a lower data type is automatically
promoted to a higher data type, such as converting an integer to a float or a float to a double.
o Example: Converting int to float when mixed in a mathematical operation.
2. Explicit Conversion (Manual Casting): When you explicitly convert a data type to another using type
casting. This is required when the compiler cannot automatically perform the conversion because it
might lose data or precision.
o Example: Converting a double to int, where the fractional part is discarded.
13
3. Conversion Between Incompatible Class Types: This occurs when one class object needs to be
converted to another class type or to a fundamental data type. This requires either a constructor or an
overloaded operator to perform the conversion.
o Example: Converting a complex class object to a basic float or int.
Next, the unit explores iterative controls, which are used to repeat a block of code multiple times. The for,
while, and do-while loops are discussed, with an emphasis on their syntax and use cases. The for loop is
described as a deterministic loop where the number of iterations is predefined, while the while and do-while
loops allow for more flexible repetition based on conditions evaluated during the loop's execution. The
difference between the while loop (which checks the condition before executing the loop body) and the do-
while loop (which checks the condition after) is clarified.
Finally, the unit introduces jumping controls, which alter the flow of the program within loops. The break
and continue statements are explained, with examples of how they can interrupt or skip iterations of loops.
The goto statement, although generally discouraged, is mentioned as another way to transfer control in the
program. Overall, this unit provides a comprehensive look at control structures essential for decision-making,
iteration, and managing program flow in C++.
A nested if statement refers to an if statement placed inside another if statement. It allows you to
check multiple conditions in a hierarchical manner, where the inner if is evaluated only when the outer
if condition is true. This structure is useful when you need to perform a decision-making process based
on multiple conditions.
For example:
cpp
Copy
#include <iostream>
using namespace std;
int main() {
int a = 10, b = 5, c = 20;
14
if (a > b) { // Outer if statement
if (a > c) { // Nested if statement
cout << "a is the greatest number" << endl;
} else {
cout << "c is the greatest number" << endl;
}
} else {
cout << "b is the greatest number" << endl;
}
return 0;
}
In this example, the outer if checks if a is greater than b. If this condition is true, it enters the nested if,
which checks if a is greater than c. If the condition for the nested if is true, it outputs that a is the
greatest. Otherwise, it outputs that c is the greatest. This demonstrates how nested if statements work to
evaluate multiple conditions sequentially.
Looping statements and jumping statements are both control flow mechanisms in C++, but they
serve different purposes:
Looping Statements: These are used to repeat a block of code multiple times as long as a certain
condition is true. The main looping statements in C++ are:
o for loop: Typically used when the number of iterations is known beforehand.
o while loop: Used when the condition needs to be checked before the execution of the loop body.
o do-while loop: Similar to the while loop, but it guarantees at least one iteration, as the condition is
checked after the loop body executes.
For example:
cpp
Copy
for (int i = 0; i < 5; i++) {
cout << i << " ";
}
Jumping Statements: These statements are used to alter the flow of execution by jumping to a
different part of the program. The main jumping statements are:
o break: Terminates the current loop or switch statement.
o continue: Skips the current iteration of the loop and moves to the next iteration.
o goto: Transfers control to another part of the program (though its use is generally discouraged due to
poor readability and maintainability).
o return: Exits from the current function and optionally returns a value.
For example:
15
cpp
Copy
for (int i = 0; i < 5; i++) {
if (i == 3) {
break; // Exits the loop when i equals 3
}
cout << i << " ";
}
cpp
Copy
#include <iostream>
using namespace std;
int main() {
int num, reversed = 0;
while (num != 0) {
int digit = num % 10; // Get the last digit
reversed = reversed * 10 + digit; // Build the reversed number
num /= 10; // Remove the last digit
}
return 0;
}
In this program, the while loop continuously extracts the last digit of the number by using the modulus
operator (% 10), adds that digit to the reversed number, and then removes the last digit from the
original number by dividing it by 10. This continues until the number becomes 0, at which point the
reversed number is printed.
4. What Do You Mean by the Switch Statement? How Is It Different from the If-Else Statement?
A switch statement is a control flow statement in C++ that allows you to test a variable or expression
against multiple possible values, called cases. Each case represents a potential match, and if the value
of the expression matches a case, the corresponding block of code is executed. If no match is found, the
default case can be executed (if defined). The switch statement is typically used when you have a fixed
set of possible values to compare against.
16
Example:
cpp
Copy
#include <iostream>
using namespace std;
int main() {
int day = 3;
switch (day) {
case 1:
cout << "Monday" << endl;
break;
case 2:
cout << "Tuesday" << endl;
break;
case 3:
cout << "Wednesday" << endl;
break;
default:
cout << "Invalid day" << endl;
}
return 0;
}
In this example, the switch statement evaluates the value of day and executes the corresponding case.
The break statement ensures that once a matching case is executed, the control exits the switch
statement.
The difference between switch and if-else lies in how they handle conditions:
The switch statement is more efficient when comparing a single variable to many potential values, as it
directly jumps to the matched case.
The if-else statement is more flexible and can handle complex conditions involving ranges, inequalities,
or multiple variables, but it may be less efficient if there are many conditions to check.
5. Differentiate Between While Loop and Do-While Loop with a Suitable Example.
The primary difference between the while loop and the do-while loop is when the condition is
checked.
While Loop: The condition is checked before executing the loop body. If the condition is false
initially, the loop body is not executed at all.
Example:
cpp
Copy
17
#include <iostream>
using namespace std;
int main() {
int i = 0;
while (i < 3) {
cout << "i = " << i << endl;
i++;
}
return 0;
}
Here, the condition i < 3 is checked before entering the loop. If the condition is false (e.g., i starts at 3),
the body of the loop is never executed.
Do-While Loop: The condition is checked after executing the loop body. This ensures that the loop is
always executed at least once, even if the condition is false initially.
Example:
cpp
Copy
#include <iostream>
using namespace std;
int main() {
int i = 3;
do {
cout << "i = " << i << endl;
i++;
} while (i < 3);
return 0;
}
In this case, even though i starts at 3, the loop body executes once before checking the condition. After
the body is executed, the condition is checked, and since i is no longer less than 3, the loop terminates.
Summary of Differences:
While Loop: The condition is evaluated before entering the loop. If the condition is false, the loop
body may not execute at all.
Do-While Loop: The condition is evaluated after the loop body, guaranteeing at least one execution of
the loop body.
18
Unit 05: Pointers and Structures
The section also describes how pointers can be used in expressions and how pointer arithmetic can be
performed, such as incrementing or dereferencing pointers. Pointers are vital in creating dynamic data
structures and managing memory effectively, but care must be taken to avoid accessing invalid memory
locations.
The second key topic of this unit is structures, which are user-defined data types that group different types of
variables under a single name. The unit explains how to define structures using the struct keyword and
create structure variables. Structures allow organizing complex data efficiently, such as grouping a person's
name, age, and address under a single structure. However, C structures have limitations: they cannot have
functions, constructors, or access modifiers like other object-oriented constructs.
The unit also highlights how to access structure members using the dot operator ( .) and initialize structures
either statically or dynamically. It concludes with a discussion on the limitations of structures, such as the
inability to use operators on them or enforce data encapsulation, which limits their use in object-oriented
programming.
Pointers are variables that store the memory address of another variable. They are a fundamental
feature of C and C++ programming, providing a mechanism to directly interact with memory. Pointers
have several important uses in programming, including:
Dynamic Memory Allocation: Pointers allow for dynamic memory management, enabling programs
to allocate and deallocate memory at runtime using functions like malloc(), calloc(), and free() in C, or
new and delete in C++. This provides flexibility when working with large amounts of data whose size
is unknown at compile time.
Efficient Array Handling: In C, arrays are closely related to pointers, and pointers can be used to
traverse and manipulate arrays efficiently. Using pointers to access array elements is faster and more
memory efficient, as it avoids the overhead of copying the data.
Function Arguments: Pointers allow functions to modify variables outside their local scope, providing
an efficient way to pass large structures or arrays by reference. This is crucial in scenarios where
returning values or passing by value would be inefficient or impractical due to the size of the data.
Memory Management: Pointers enable more advanced memory management techniques, like linked
lists, trees, and graphs, where each element contains a pointer to the next one, allowing for dynamic,
non-contiguous memory structures.
Pointer Arithmetic: Pointers can be used to directly manipulate memory addresses, providing more
granular control over memory access, such as incrementing or decrementing memory addresses to
traverse data structures.
19
Overall, pointers offer flexibility and efficiency in memory manipulation, but they also introduce
complexity and the risk of errors like memory leaks or segmentation faults if misused.
C structures are a powerful feature for grouping data of different types, but they come with some
limitations that developers must consider when using them. These limitations include:
No Data Hiding/Encapsulation: One of the primary drawbacks of C structures is that they do not
support encapsulation, a fundamental principle of object-oriented programming (OOP). All members of
a structure are publicly accessible, meaning that you cannot restrict access to the data fields, which
could lead to unintended modifications or misuse of the data.
No Functions/Methods: Unlike classes in C++, C structures cannot have member functions or methods
associated with them. This means that structures cannot encapsulate behavior alongside data, which
limits the modularity and reusability of code. In contrast, C++ allows structures to have functions as
part of the structure, providing more flexibility.
No Inheritance or Polymorphism: C structures do not support object-oriented features like
inheritance or polymorphism, which are essential in modern software development for creating reusable
and maintainable code. In C, developers need to use other techniques, such as function pointers, to
achieve some level of functionality similar to OOP, but this can be complex and error-prone.
Memory Padding and Alignment: Structures in C may experience memory alignment issues due to
the way data is organized in memory. This can lead to wasted memory, as compilers may insert
padding bytes to ensure that members of the structure are properly aligned for efficient access. This can
also affect the performance and memory usage of applications, particularly when dealing with large
structures.
Lack of Default Constructor/Destructor: In C, structures do not have constructors or destructors,
which makes it difficult to automatically initialize or clean up structure members when they are created
or destroyed. This is especially problematic for managing resources like dynamic memory or file
handles.
These limitations make C structures less suitable for large, complex systems compared to the more
advanced features provided by C++ classes, such as encapsulation, inheritance, and polymorphism.
Here’s a simple C program that demonstrates how to define and use structures in C:
c
Copy
#include <stdio.h>
#include <string.h>
int main() {
20
// Declare and initialize a structure variable
struct Student student1;
return 0;
}
In this program, we define a structure Student with three members: name, age, and grade. We then
create a structure variable student1, assign values to its members, and print those values. This
demonstrates how to define and manipulate structures in C.
A function is a block of code designed to perform a specific task. Functions can accept inputs
(parameters) and return a value, or they can perform an action without returning anything (void
functions). Functions are fundamental in programming for promoting code reuse, modularity, and
organization.
In the context of Object-Oriented Programming (OOP), functions are often referred to as methods
when they are part of a class. Methods define the behavior of objects created from the class, and they
are essential for encapsulating functionality. The key advantages of using functions (methods) in OOP
include:
Encapsulation: Functions allow the encapsulation of behavior within a class, making the internal
workings of an object hidden from the outside world. This leads to better control over how data is
accessed and modified, as functions can enforce rules and logic for interacting with data.
Code Reusability: Functions can be reused across different parts of the program or even across
different programs. This eliminates redundancy and ensures that functionality does not need to be re-
implemented multiple times, making the codebase easier to maintain and extend.
Modularity: By dividing a program into smaller, self-contained functions (or methods), the program
becomes more modular. Each function can focus on a specific task, making it easier to understand, test,
and debug the program.
Abstraction: Functions abstract away complex logic. Users of a function do not need to understand
how the function works internally but can focus on what it does. This increases the usability and
maintainability of the code.
Inheritance and Overriding: In OOP, methods can be inherited by subclasses and, in many cases,
overridden to provide specialized functionality. This allows for the creation of flexible and extensible
systems where the base class can provide default behavior, and subclasses can modify or extend that
behavior.
21
Using functions in OOP provides a structured and organized approach to writing programs that are
more flexible, reusable, and easier to manage.
Function prototyping is the process of declaring a function before its actual definition. It involves
specifying the function’s name, return type, and the types of its parameters in a declaration, which
provides the compiler with the necessary information about the function's signature. This allows the
compiler to check for correct usage of the function in the code, ensuring that the function is called with
the correct number and types of arguments.
Function prototypes are typically placed at the beginning of a program or in a header file to enable
early declaration of functions, which is especially useful in programs that contain multiple functions or
when functions call each other in a non-linear sequence.
For example:
c
Copy
#include <stdio.h>
// Function prototype
int add(int, int);
int main() {
int result = add(5, 7);
printf("The sum is: %d\n", result);
return 0;
}
// Function definition
int add(int a, int b) {
return a + b;
}
In this example, the add() function is declared before it is used in the main() function, which is the
function prototype. This declaration tells the compiler what the function looks like, allowing it to check
that the function is called correctly in the program.
Function prototyping helps improve code organization and ensures that the program is compiled and
linked correctly. It also reduces the possibility of errors related to function usage, such as mismatched
parameter types or missing return values.
The unit starts with specifying a class, which includes declaring data members and functions within a class
body. It then covers how to define member functions both inside and outside the class. Member functions
serve as the behaviors or operations that objects of the class can perform. An important concept introduced is
that of access specifiers, which control the accessibility of class members. These include the public,
private, and protected specifiers. The public members are accessible from outside the class, while
private members can only be accessed within the class itself, ensuring encapsulation and data protection.
Protected members allow access within the class and its derived classes.
The section on creating class objects highlights how objects are instantiated from a class and stored in
memory. It also introduces the syntax for defining and accessing class members using objects. Practical
examples are provided, demonstrating how to declare and use classes, create objects, and access members
through public or private access specifiers. The unit wraps up by emphasizing the importance of
understanding these core concepts in object-oriented programming, as they form the building blocks of C++
applications.
1. Define class. What is the difference between structures and classes in C++?
A class in C++ is a user-defined data type that represents real-world entities and allows the grouping of
data members (attributes) and methods (functions) together. It serves as a blueprint for creating objects
(instances) of that class. The main difference between structures and classes in C++ lies in their default
access control. In a structure, all members are public by default, meaning they can be accessed by any
function. In contrast, in a class, all members are private by default, meaning they cannot be accessed
directly outside the class unless explicitly specified as public. Furthermore, classes can support features
such as inheritance, polymorphism, and encapsulation, while structures are generally more basic and
intended for simpler use cases where object-oriented features are unnecessary.
C++ and C have different mechanisms for handling data types, and C functions do not understand C++
classes directly. To pass a C++ object to/from a C function, you would need to expose the object to C
functions through an interface that C can understand. One approach is to use extern "C" to ensure
proper linkage between C and C++ code. You could pass pointers to objects or use a wrapper structure
in C to hold the object data and pass that to the C function. An example involves defining a class in
C++, then wrapping it with a C-style interface function (declared extern "C") that can accept or return
pointers to the class.
3. Can a method directly access the non-public members of another instance of its class?
Yes, a method within a class can directly access non-public (private or protected) members of another
instance of its class, as long as it is a member of the same class. This is because member functions of a
class have access to all the private and protected members of the class, regardless of whether they are
accessing them from the current instance or another instance of the same class.
23
4. What’s the difference between the keywords struct and class?
In C++, the primary difference between struct and class is the default access control. In a struct,
members are public by default, while in a class, members are private by default. Additionally, while
struct is often used for simpler data grouping, a class is typically used to define more complex data
structures that include both data and functions, and supports object-oriented features like encapsulation,
inheritance, and polymorphism. Technically, the struct and class keywords are functionally equivalent,
with the difference being their default member visibility.
5. Write a program to add two numbers defining a member getdata() and display() inside a class
named sum and displaying the result.
cpp
Copy
#include <iostream>
using namespace std;
class sum {
private:
int num1, num2;
public:
void getdata() {
cout << "Enter two numbers: ";
cin >> num1 >> num2;
}
void display() {
cout << "The sum of " << num1 << " and " << num2 << " is: " << num1 + num2 << endl;
}
};
int main() {
sum s;
s.getdata();
s.display();
return 0;
}
This program defines a sum class with two private members to store numbers. The getdata() method
prompts the user to input two numbers, and the display() method calculates and displays their sum.
6. How does C++ help with the tradeoff of safety vs. usability?
C++ provides a balance between safety and usability through its features like strong typing, object-
oriented principles, and the option of manual memory management. For example, C++ allows you to
encapsulate data within classes, thereby promoting safety by restricting access to private members
while still providing usability through public methods. Additionally, C++'s use of pointers allows for
efficient memory manipulation (usability), but also requires the programmer to manage memory
24
manually, which can introduce risks (safety). The combination of these features gives developers the
flexibility to optimize for either safety or performance, depending on the needs of the application.
7. “The main advantage of using a static member is to declare the global data which should be
updated while the program lives in memory”. Justify your statement.
A static member in C++ is a class member that belongs to the class itself rather than to instances of the
class. This means that a static member is shared across all instances of the class, and its data persists for
the lifetime of the program. Using static members allows you to store global data that is not tied to any
particular object instance but is updated as the program runs. This is particularly useful for maintaining
state across multiple function calls or objects, such as counting the number of instances of a class,
managing global resources, or tracking program-wide statistics. Static members help optimize memory
usage by not duplicating data for each object.
8. Write a program to print the score board of a cricket match in real time.
cpp
Copy
#include <iostream>
#include <string>
using namespace std;
struct Batsman {
string name;
int runs;
bool out;
string outMode;
};
struct Bowler {
string name;
int oversPlayed;
int maidenOvers;
int runsGiven;
int wicketsTaken;
};
int main() {
int numBatsmen = 2, numBowlers = 1;
Batsman batsmen[numBatsmen] = {{"Batsman1", 0, false, ""}, {"Batsman2", 0, false, ""}};
Bowler bowlers[numBowlers] = {{"Bowler1", 0, 0, 0, 0}};
return 0;
}
This program uses structures for batsmen and bowlers and updates their scores in real time, printing the
updated scoreboard after each ball.
When a member of a class is public, it can be accessed from outside the class, including from derived
classes. For example, if a base class has a public member, a derived class or any external function can
directly access that member.
cpp
Copy
#include <iostream>
using namespace std;
class Base {
public:
26
int publicVar;
Base() : publicVar(10) {}
};
int main() {
Derived d;
d.display();
return 0;
}
In this example, publicVar from the Base class is directly accessible in the Derived class due to its
public access modifier.
10. Write a program in which a class has one private member (properties) and two public
members (methods).
cpp
Copy
#include <iostream>
using namespace std;
class MyClass {
private:
int properties; // Private member
public:
void setProperties(int value) { // Public method to set the property
properties = value;
}
int main() {
MyClass obj;
obj.setProperties(100); // Accessing the public method to set the value
obj.displayProperties(); // Accessing the public method to display the value
return 0;
}
27
This program defines a class with a private member properties and provides two public methods to set
and display this private member's value.
The section also covers nested member functions, where one member function calls another within the same
class. This is essential for organizing code and ensuring functionality is compartmentalized. It further clarifies
that nested functions can access both public and private members of the class, making them versatile in
operation.
The unit then explores private member functions, which are functions that are only accessible within the class
itself. This feature supports encapsulation, ensuring that certain operations are restricted from external access.
It helps maintain the integrity of the object by limiting interaction with its internal workings.
Additionally, arrays within a class are addressed, with the unit explaining how arrays can be used as class
members. Arrays can be declared with different access specifiers, including private, public, or protected,
depending on how accessible they should be to other parts of the program. Practical examples with array
handling inside classes are provided to demonstrate this concept.
Lastly, memory allocation of objects is discussed, emphasizing the importance of dynamic memory allocation
when the size of an object cannot be predetermined. The new operator is introduced for allocating memory at
runtime, and the delete operator is covered for deallocating memory when it is no longer needed. This
section also explains how memory management plays a crucial role in handling objects efficiently in real-time
systems or applications with complex memory requirements.
In summary, this unit builds on the fundamental concepts of classes and objects by introducing advanced
features such as nested functions, private functions, arrays in classes, and dynamic memory allocation, all of
which are essential for creating robust object-oriented programs.
In object-oriented programming (OOP), classes and objects are fundamental concepts that help model
real-world entities. A class serves as a blueprint or template for creating objects. It defines a set of
properties (data members) and behaviors (member functions or methods) that the objects of the class
will have. A class provides the structure and functionality, but it doesn't consume memory until an
object is created. An object is an instance of a class, representing a specific entity in memory. When a
class is instantiated, an object is created that contains the data members and can perform the operations
defined in the class’s member functions. For example, a Car class might define properties like color,
28
model, and speed, and methods like start() or stop(). An object of this class would represent a specific
car with its own color, model, and speed values.
2. Write a program that demonstrates the working of member functions and classes.
cpp
Copy
#include <iostream>
using namespace std;
class Car {
private:
string model;
int year;
public:
// Member function to set the values of model and year
void setData(string m, int y) {
model = m;
year = y;
}
int main() {
// Creating an object of the class Car
Car myCar;
return 0;
}
In this program, the class Car has two private data members: model and year. It also has two member
functions, setData() to set values for the properties and displayData() to print them. In the main()
function, an object myCar of class Car is created, and its member functions are called to set and display
the data.
In C++, memory for an object is allocated either on the stack or the heap depending on how the object
is created. When an object is created on the stack, memory is automatically allocated when the object is
29
created and deallocated when the object goes out of scope. On the other hand, if an object is created
dynamically using the new operator, memory is allocated from the heap, and the object will persist until
it is explicitly deleted using the delete operator.
cpp
Copy
#include <iostream>
using namespace std;
class MyClass {
public:
int data;
~MyClass() {
cout << "Destructor called! Memory deallocated." << endl;
}
};
int main() {
// Stack allocation
MyClass stackObj(10);
cout << "Stack Object Data: " << stackObj.data << endl;
// Heap allocation
MyClass* heapObj = new MyClass(20);
cout << "Heap Object Data: " << heapObj->data << endl;
return 0;
}
In this program, stackObj is allocated on the stack, meaning it will be destroyed automatically when it
goes out of scope. heapObj is dynamically allocated using new, which requires an explicit delete to free
the memory. The constructor and destructor are called when the objects are created and destroyed,
respectively.
In C++, access specifiers are used to define the visibility of members (variables and methods) of a
class. They control how the members can be accessed by other parts of the program.
Public: Members declared as public are accessible from anywhere in the program. They can be
accessed by objects of the class, other functions, and even derived classes. Public members are
typically used for methods that need to be accessed by external entities.
30
Private: Members declared as private are accessible only within the class itself. They cannot be
accessed directly by external functions or derived classes. Private members are often used for internal
data and operations that should not be exposed or modified by external code, ensuring encapsulation.
Protected: Members declared as protected are similar to private members, except that they can be
accessed by derived classes (subclasses). However, they are not accessible by functions outside of the
class or derived classes. Protected members are used when you want to allow access to certain data or
methods for subclasses but not for general external code.
Example:
cpp
Copy
class MyClass {
public:
int publicVar; // Accessible anywhere
private:
int privateVar; // Accessible only within this class
protected:
int protectedVar; // Accessible within this class and derived classes
};
5. What is the significance of member functions, explain using an example.
Member functions are integral to the functioning of a class because they define the behavior of an
object. While data members hold the state of an object, member functions allow that state to be
modified or retrieved and define the operations that can be performed on the object. They provide a
way to manipulate an object's internal data while adhering to the principles of encapsulation, which
ensures that the object's internal state is protected from unauthorized access.
For example, consider a class BankAccount where member functions are used to deposit and withdraw
money, ensuring that the account balance remains valid.
cpp
Copy
#include <iostream>
using namespace std;
class BankAccount {
private:
double balance;
public:
// Constructor to initialize the balance
BankAccount(double initialBalance) : balance(initialBalance) {}
int main() {
BankAccount account(1000); // Creating an object with an initial balance of 1000
account.displayBalance();
account.deposit(500); // Deposit money
account.withdraw(300); // Withdraw money
account.displayBalance();
return 0;
}
In this program, the BankAccount class has private data (balance) and several member functions
(deposit(), withdraw(), and displayBalance()) that manage and display the balance. These functions
provide controlled access to the internal state of the object, ensuring the integrity of the account
balance. The member functions are crucial for interacting with the object's data while maintaining
encapsulation.
The unit explains different types of functions in C++, focusing on user-defined and library functions. Library
functions are those pre-written functions included in standard libraries, while user-defined functions are
32
created by the programmer to perform specific tasks. Function declaration is crucial as it informs the compiler
about the function's name, return type, and parameters, while the definition provides the actual body of the
function.
A key feature introduced is the difference between call-by-value and call-by-reference mechanisms. In call-
by-value, a copy of the actual parameter is passed to the function, ensuring that the original value remains
unchanged. In contrast, call-by-reference passes the address of the actual parameter, meaning changes inside
the function will affect the original value.
The concept of default arguments is also introduced, which allows parameters to have default values if no
value is passed when the function is called. This feature simplifies function calls by reducing the need to pass
every parameter explicitly.
In summary, this unit emphasizes how functions make C++ programs more modular, reusable, and easier to
understand. The key aspects covered include function types, declaration, definition, and mechanisms for
passing arguments, all fundamental to structuring efficient C++ programs.
A function is a self-contained block of code that performs a specific task. It can take input in the form
of parameters, process the input, and return an output. Functions are used to break down a program into
smaller, manageable parts, enhancing modularity and reusability. In C++, a function is defined by a
name, a return type, and an optional list of parameters. When a function is called, it executes the code
inside its body and returns the result (if any). Functions can be used to organize code, avoid repetition,
and improve readability.
Here’s an example:
cpp
Copy
#include <iostream>
using namespace std;
// Function definition
int add(int a, int b) {
return a + b; // Adds two numbers and returns the result
}
int main() {
int result = add(5, 7); // Function call with arguments 5 and 7
cout << "The sum is: " << result << endl; // Output the result
return 0;
}
In this example, the add function is defined to take two integer parameters a and b, adds them together,
and returns the sum. The function is called in main() with arguments 5 and 7, and the result is printed to
the console.
cpp
Copy
#include <iostream>
using namespace std;
int main() {
int x = 5;
cout << "Before function call: " << x << endl; // Original value
modifyValue(x); // Call by value
cout << "After function call: " << x << endl; // Original value remains unchanged
return 0;
}
In this example, modifyValue accepts a copy of the value of x as a parameter. The value of x is
changed inside the function, but the original value in main() remains unaffected because only a copy of
the value was passed.
In call by reference, the memory address of the argument is passed to the function, allowing the
function to modify the original argument directly. This method is used when the function needs to
modify the data of the calling function.
cpp
Copy
#include <iostream>
using namespace std;
int main() {
int x = 5;
cout << "Before function call: " << x << endl; // Original value
modifyValue(x); // Call by reference
cout << "After function call: " << x << endl; // Original value is modified
return 0;
}
34
Here, modifyValue takes a reference to the parameter num. This means that the changes made inside
the function directly affect the original value of x in main(). The value of x is modified after the
function call, which is the key difference from call by value.
For example, a class BankAccount might have methods like deposit(), withdraw(), and getBalance() to
interact with the internal balance of the account. This keeps the balance private while allowing
controlled access and modification, promoting better organization, security, and maintainability.
Default arguments in C++ allow a function to be called with fewer arguments than it is defined to
accept. If an argument is not provided in the function call, the default value defined in the function
prototype is used. This feature enhances flexibility by allowing the function to operate with different
numbers of arguments without the need for multiple overloaded functions.
Here's an example:
cpp
Copy
#include <iostream>
using namespace std;
int main() {
greet(); // Uses default values
greet("Alice"); // Uses default age
greet("Bob", 30); // Uses provided values
return 0;
}
In this example, the greet function has two parameters, name and age, both of which have default
values. If no arguments are provided, the default values ("Guest" for name and 25 for age) are used.
However, the caller can provide one or both arguments to override the defaults, offering a flexible way
to call the function with varying arguments.
The concept of inline functions is introduced, where small, frequently used functions are expanded directly at
the point of invocation, reducing the overhead of function calls. This improves performance by avoiding the
function call stack creation and execution time, especially when the function consists of a few statements.
The unit also explains function overloading, which allows multiple functions to share the same name but
differ in their argument lists. This increases the flexibility of code by enabling the same function to perform
different operations depending on the parameters passed. Another critical topic discussed is friend functions,
which are functions that can access private and protected members of a class despite not being a member of
the class itself. Friend functions offer a mechanism for classes to cooperate while still encapsulating their
internal data.
Finally, the unit covers C++ static data members and functions, which belong to the class itself rather than any
specific object. Static members are shared by all instances of the class and are particularly useful for tracking
global state or implementing class-wide behavior. Static functions, like static data members, can be called
using the class name rather than an object, making them suitable for operations that don't require access to
instance-specific data.
In summary, Unit 9 provides in-depth coverage of essential function concepts, such as call methods, inline
functions, overloading, friend functions, and static members, which are integral to writing efficient, modular,
and maintainable C++ programs.
Function prototyping refers to declaring a function before its actual definition in the code. A function
prototype provides the compiler with the function's signature, including its return type, name, and
parameter types, allowing the compiler to verify that the function calls in the code are correct (i.e., they
match the function's expected parameters and return type). Prototyping is necessary when the function
is used before its definition in the code, as it ensures that the compiler can check for consistency in the
function calls. It’s not necessary if the function is defined before it is called because, in that case, the
compiler automatically knows the function’s signature.
Example:
cpp
Copy
#include <iostream>
using namespace std;
36
int main() {
myFunction(5, 3.14); // Function call
return 0;
}
In this example, the function prototype void myFunction(int, double); tells the compiler that
myFunction will accept an int and a double as parameters, and this allows the function to be called in
main() before it is defined.
An inline function is a function that is expanded in line when it is called. The compiler replaces the
function call with the actual code of the function during the compilation process, thus avoiding the
overhead of function calls (such as pushing and popping function arguments on the stack). This can
result in performance improvements, especially for small, frequently called functions. The main
purpose of inline functions is to optimize performance by eliminating the function call overhead, but
they should only be used for small functions, as they increase the size of the code and could negatively
impact performance if used excessively.
Example:
cpp
Copy
#include <iostream>
using namespace std;
int main() {
cout << "Square of 5: " << square(5) << endl;
return 0;
}
In this example, the square function is defined as inline, meaning that wherever square(5) is called, the
code 5 * 5 will be directly inserted in place of the function call.
(a) void main(): In older versions of C++, it was allowed to use void main(), where the main() function
doesn't return anything. However, this is not standard-compliant according to C++ standards. The
standard specifies that main() should return an int.
37
(b) int main(): This is the standard declaration of the main function in C++. It returns an integer, which
is typically used to indicate the status of program termination (returning 0 usually means successful
execution).
(c) int main(int argc, char *argv[]): This version of the main() function allows for the passing of
command-line arguments to the program. The argc parameter holds the number of arguments, and argv
is an array of strings (character arrays) representing the individual arguments passed to the program.
cpp
Copy
#include <iostream>
using namespace std;
This allows the program to accept external parameters when run from the command line.
In object-oriented programming, a friend function is a function that can access the private and
protected members of a class, even though it is not a member of that class. Member functions of a class
are usually not declared as friends because they already have access to the class's private and protected
members. However, you might declare a function or another class as a friend when there is a need for a
non-member function or another class to access the private or protected members of the class. For
example, friend functions are used in operator overloading, where non-member functions may need
access to private data of a class.
Example:
cpp
Copy
#include <iostream>
using namespace std;
class Box {
private:
double length;
public:
Box(double l) : length(l) {}
void printLength(Box b) {
cout << "Length: " << b.length << endl; // Accessing private member directly
}
int main() {
Box b(10.5);
printLength(b); // Accessing private member through friend function
return 0;
}
Here, printLength() is a friend function, allowing it to access the private member length of the class
Box.
5. Write a program that uses overloaded member functions for converting temperature from
Celsius to Kelvin scale.
cpp
Copy
#include <iostream>
using namespace std;
class Temperature {
public:
// Overloaded function to convert Celsius to Kelvin
double convertToKelvin(double celsius) {
return celsius + 273.15;
}
int main() {
Temperature temp;
double celsius = 25.0;
cout << "Temperature in Kelvin (precise): " << temp.convertToKelvin(celsius, true) << endl;
cout << "Temperature in Kelvin (approximate): " << temp.convertToKelvin(celsius) << endl;
return 0;
}
In this example, the convertToKelvin method is overloaded to handle both precise and approximate
conversions from Celsius to Kelvin.
6. To calculate the area of a circle, rectangle, and triangle using function overloading.
39
cpp
Copy
#include <iostream>
#include <cmath>
using namespace std;
class Area {
public:
// Overloaded function to calculate area of a circle
double calculate(double radius) {
return M_PI * radius * radius; // Area of a circle
}
int main() {
Area areaCalculator;
cout << "Area of Circle: " << areaCalculator.calculate(5) << endl;
cout << "Area of Rectangle: " << areaCalculator.calculate(5, 3) << endl;
cout << "Area of Triangle: " << areaCalculator.calculate(5, 3, true) << endl;
return 0;
}
Here, the calculate function is overloaded for calculating the area of different shapes: circle, rectangle,
and triangle.
Inline functions can improve performance in cases where the function is small and called frequently.
By replacing the function call with the actual function code during compilation, inline functions
eliminate the overhead of the function call, which involves pushing parameters onto the stack, jumping
to the function code, and returning back. However, if the function is large or called infrequently,
making it inline can increase the size of the binary and hurt performance due to code bloat. Therefore,
the performance improvement depends on the size and frequency of the function calls.
8. How can inline functions help with the tradeoff of safety vs. speed?
Inline functions help optimize performance by avoiding function call overhead, making them faster.
However, the tradeoff is that they increase code size since the function’s body is inserted at each call
site. This might impact the program's memory usage, but it can also result in better performance for
40
small functions that are called frequently. By making functions inline, you can improve speed
(increased performance), but you need to be cautious because large or complex inline functions can
lead to code bloat and reduce the readability and maintainability of the code.
cpp
Copy
#include <iostream>
using namespace std;
class Print {
public:
// Overloaded function to print integers
void display(int num) {
cout << "Integer: " << num << endl;
}
int main() {
Print obj;
obj.display(10); // Calls the int version
obj.display(3.14); // Calls the double version
obj.display("Hello!"); // Calls the string version
return 0;
}
cpp
Copy
#include <iostream>
using namespace std;
int main() {
41
int result = add(10, 20);
cout << "Sum: " << result << endl;
return 0;
}
This program demonstrates an inline function add that is called in the main() function. The inline
function eliminates the overhead of a regular function call, improving performance.
The unit also addresses modes of inheritance, which define the accessibility of the base class members in the
derived class. These include public inheritance (where the public members of the base class remain public in
the derived class), private inheritance (where both public and protected members of the base class become
private in the derived class), and protected inheritance (where both public and protected members remain
protected in the derived class). It highlights that each type of inheritance supports the reusability of code and
the extension of base class logic to meet specific needs in derived classes.
Further, the unit discusses how private members of a class can be made inheritable, emphasizing the use of
access specifiers to control the visibility and accessibility of class members. The ability to derive properties
from other classes and the various access control mechanisms provided by inheritance are key to creating
efficient, modular, and reusable code in C++.
1. Program to demonstrate working static data members and static member functions in object-
oriented programming C++
In C++, static data members are shared by all instances of a class. They belong to the class itself
rather than to any specific object, which means they are not tied to any individual object of the class.
Similarly, static member functions can only access static data members of the class and are called
using the class name or an object of the class. These functions do not require an instance of the class to
be invoked. Static members are useful when you want to maintain a value common across all instances
of the class, such as counting the number of objects created.
Here’s a C++ program that demonstrates static data members and static member functions:
cpp
42
Copy
#include <iostream>
using namespace std;
class MyClass {
private:
int value;
static int objectCount; // Static data member
public:
MyClass(int v) : value(v) {
objectCount++; // Increment static data member on object creation
}
void displayValue() {
cout << "Value: " << value << endl;
}
};
int main() {
MyClass obj1(10);
MyClass obj2(20);
obj1.displayValue();
obj2.displayValue();
return 0;
}
In this program, objectCount is a static data member that keeps track of how many instances of
MyClass have been created. The static function displayObjectCount() prints the value of objectCount.
Static data members are shared by all objects, so every time a new object is created, the static variable
is incremented.
2. To calculate the area of circle, rectangle, and triangle using function overloading
Function overloading allows multiple functions with the same name but different parameter types or
numbers of parameters to exist. In this case, we can use function overloading to calculate the area of
different shapes (circle, rectangle, and triangle) by providing different argument lists for each shape.
43
Here's the C++ code for calculating the area of a circle, rectangle, and triangle using function
overloading:
cpp
Copy
#include <iostream>
using namespace std;
class Shape {
public:
// Overloaded function to calculate area of a circle
double area(double radius) {
return 3.14159 * radius * radius; // Area of a circle
}
int main() {
Shape shape;
cout << "Area of Circle: " << shape.area(5) << endl;
cout << "Area of Rectangle: " << shape.area(5, 3) << endl;
cout << "Area of Triangle: " << shape.area(5, 3, true) << endl;
return 0;
}
In this example, the area() function is overloaded three times to calculate the area of a circle (with one
argument), a rectangle (with two arguments), and a triangle (with two arguments and a flag). Each call
to area() invokes the appropriate version based on the number and type of arguments passed.
Inline functions can improve performance in certain situations by eliminating the overhead associated
with function calls. When a function is declared inline, the compiler attempts to replace the function
call with the actual code of the function, effectively "inlining" the function into the calling code. This
eliminates the need for pushing and popping arguments on the stack and jumping to a different memory
location to execute the function, which is particularly beneficial for small functions called frequently.
However, inline functions can also increase the size of the binary if used excessively, leading to code
bloat, which may negatively affect performance. Therefore, while inline functions can enhance
performance for small functions, it is essential to use them judiciously to avoid potential downsides.
44
4. How can inline functions help with the tradeoff of safety vs. speed?
Inline functions can improve speed by reducing function call overhead, which is especially important
for small, frequently called functions. This enhances performance because the function's body is
inserted directly where the function is called, avoiding the overhead of making a function call.
However, there is a tradeoff in terms of safety because inlining increases the size of the program. If
inline functions are used excessively, they can lead to larger binaries and cause issues like cache
misses, which negatively impact performance. Additionally, inlining large functions may reduce
readability and maintainability of code. Therefore, the tradeoff is between the speed gained from
avoiding function call overhead and the safety of keeping code maintainable and avoiding unnecessary
code bloat.
Function overloading is a feature in C++ where you can have multiple functions with the same name
but different parameter lists. The correct function is chosen based on the arguments provided during the
function call.
cpp
Copy
#include <iostream>
using namespace std;
class Print {
public:
// Overloaded function to print an integer
void display(int num) {
cout << "Integer: " << num << endl;
}
int main() {
Print obj;
obj.display(10); // Calls the integer version
obj.display(3.14); // Calls the double version
obj.display("Hello!"); // Calls the string version
return 0;
45
}
In this program, the display function is overloaded to handle integers, doubles, and strings. The
appropriate function is called based on the argument type.
Inline functions can be defined using the inline keyword and are expanded at the point of the function
call to reduce the overhead of calling the function. Inline functions are typically used for small
functions that are called frequently to improve performance.
cpp
Copy
#include <iostream>
using namespace std;
int main() {
int sum = add(10, 20); // Inline function call for addition
int product = multiply(10, 20); // Inline function call for multiplication
return 0;
}
In this program, add and multiply are inline functions that perform simple arithmetic operations. By
using the inline keyword, these functions are expanded at the point where they are called, potentially
improving performance by eliminating the overhead of function calls. However, since the functions are
simple and small, inlining is beneficial in terms of speed.
Destructors, on the other hand, are used for cleanup when an object is destroyed. A destructor has the same
name as the class but is prefixed with a tilde (~). Unlike constructors, destructors do not take any arguments
or return any value. They are automatically called when an object goes out of scope or when the program
ends, releasing any dynamically allocated memory or performing other necessary cleanup tasks. The unit
emphasizes that while C++ provides a default destructor, programmers can define their own destructors,
especially when dealing with dynamic memory allocation. Destructor functions play a crucial role in ensuring
that resources like dynamically allocated memory are properly freed.
The unit also explores copy constructors, which are used when an object is initialized with another object of
the same class. This allows the values of one object to be copied into another. Additionally, the unit includes
examples of dynamic constructors, which allocate memory at runtime, showcasing how constructors can be
used in more complex scenarios like dynamic memory management. Overall, this unit provides a thorough
understanding of constructors and destructors, key components in managing object lifecycle and memory in
C++.
In C++, constructors are special functions that are called when an object of a class is created. To check
if a number is prime using a constructor, we can define a class with a constructor that accepts an
integer. The constructor can then check if the number is prime and print the result. Here's an example
program to check whether a number is prime using a constructor:
cpp
Copy
#include <iostream>
using namespace std;
class Prime {
public:
Prime(int num) {
bool isPrime = true;
if (num <= 1) {
isPrime = false; // Numbers less than or equal to 1 are not prime
} else {
for (int i = 2; i * i <= num; i++) {
if (num % i == 0) {
isPrime = false; // Found a divisor, so it's not prime
break;
}
}
47
}
if (isPrime) {
cout << num << " is a prime number." << endl;
} else {
cout << num << " is not a prime number." << endl;
}
}
};
int main() {
int number;
cout << "Enter a number: ";
cin >> number;
Prime p(number); // Create object of Prime, the constructor will check for prime number
return 0;
}
In this program, the constructor of the Prime class checks if the passed number is prime or not. If it's
prime, it prints a message stating that the number is prime; otherwise, it indicates that the number is not
prime.
obj x;: This statement declares an object x of type obj and calls the default constructor (if it exists). It's
a normal object declaration where the object x is created using the default constructor of the class obj.
obj x();: This statement does not create an object of type obj. Instead, it declares a function named x
that returns an obj and takes no parameters. This is a case of most vexing parse, a situation in C++
where the compiler might interpret a statement ambiguously. In this case, the compiler interprets obj
x(); as a declaration of a function, not an object. Therefore, x is a function, not an object.
Example:
cpp
Copy
#include <iostream>
using namespace std;
class obj {
public:
obj() {
cout << "Constructor called!" << endl;
}
};
int main() {
obj x; // Declares an object x of type obj (constructor is called)
48
// obj x(); // Would be interpreted as a function declaration, not an object
return 0;
}
In the code above, obj x; creates an object and calls the constructor. On the other hand, obj x(); would
be treated as a function declaration and would not create an object.
3. Can one constructor of a class call another constructor of the same class to initialize the this
object? Justify your answers with an example.
Yes, one constructor of a class can call another constructor of the same class to initialize the this object.
This is called constructor delegation or constructor chaining. In C++, a constructor can call another
constructor in the same class using the syntax : constructor_name(parameters). This helps in avoiding
redundancy and ensures that common initialization code is shared between constructors.
Example:
cpp
Copy
#include <iostream>
using namespace std;
class MyClass {
public:
int x, y;
int main() {
MyClass obj1(10, 20); // Calls the constructor with two parameters
MyClass obj2(5); // Calls the constructor with one parameter, which calls the two-parameter
constructor
return 0;
}
In this example, the constructor MyClass(int xVal) delegates the initialization to the two-parameter
constructor MyClass(int xVal, int yVal) by passing xVal and 0 as arguments. This allows the reuse of
the two-parameter constructor's initialization code.
49
4. Should my constructors use “initialization lists” or “assignment”? Discuss.
In C++, constructors can initialize class members using either initialization lists or assignment. It is
generally recommended to use initialization lists rather than assignment for several reasons:
Efficiency: Initialization lists directly initialize class members, while assignment involves first default
initializing the members and then assigning values. This can lead to inefficiency, especially for non-
trivial data types like strings or vectors, where the object is first initialized with a default value and then
overwritten.
Const members and reference members: If a class contains constant or reference members, these
members must be initialized through the initialization list, as they cannot be assigned values after the
object has been created.
Clarity: Initialization lists are clearer because they directly indicate how each member is initialized.
It’s easier to read and maintain.
Example:
cpp
Copy
#include <iostream>
using namespace std;
class MyClass {
public:
const int x;
int y;
int main() {
MyClass obj(10, 20);
cout << "x: " << obj.x << ", y: " << obj.y << endl;
return 0;
}
Here, the member x is a constant, so it must be initialized using the initialization list. y is assigned
using the initialization list as well.
A constructor in C++ is a special function that is automatically called when an object of a class is
created. It initializes the object's data members. Constructors have the same name as the class and do
not have a return type. There are several types of constructors in C++:
1. Default Constructor: A constructor that takes no parameters. It is called when no arguments are
passed during object creation.
50
2. Parameterized Constructor: A constructor that takes parameters to initialize objects with specific
values.
3. Copy Constructor: A constructor that creates a new object as a copy of an existing object.
Example:
cpp
Copy
#include <iostream>
using namespace std;
class MyClass {
public:
int x, y;
// Default constructor
MyClass() {
x = 0;
y = 0;
}
// Parameterized constructor
MyClass(int xVal, int yVal) {
x = xVal;
y = yVal;
}
// Copy constructor
MyClass(const MyClass& other) {
x = other.x;
y = other.y;
}
void display() {
cout << "x: " << x << ", y: " << y << endl;
}
};
int main() {
MyClass obj1; // Calls the default constructor
MyClass obj2(10, 20); // Calls the parameterized constructor
MyClass obj3 = obj2; // Calls the copy constructor
obj1.display();
obj2.display();
obj3.display();
return 0;
}
51
In this example:
A copy constructor is used to create a new object as a copy of an existing object. It's called when an
object is passed by value or when an object is returned from a function by value.
cpp
Copy
#include <iostream>
using namespace std;
class MyClass {
public:
int x, y;
// Copy constructor
MyClass(const MyClass& other) {
x = other.x;
y = other.y;
cout << "Copy constructor called!" << endl;
}
void display() {
cout << "x: " << x << ", y: " << y << endl;
}
};
int main() {
MyClass obj1(10, 20); // Calls parameterized constructor
MyClass obj2 = obj1; // Calls copy constructor
obj1.display();
obj2.display();
return 0;
}
52
In this program, the copy constructor is used when obj2 is created as a copy of obj1. The copy
constructor initializes the new object's members with the values of the original object.
7. What about returning a local variable by value? Does the local exist as a separate object, or
does it get optimized away?
When a local variable is returned by value in C++, the local variable does not get optimized away.
Instead, the compiler typically creates a temporary copy of the local variable to return it to the caller.
This copy is returned by value, meaning that a new object is created for the returned value. This can
involve some overhead, but modern compilers optimize this process using Return Value
Optimization (RVO) or Copy Elision, which may eliminate the copying cost under certain
circumstances.
Here’s an example:
cpp
Copy
#include <iostream>
using namespace std;
class MyClass {
public:
int x;
MyClass getObject() {
MyClass obj(10); // Local object
return obj; // Return by value
}
};
int main() {
MyClass obj1(5);
MyClass obj2 = obj1.getObject(); // Return by value
In this case, obj is returned by value from the getObject() function. While it may seem like the local
variable would be discarded, modern compilers may optimize this by eliding the copy altogether.
However, technically, a new temporary object is created to hold the return value.
53
Unit 12 of the document "DECAP202_OBJECT_ORIENTED_PROGRAMMING" delves deeper into the
concepts of constructors and destructors in C++. It introduces the notion of default arguments, which are
values specified in function declarations that automatically fill in if the caller doesn't provide them. Default
arguments can be overwritten by the user if necessary, providing flexibility in function calls. The unit also
discusses constructors with default arguments, allowing constructors to accept default values for their
parameters. This feature is beneficial for simplifying object initialization by allowing some parameters to be
omitted during object creation.
Dynamic initialization of objects is another critical topic in this unit. This refers to initializing objects during
runtime, typically using constructors that accept parameters. Dynamic initialization is especially useful when
multiple constructors exist for a class, each requiring different inputs. This form of initialization, combined
with memory allocation via the new operator, offers flexibility in how objects are created and managed.
The unit concludes with a discussion of destructors, which are special member functions invoked when an
object is destroyed. Destructors help release any resources or memory allocated to an object, ensuring proper
cleanup. It is highlighted that destructors can only be defined once per class and are automatically called when
an object goes out of scope. In cases involving dynamic memory allocation, destructors are essential for
deallocating memory to prevent memory leaks. This unit underscores the importance of constructors and
destructors in managing the lifecycle of objects and memory in C++.
A constructor in C++ is a special member function of a class that is automatically invoked when an
object of that class is created. Its primary purpose is to initialize objects of the class with default or
specified values, ensuring that the object is in a valid state right after it is created. Constructors have the
same name as the class, and they do not have a return type, not even void. There are different types of
constructors, such as default constructors, parameterized constructors, and copy constructors, each
serving a specific role in object initialization. A default constructor does not take any parameters and
initializes the object with default values, while a parameterized constructor takes arguments and
initializes the object with user-defined values. A copy constructor is used to initialize an object by
copying data from another existing object.
Example:
cpp
Copy
#include <iostream>
using namespace std;
class MyClass {
public:
int x, y;
// Default constructor
MyClass() {
x = 0;
y = 0;
cout << "Default constructor called!" << endl;
54
}
// Parameterized constructor
MyClass(int xVal, int yVal) {
x = xVal;
y = yVal;
cout << "Parameterized constructor called!" << endl;
}
};
int main() {
MyClass obj1; // Calls default constructor
MyClass obj2(10, 20); // Calls parameterized constructor
return 0;
}
In the above code, obj1 invokes the default constructor, while obj2 uses the parameterized constructor.
A destructor is a special member function that is automatically called when an object of a class goes
out of scope or is explicitly deleted. The main purpose of a destructor is to perform cleanup tasks such
as releasing resources (memory, file handles, etc.) that were acquired during the lifetime of the object.
It is crucial to ensure that any dynamic memory allocated by the object is freed, preventing memory
leaks. Unlike constructors, destructors do not take parameters, do not return any value, and are invoked
once when the object is destroyed. In the context of dynamic memory allocation (using new),
destructors help to ensure that memory is released properly when an object is deleted or goes out of
scope.
Example:
cpp
Copy
#include <iostream>
using namespace std;
class MyClass {
public:
int *ptr;
int main() {
MyClass obj(100); // Constructor called, memory allocated
// Destructor will be called automatically when obj goes out of scope
return 0;
}
In the example above, the destructor ensures that the dynamically allocated memory for ptr is freed
when the object obj goes out of scope.
Constructors and destructors are both special member functions of a class, but they serve opposite
purposes and are called at different points in the lifecycle of an object.
Constructors are called automatically when an object is created. Their purpose is to initialize the
object's data members and set up any resources needed for the object’s lifetime. Constructors can take
parameters (parameterized constructors) or have no parameters (default constructors), and they can be
overloaded to provide different ways of initializing objects.
Destructors, on the other hand, are called automatically when an object is destroyed, either when it
goes out of scope or when it is explicitly deleted. The primary role of a destructor is to clean up any
resources (like dynamically allocated memory) that were acquired by the object during its lifetime. A
class can only have one destructor, which cannot take parameters or return any value.
Key differences:
Default arguments in C++ allow a function to be called without providing all of its arguments. If the
caller does not provide a value for an argument, the default value specified in the function declaration
or definition is used. Default arguments must be provided in the function declaration, and if used, they
must appear from right to left (i.e., default values can only be specified for the trailing parameters).
Example:
cpp
Copy
#include <iostream>
using namespace std;
class MyClass {
public:
// Function with default arguments
56
void display(int x = 10, int y = 20) {
cout << "x: " << x << ", y: " << y << endl;
}
};
int main() {
MyClass obj;
return 0;
}
In the above program, the display function uses default arguments for x and y. If no arguments are
passed, it uses the default values 10 and 20. If only one argument is passed, the second one defaults to
20. When both arguments are passed, the function uses those values.
A destructor is a member function that is automatically called when an object goes out of scope or is
explicitly deleted. Its primary role is to free any resources or memory that were allocated to the object
during its lifetime, thus helping to avoid memory leaks. A destructor has the same name as the class,
preceded by a tilde (~), and it does not take any parameters nor return any value. C++ will
automatically call the destructor when the object is destroyed (goes out of scope or is explicitly
deleted).
Example:
cpp
Copy
#include <iostream>
using namespace std;
class MyClass {
public:
int *ptr;
// Constructor
MyClass(int value) {
ptr = new int; // Allocate memory
*ptr = value;
cout << "Constructor: Memory allocated and value set to " << *ptr << endl;
}
// Destructor
~MyClass() {
delete ptr; // Deallocate memory
cout << "Destructor: Memory released!" << endl;
}
57
};
int main() {
MyClass obj(100); // Constructor called, memory allocated
// Destructor will be called automatically when obj goes out of scope
return 0;
}
In this program, the constructor allocates memory dynamically for the ptr pointer. The destructor is
responsible for releasing the memory when the object obj goes out of scope. The destructor is invoked
automatically when the object goes out of scope at the end of the main function, ensuring proper
resource management and preventing memory leaks.
The unit explains the syntax for defining derived classes, where the derived class inherits from the base class
using an access specifier (public, private, or protected). Public inheritance means that public members of the
base class remain public in the derived class, while private inheritance makes base class members private in
the derived class. Protected inheritance offers a middle ground where base class members become protected in
the derived class, making them accessible only within the class and its derived classes.
Different types of inheritance in C++ are detailed, including single inheritance, where a class inherits from a
single base class, and multiple inheritance, where a class inherits from more than one base class. Multilevel
inheritance, where a class is derived from another derived class, and hierarchical inheritance, where multiple
classes inherit from a single base class, are also discussed. Additionally, hybrid inheritance, a combination of
multiple inheritance types, is introduced.
The unit also addresses how to manage the accessibility of private members in derived classes. Specifically, it
explains how visibility modes (public, private, and protected) control the accessibility of base class members
in the derived class. Making private members inheritable involves changing the visibility of those members
through protected or public inheritance.
In conclusion, Unit 13 provides a detailed exploration of inheritance, its various types, and the mechanisms
for controlling member accessibility, all of which are crucial for creating extensible and maintainable C++
programs.
58
Inheritance is one of the key features of object-oriented programming (OOP) that allows a class
(known as a derived class or subclass) to inherit properties and behaviors (attributes and methods) from
another class (known as the base class or superclass). This helps in code reusability, reduces
redundancy, and establishes a relationship between the base and derived classes.
cpp
Copy
class Animal {
public:
void eat() {
cout << "Eating..." << endl;
}
};
Multiple Inheritance: A class can inherit from more than one base class.
o Example:
cpp
Copy
class Animal {
public:
void eat() {
cout << "Eating..." << endl;
}
};
class Pet {
public:
void play() {
cout << "Playing..." << endl;
}
};
Multilevel Inheritance: A class can inherit from another derived class, forming a chain.
o Example:
cpp
Copy
class Animal {
public:
void eat() {
cout << "Eating..." << endl;
}
};
cpp
Copy
class Animal {
public:
void eat() {
cout << "Eating..." << endl;
}
};
60
class Cat : public Animal {
public:
void meow() {
cout << "Meowing..." << endl;
}
};
In this case, both Dog and Cat inherit from Animal.
cpp
Copy
class Animal {
public:
void eat() {
cout << "Eating..." << endl;
}
};
class Pet {
public:
void play() {
cout << "Playing..." << endl;
}
};
2. Consider a situation where three kinds of inheritance are involved. Explain this situation with
an example.
A scenario where multilevel inheritance, multiple inheritance, and hierarchical inheritance all
come into play is often seen in complex systems where a base class provides common functionality,
derived classes extend this functionality in different ways, and multiple classes inherit from the same
base class.
Example:
cpp
Copy
class Animal {
public:
void eat() {
61
cout << "Eating..." << endl;
}
};
class Pet {
public:
void play() {
cout << "Playing..." << endl;
}
};
// Dog uses multilevel inheritance (from Mammal -> Animal) and multiple inheritance (from Mammal
and Pet)
class Dog : public Mammal, public Pet {
public:
void bark() {
cout << "Barking..." << endl;
}
};
In C++, access specifiers control the visibility and accessibility of class members. Both protected and
private restrict access, but they differ in their usage:
Private Members: Members declared as private are only accessible within the class itself. No outside
class or derived class (except through friend functions) can access these members directly.
o Example:
cpp
Copy
62
class MyClass {
private:
int x; // Private member
public:
void setX(int val) { x = val; }
int getX() { return x; }
};
Protected Members: Members declared as protected are accessible within the class and its derived
classes, but not outside the class hierarchy. This allows subclasses to access and modify the inherited
data directly.
o Example:
cpp
Copy
class MyClass {
protected:
int x; // Protected member
};
Multilevel inheritance is useful when there is a hierarchy or chain of classes, where each class builds
upon the functionality of the class above it. It helps in creating a class hierarchy that reflects real-world
relationships, like in the case of animals, vehicles, or employees. Multilevel inheritance promotes
reusability, as common functionalities in higher-level classes can be inherited by lower-level classes.
For example, consider a hierarchy of Vehicle -> Car -> ElectricCar. Each class can inherit and extend
the functionality of its predecessor while maintaining a logical structure.
5. Discuss a situation in which private derivation will be more appropriate as compared to public
derivation.
Private derivation is used when a class does not want to expose the base class's public and protected
members directly to the derived class. This is useful when we need to control or restrict access to the
base class's members but still want to reuse its functionality internally.
For example, in the case of implementing a complex data structure like a BankAccount, a derived class
might need to manage the account internally without exposing the account details (like balance,
account_number, etc.) to outside code:
cpp
Copy
class Account {
private:
int balance;
public:
63
void deposit(int amount) { balance += amount; }
int getBalance() { return balance; }
};
cpp
Copy
#include <iostream>
#include <vector>
using namespace std;
class Employee {
protected:
int empID;
string name;
string address;
string department;
public:
void getData(int id, string n, string addr, string dept) {
empID = id;
name = n;
address = addr;
department = dept;
}
void display() {
cout << "Employee ID: " << empID << endl;
cout << "Name: " << name << endl;
cout << "Address: " << address << endl;
cout << "Department: " << department << endl;
}
};
void displayEmployees() {
for (auto &emp : employees) {
64
emp.display();
cout << "------------------------" << endl;
}
}
};
int main() {
Employee emp1, emp2;
emp1.getData(101, "John Doe", "1234 Elm St", "Sales");
emp2.getData(102, "Jane Smith", "5678 Oak St", "Marketing");
Manager mgr;
mgr.addEmployee(emp1);
mgr.addEmployee(emp2);
return 0;
}
7. Differentiate between public and private inheritances with suitable examples.
Public Inheritance: When a class inherits publicly, the public and protected members of the base class
remain public and protected in the derived class. This maintains the "is-a" relationship between the base
and derived classes.
o Example:
cpp
Copy
class Animal {
public:
void eat() { cout << "Eating..." << endl; }
};
cpp
Copy
class Animal {
public:
void eat() { cout << "Eating..." << endl; }
};
65
class Dog : private Animal {
public:
void bark() { cout << "Barking..." << endl; }
};
8. Explain how a sub-class may inherit from multiple classes.
A subclass can inherit from multiple classes, a concept known as multiple inheritance. This allows a
derived class to inherit members from more than one base class. It is used when a class needs to
combine the functionality of more than one class.
Example:
cpp
Copy
class Animal {
public:
void eat() { cout << "Eating..." << endl; }
};
class Pet {
public:
void play() { cout << "Playing..." << endl; }
};
A virtual base class is used in diamond-shaped inheritance scenarios to prevent multiple copies of the
base class's data members. It ensures that only one instance of the base class is shared among all
derived classes. Virtual inheritance helps to avoid ambiguity and ensures a single copy of the base class
is inherited.
10. Write a C++ program that demonstrates the working of hybrid inheritance.
cpp
Copy
#include <iostream>
using namespace std;
class Vehicle {
public:
void start() { cout << "Vehicle started" << endl; }
};
class Engine {
public:
void fuel() { cout << "Engine fueled" << endl; }
66
};
int main() {
Car myCar;
myCar.start();
myCar.fuel();
myCar.drive();
return 0;
}
In this program, the Car class inherits from both Vehicle and Engine, demonstrating hybrid inheritance.
The unit details how to open and create files, using constructors or the open() function. The ofstream class
is used for creating and writing files, while the ifstream class is used for reading files. The fstream class
combines both input and output operations, useful for handling files in both ways. The file modes like
ios::in, ios::out, and ios::app are explained, determining whether a file is opened for reading, writing,
or appending.
The unit also covers the detection of the end-of-file (EOF) condition, an essential part of file processing. The
eof() function is introduced as a method to check whether the end of the file has been reached, allowing the
program to stop reading data when necessary. Various examples demonstrate reading from and writing to
files, including how to handle errors and close files after operations are completed.
In conclusion, Unit 14 provides a detailed exploration of file handling in C++, highlighting the use of stream
classes for file operations, file modes, and EOF detection to ensure proper file processing and management.
In C++, streams are used to handle input and output operations. A stream is a sequence of data
elements that are made available for processing, either for input from a device or file or for output to a
device or file. C++ provides two types of streams:
67
Input Stream (istream): Used to read data from a source (e.g., keyboard, file).
Output Stream (ostream): Used to write data to a destination (e.g., console, file).
Streams provide an abstraction that allows programs to interact with different types of I/O sources or
destinations, such as files, standard input, or output, without having to worry about the specifics of the
underlying hardware or system.
C++ provides a set of classes to manage streams, including cin for standard input, cout for standard
output, and file streams like ifstream and ofstream for file operations.
2. What are the uses of files in a computer system and how data can be written using C++?
Files are an essential part of a computer system because they allow data to be stored persistently,
beyond the runtime of a program. Files enable data to be saved, retrieved, and shared across programs
or sessions. They provide a way to store large amounts of information and ensure that data is not lost
when a program ends.
In C++, data can be written to files using output file streams. The ofstream class is used to create and
write data to a file. The basic syntax for writing to a file is:
cpp
Copy
#include <iostream>
#include <fstream>
using namespace std;
int main() {
ofstream file("example.txt"); // Open file for writing
if (file.is_open()) {
file << "Hello, world!" << endl; // Write to file
file.close(); // Close the file
} else {
cout << "Unable to open the file." << endl;
}
return 0;
}
This program creates a file named example.txt and writes "Hello, world!" to it.
1. Include the necessary header: The header #include <fstream> provides the file stream classes needed
for file I/O operations.
2. Create a file stream object: Use an ofstream object for writing to files or an ifstream object for
reading from files.
3. Open the file: Use the open() function or directly specify the filename when creating the object.
4. Perform file operations: Use the << operator (for ofstream) to write data to the file, or >> (for
ifstream) to read data from the file.
68
5. Close the file: After completing the file operations, close the file using the close() method to ensure
data is properly saved and resources are released.
4. What is a file mode? Describe the various file mode options available.
In C++, file modes are used to specify how a file should be opened (whether for reading, writing, or
appending data). The file mode is passed as an argument when opening a file using an ofstream,
ifstream, or fstream object. Some of the common file modes are:
A file can be opened in multiple modes at once by using a bitwise OR operation. For example:
cpp
Copy
ofstream file("example.txt", ios::out | ios::app);
This opens the file for writing and appends data if the file exists.
5. Describe the various approaches by which we can detect the end of file condition successfully.
In C++, detecting the End of File (EOF) is crucial to prevent reading beyond the file's contents. The
following methods can be used to detect EOF:
1. Using eof() function: The eof() function of a file stream object returns true when the end of the file is
reached. However, eof() returns true only after an attempt to read past the EOF, so it is typically
checked after reading.
Example:
cpp
Copy
if (file.eof()) {
// End of file reached
}
2. Using stream state check: The stream's state can be checked using the fail() function. If the stream
fails (e.g., due to reading past the EOF), it will return true.
Example:
cpp
Copy
if (file.fail()) {
// Handle error, possibly EOF
}
69
3. Using getline() or >> operator: The getline() function or extraction (>>) operator automatically stops
when the end of the file is encountered.
Example:
cpp
Copy
string line;
while (getline(file, line)) {
// Process line
}
6. Write a C++ program to demonstrate working of detection of end of file in C++.
cpp
Copy
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main() {
ifstream file("example.txt");
if (!file) {
cout << "Unable to open the file!" << endl;
return 1;
}
string line;
while (!file.eof()) {
getline(file, line);
cout << line << endl;
}
file.close();
return 0;
}
In this example, the program reads a file line by line and stops when the EOF is reached. The eof()
function is used to check if the end of the file has been encountered.
Persistent storage: Data stored in files remains available even after the program has ended, allowing it
to be accessed later.
Data sharing: Files enable data to be shared across different programs or systems.
Large data storage: Files allow the storage of large amounts of data, unlike in-memory storage which
is limited by RAM.
70
Organization: Files can be structured in various formats, making it easy to organize and retrieve
information.
A file can be opened in C++ using the open() method or by passing the filename directly during object
creation. Here's an example using ofstream for writing to a file:
cpp
Copy
#include <iostream>
#include <fstream>
using namespace std;
int main() {
ofstream file;
file.open("example.txt", ios::out); // Open the file for writing
if (file.is_open()) {
file << "Hello, file!" << endl;
file.close(); // Don't forget to close the file
} else {
cout << "Failed to open the file." << endl;
}
return 0;
}
In this example, the file example.txt is opened for writing. If the file opens successfully, the program
writes "Hello, file!" into the file.
9. Write full process with suitable C++ program for creating a new file.
Here is a C++ program that demonstrates creating a new file and writing data to it:
cpp
Copy
#include <iostream>
#include <fstream>
using namespace std;
int main() {
ofstream file("newfile.txt"); // Open or create a new file
if (file.is_open()) {
file << "This is a new file!" << endl;
file.close(); // Close the file
cout << "File created and data written successfully!" << endl;
} else {
cout << "Unable to create the file." << endl;
}
71
return 0;
}
In this program, the file newfile.txt is created and data is written to it. If the file does not exist, it will be
created.
1. Create a file stream object: You create an object of ifstream, ofstream, or fstream class, depending on
whether you want to read, write, or perform both operations.
2. Open the file: You can either specify the filename during object creation or use the open() method to
open an existing file or create a new one.
3. Set file modes: You can specify various file modes (e.g., ios::in, ios::out, ios::app, ios::binary) while
opening a file.
4. Perform operations: After the file is successfully opened, you can read from or write to the file.
5. Close the file: Always close the file after completing the file operations to release system resources.
cpp
Copy
ifstream file("example.txt", ios::in);
This opens the file example.txt for reading in text mode.
72