OOP in C++
OOP in C++
// Default constructor
Car() {
color = "Unknown";
model = "Unknown";
}
void display() {
cout << "Color: " << color << ", Model: " << model << endl;
}
};
int main() {
Car car1; // Calls the default constructor
car1.display(); // Outputs: Color: Unknown, Model: Unknown
return 0;
}
2. Parameterized Constructor
A parameterized constructor allows you to initialize an object with specific values at
the time of its creation. The constructor takes parameters, and you can pass arguments
to initialize the object with different values.
Example:
cpp
Copy code
class Car {
public:
string color;
string model;
// Parameterized constructor
Car(string c, string m) {
color = c;
model = m;
}
void display() {
cout << "Color: " << color << ", Model: " << model << endl;
}
};
int main() {
Car car1("Red", "Sedan"); // Calls parameterized constructor
car1.display(); // Outputs: Color: Red, Model: Sedan
return 0;
}
3. Copy Constructor
A copy constructor is used to create a new object as a copy of an existing object. It is
called when an object is passed by value, returned by value, or explicitly copied. The
copy constructor ensures that a proper copy is made, especially when the class
involves dynamic memory allocation.
Example:
cpp
Copy code
class Car {
public:
string color;
string model;
// Parameterized constructor
Car(string c, string m) {
color = c;
model = m;
}
// Copy constructor
Car(const Car &other) {
color = other.color;
model = other.model;
}
void display() {
cout << "Color: " << color << ", Model: " << model << endl;
}
};
int main() {
Car car1("Blue", "Coupe"); // Original object
Car car2 = car1; // Calls copy constructor
car2.display(); // Outputs: Color: Blue, Model: Coupe
return 0;
}
4. Dynamic Constructor
A dynamic constructor is used when an object is created dynamically using new and
memory needs to be allocated dynamically. This constructor typically uses pointers
and memory allocation techniques such as new or malloc.
Example:
cpp
Copy code
class Car {
public:
string *color;
string *model;
// Dynamic constructor
Car(string c, string m) {
color = new string(c);
model = new string(m);
}
void display() {
cout << "Color: " << *color << ", Model: " << *model << endl;
}
int main() {
Car *car1 = new Car("Black", "SUV"); // Dynamically created
object
car1->display(); // Outputs: Color: Black, Model: SUV
delete car1; // Free dynamically allocated memory
return 0;
}
Q2. A)What is operator overloading? Write the rules for operator overloading?
class Complex {
public:
int real;
int imag;
// Parameterized constructor
Complex(int r, int i) : real(r), imag(i) {}
// Overloading + operator to add two Complex numbers
Complex operator + (const Complex& obj) {
return Complex(real + obj.real, imag + obj.imag);
}
// Display method
void display() {
cout << real << " + " << imag << "i" << endl;
}
};
int main() {
Complex num1(5, 3), num2(2, 7);
Complex result = num1 + num2; // Using overloaded + operator
result.display(); // Outputs: 7 + 10i
return 0;
}
In the example, the + operator is overloaded to add two complex numbers by defining how
the + operator works for objects of the Complex class.
Rules for Operator Overloading
While operator overloading in C++ provides a lot of flexibility, there are certain rules and
limitations that must be followed when overloading operators:
1. Cannot Overload Certain Operators:
Some operators cannot be overloaded, including:
o :: (Scope resolution operator)
o . (Member access operator)
o .* (Pointer to member operator)
o sizeof (Size operator)
o typeid (Run-time type identification)
2. The Number of Operands Cannot Change:
You cannot change the number of operands the operator works with. For example, the
binary + operator will always take two operands.
3. Cannot Overload Built-in Operators for Primitive Types:
You cannot overload operators for built-in types (e.g., int, char, etc.). Operator
overloading only applies to user-defined types.
4. Must Preserve Operator Semantics:
The overloaded operator should behave similarly to its original semantics to avoid
confusing the programmer. For example, the + operator should not change its
behavior in an unexpected way.
5. Cannot Overload the Assignment Operator (=) with Custom Syntax:
The assignment operator can be overloaded, but it must return a reference to *this
and must follow a specific pattern to properly handle object assignment.
Example:
cpp
Copy code
class MyClass {
public:
int value;
MyClass& operator = (const MyClass& obj) {
if (this == &obj) return *this; // Handle self-assignment
value = obj.value;
return *this;
}
};
6. Friend Function for Certain Operators:
Some operators, like << and >> (stream insertion and extraction operators), are often
implemented as friend functions since they need access to the private or protected
members of a class.
Example:
cpp
Copy code
class MyClass {
private:
int value;
public:
MyClass(int v) : value(v) {}
B)Write a menu-driven program for addition ,substraction,display result of two distance (given in
meter and centimetre)using member function and friend function?
Ans:- Program:
cpp
Copy code
#include<iostream>
using namespace std;
class Distance {
private:
int meters; // stores distance in meters
int centimeters; // stores distance in centimeters
public:
// Constructor to initialize distance
Distance(int m = 0, int cm = 0) {
meters = m;
centimeters = cm;
}
int main() {
int choice, m1, cm1, m2, cm2;
cout << "Enter the first distance (in meters and centimeters):\n";
cout << "Meters: ";
cin >> m1;
cout << "Centimeters: ";
cin >> cm1;
cout << "Enter the second distance (in meters and centimeters):\n";
cout << "Meters: ";
cin >> m2;
cout << "Centimeters: ";
cin >> cm2;
do {
cout << "\nMenu:\n";
cout << "1. Add the two distances\n";
cout << "2. Subtract the two distances\n";
cout << "3. Compare the two distances\n";
cout << "4. Exit\n";
cout << "Enter your choice: ";
cin >> choice;
switch(choice) {
case 1: {
Distance result = d1.add(d2);
cout << "The result of addition is: ";
result.display();
break;
}
case 2: {
Distance result = d1.subtract(d2);
cout << "The result of subtraction is: ";
result.display();
break;
}
case 3: {
compare(d1, d2);
break;
}
case 4:
cout << "Exiting the program.\n";
break;
default:
cout << "Invalid choice. Please try again.\n";
}
} while(choice != 4);
return 0;
}
Sample Output:
vbnet
Copy code
Enter the first distance (in meters and centimeters):
Meters: 5
Centimeters: 80
Enter the second distance (in meters and centimeters):
Meters: 3
Centimeters: 40
Menu:
1. Add the two distances
2. Subtract the two distances
3. Compare the two distances
4. Exit
Enter your choice: 1
The result of addition is: 9 meters 20 centimeters
Menu:
1. Add the two distances
2. Subtract the two distances
3. Compare the two distances
4. Exit
Enter your choice: 2
The result of subtraction is: 2 meters 40 centimeters
Menu:
1. Add the two distances
2. Subtract the two distances
3. Compare the two distances
4. Exit
Enter your choice: 3
Distance 1 is greater.
Menu:
1. Add the two distances
2. Subtract the two distances
3. Compare the two distances
4. Exit
Enter your choice: 4
Exiting the program.
Q3.A)How is polymorphism Achived at a) compile time b)run time? What is the difference between
run time binding and compile time binding?
class Printer {
public:
void print(int i) {
cout << "Printing integer: " << i << endl;
}
void print(double d) {
cout << "Printing double: " << d << endl;
}
void print(string s) {
cout << "Printing string: " << s << endl;
}
};
int main() {
Printer p;
p.print(10); // Calls print(int)
p.print(10.5); // Calls print(double)
p.print("Hello"); // Calls print(string)
return 0;
}
In the above example:
The print function is overloaded to handle different types of arguments: integer, double,
and string.
The compiler determines which version of the print function to call at compile time, based
on the arguments passed.
Example of Operator Overloading (Compile-time Polymorphism):
cpp
Copy code
#include <iostream>
using namespace std;
class Complex {
public:
int real, imag;
int main() {
Complex c1(5, 3), c2(2, 7);
Complex c3 = c1 + c2; // Uses overloaded + operator
cout << "Result: " << c3.real << " + " << c3.imag << "i" << endl;
return 0;
}
In this case, the + operator is overloaded for the Complex class to perform addition on
complex numbers. The overload is resolved at compile time.
B) Run-Time Polymorphism (Dynamic Polymorphism)
Run-time polymorphism is achieved using inheritance and virtual functions. The function
to be invoked is decided at runtime based on the type of the object pointed to by the base
class pointer or reference.
Example of Run-time Polymorphism Using Virtual Functions:
cpp
Copy code
#include <iostream>
using namespace std;
class Animal {
public:
virtual void sound() { // Virtual function
cout << "Animal makes a sound" << endl;
}
};
int main() {
Animal* animal1 = new Dog(); // Animal pointer points to Dog object
Animal* animal2 = new Cat(); // Animal pointer points to Cat object
animal1->sound(); // Outputs: Dog barks
animal2->sound(); // Outputs: Cat meows
delete animal1;
delete animal2;
return 0;
}
In this example:
sound() is a virtual function in the base class Animal, and it is overridden in the derived
classes Dog and Cat.
At runtime, the program determines which sound() function to call based on the actual
type of the object (i.e., Dog or Cat) rather than the type of the pointer (Animal*).
This is called runtime polymorphism because the function to be called is resolved at
runtime.
Difference Between Compile-Time Binding and Run-Time Binding
1. Compile-Time Binding:
o Also known as Static Binding.
o The method or function to be invoked is determined at compile time.
o Achieved through function overloading or operator overloading.
o The decision is made based on the function signature or the type of the object.
o No virtual functions are involved.
o Faster because the compiler resolves function calls during compilation.
Example: Function overloading, operator overloading.
cpp
Copy code
class A {
public:
void show() { cout << "Class A" << endl; }
};
class B : public A {
public:
void show() { cout << "Class B" << endl; }
};
int main() {
A* obj = new B();
obj->show(); // This will call the version of show in class A at
compile time.
}
2. Run-Time Binding:
o Also known as Dynamic Binding.
o The method or function to be invoked is determined at runtime.
o Achieved using virtual functions in base classes and function overriding in derived
classes.
o The decision is made based on the actual object that the base class
pointer/reference is pointing to at runtime.
o Virtual functions are involved.
o Slower than compile-time binding due to the overhead of dynamic dispatch.
Example: Virtual function overriding.
cpp
Copy code
class Base {
public:
virtual void display() { cout << "Base Class Display" << endl; }
};
int main() {
Base* b = new Derived();
b->display(); // Resolves at runtime, calls Derived's display.
}
Key Differences Between Run-time Binding and Compile-time Binding:
Feature Compile-time Binding Run-time Binding
Binding Time At compile time At runtime
Types of Function overloading, Operator
Virtual function, Function overriding
Functions overloading
Slower due to overhead of dynamic
Performance Faster because resolved at compile time
dispatch
Flexibility Less flexible, decisions are fixed More flexible, decisions are dynamic
Use Cases Function/Operator Overloading Inheritance, Virtual Functions
Ans:- A virtual function in C++ is a member function in a base class that is declared using
the virtual keyword. It allows derived classes to override the base class function, and it
supports runtime polymorphism. The function to be called is determined at runtime based
on the type of object (not the type of pointer or reference) that is used to invoke the function.
This mechanism is crucial in achieving dynamic polymorphism.
Key Points about Virtual Functions:
1. Virtual Function Declaration: A virtual function is declared in the base class using
the virtual keyword.
2. Function Overriding: Derived classes can override the virtual function to provide
their own implementation.
3. Dynamic Binding: The function that is invoked is determined at runtime, depending
on the type of the object (not the pointer type).
4. Base Class Pointer: Virtual functions are typically invoked through a pointer or
reference to the base class.
5. Pure Virtual Function: A virtual function can be made pure virtual by assigning it
to 0 (making the class abstract).
Syntax:
cpp
Copy code
class Base {
public:
virtual void display() { // Virtual function
cout << "Base class display function" << endl;
}
};
class Derived : public Base {
public:
void display() override { // Overriding virtual function
cout << "Derived class display function" << endl;
}
};
Example of Virtual Function in C++
cpp
Copy code
#include <iostream>
using namespace std;
class Animal {
public:
// Virtual function
virtual void sound() {
cout << "Animal makes a sound" << endl;
}
~Dog() {
cout << "Dog destructor" << endl;
}
};
~Cat() {
cout << "Cat destructor" << endl;
}
};
int main() {
// Creating objects of derived classes
Animal* animal1 = new Dog();
Animal* animal2 = new Cat();
Q4. A)Enlist Header files in c++ draw and explain stream class hierarchy?
Copy code
ios (base class)
/ \
ifstream ofstream
/ \ / \
istream ostream fstream
| | |
iostream iostream
Explanation:
1. ios (Base Class):
o The ios class is the base class for all I/O stream classes. It defines the common
functionalities and properties for both input and output operations.
o It provides basic operations like formatting, error flags, and buffer management.
o The ios class also defines important flags like std::ios::in (for input) and
std::ios::out (for output).
2. istream (Input Stream):
o istream is derived from ios and is responsible for handling input operations (e.g.,
reading from the console or a file).
o It provides functions like operator>> to extract data from the stream.
o cin is an object of the istream class, and it's used to read input from the standard
input (keyboard).
3. ostream (Output Stream):
o ostream is another class derived from ios and is responsible for output operations
(e.g., writing to the console or a file).
o It provides functions like operator<< to insert data into the stream.
o cout is an object of the ostream class, and it's used to write output to the standard
output (screen).
4. fstream (File Stream):
o fstream is derived from both istream and ostream, and it supports both input
and output operations for files.
o It is used for reading from and writing to files.
o It includes functions like open(), close(), and read() for file handling
operations.
o fstream objects can be used for both input and output to files.
5. ifstream (Input File Stream):
o ifstream is a subclass of istream, specifically designed for input operations from
files.
o It allows reading from files using the same syntax as console input (i.e.,
operator>>).
o ifstream objects are typically used to open files for reading.
6. ofstream (Output File Stream):
o ofstream is a subclass of ostream, specifically designed for output operations to
files.
o It allows writing to files using the same syntax as console output (i.e., operator<<).
o ofstream objects are typically used to open files for writing.
Example of Using Stream Classes:
cpp
Copy code
#include <iostream>
#include <fstream>
using namespace std;
int main() {
// Creating an output file stream to write to a file
ofstream outFile("example.txt");
if (outFile.is_open()) {
outFile << "Hello, this is a file write operation!\n";
outFile.close(); // Close the file after writing
} else {
cout << "Unable to open file for writing.\n";
}
if (inFile.is_open()) {
string line;
while (getline(inFile, line)) { // Reading file line by line
cout << line << endl;
}
inFile.close(); // Close the file after reading
} else {
cout << "Unable to open file for reading.\n";
}
return 0;
}
Explanation of the Code:
ofstream is used to open a file for output and write a line of text to the file.
ifstream is used to open the same file for reading and display the contents on the console.
class Box {
public:
int length;
int main() {
Box box1(10); // Create first Box object
Box box2(20); // Create second Box object
cout << "Length of box1: " << box1.getLength() << endl; // Output: 10
cout << "Length of box2: " << box2.getLength() << endl; // Output: 20
if (box1.compare(box2)) {
cout << "Both boxes have the same length." << endl;
} else {
cout << "The boxes have different lengths." << endl; // This will
be executed
}
return 0;
}
Output:
mathematica
Copy code
Length of box1: 10
Length of box2: 20
The boxes have different lengths.
Explanation of the Code:
1. Class Definition: The class Box has a member variable length and a constructor that
initializes it.
2. Using this Pointer: In the getLength() function, the this pointer is used to refer to
the current object’s length. This is optional here, but demonstrates how the this
pointer can be used to access members.
3. Comparing Objects: The compare() function compares the length of the current
object (this->length) with another Box object (b.length). The this pointer is used
to refer to the current object in the comparison.
4. Output:
o The lengths of box1 and box2 are printed.
o The comparison between box1 and box2 shows that their lengths are different.
Key Points in the Example:
this->length: In the getLength() and compare() functions, this->length
accesses the length of the current object. This is especially useful when there are
naming conflicts or when you want to clarify that you are referring to the current
object's member variable.
The this pointer is implicitly passed to member functions like getLength() and
compare(). In this program, the explicit use of this is only for demonstration.
Why Use the this Pointer?
1. Disambiguation: The this pointer can help in situations where there is a naming
conflict between parameters and member variables.
2. Referring to Current Object: It helps refer to the current object explicitly,
particularly useful when implementing chained function calls (e.g., returning the
object itself from a function).
Example with Naming Conflict:
cpp
Copy code
#include <iostream>
using namespace std;
class Box {
public:
int length;
void display() {
cout << "Length of the box: " << this->length << endl; // Using
'this' to refer to the member
}
};
int main() {
Box box1(15); // Creating an object with length 15
box1.display(); // Output: Length of the box: 15
return 0;
}
Output:
mathematica
Copy code
Length of the box: 15
Q5.A)What is an exception? How is an exceptions handle in c++ ? What are the advantages of using
exceptions handling mechanism in a program?
int main() {
int x = 10, y = 0;
try {
cout << "Result: " << divide(x, y) << endl; // This will throw an
exception
}
catch (DivideByZeroException &e) {
cout << e.what() << endl; // Catch and handle divide by zero
exception
}
catch (exception &e) {
cout << "Exception: " << e.what() << endl; // Catch other types of
exceptions
}
return 0;
}
Output:
vbnet
Copy code
Error: Division by zero!
Explanation of the Program:
1. DivideByZeroException Class: This is a custom exception class derived from
std::exception. It overrides the what() function to return a custom error message.
2. divide Function: This function attempts to divide two integers. If the divisor b is
zero, it throws a DivideByZeroException.
3. try Block: In the main function, the try block calls divide(x, y). Since y is zero,
an exception is thrown.
4. catch Block: The catch block catches the thrown DivideByZeroException and
displays the error message using the what() function.
Advantages of Using Exception Handling Mechanism in C++:
1. Separation of Error-Handling Code from Regular Code:
o Exception handling allows you to separate normal logic from error-handling
code. This makes the program cleaner, easier to read, and more maintainable.
You don’t need to clutter your main code with error checks at every step.
2. Automatic Cleanup:
o When an exception is thrown, the runtime system performs automatic
cleanup of local objects. This ensures that any resources (like dynamically
allocated memory or file handles) are properly cleaned up, preventing resource
leaks.
3. Error Propagation:
o Exceptions propagate up the call stack until they are caught by a suitable
catch block. This means you don’t have to handle every error immediately,
and you can let higher-level code handle errors. This is particularly useful in
complex programs with nested function calls.
4. Catching Specific Errors:
o The ability to define and catch specific types of exceptions allows for precise
handling of different errors. You can catch different types of exceptions in
different catch blocks and apply appropriate solutions to each situation (e.g.,
handle division by zero differently from file not found errors).
5. Improved Program Stability:
o Exception handling increases the robustness of the program. Instead of the
program crashing on encountering an error, the exception handling mechanism
can ensure that the program continues to run smoothly or shuts down
gracefully.
6. Better Debugging and Maintenance:
o With exception handling, it is easier to debug and maintain a program because
you can define clear error messages and isolate errors in the code. The use of
try, throw, and catch provides a structured way of identifying and resolving
problems in a program.
B)Write a program in c++ by using swap () function from the functional template?
Ans:- A function template in C++ allows you to write generic functions that can work with
any data type. The swap() function is commonly used to exchange the values of two
variables. Using function templates, we can create a swap() function that works with any
data type, including integers, floats, or custom objects.
Here is an example program using a swap() function with a function template:
Code:
cpp
Copy code
#include <iostream>
using namespace std;
int main() {
// Test swap with integers
int x = 5, y = 10;
cout << "Before swap (int): x = " << x << ", y = " << y << endl;
swap(x, y); // Call swap for integers
cout << "After swap (int): x = " << x << ", y = " << y << endl;
return 0;
}
Output:
csharp
Copy code
Before swap (int): x = 5, y = 10
After swap (int): x = 10, y = 5
int main() {
vector<int> v = {1, 2, 3, 4};
v.push_back(5); // Add an element to the end of the vector
for(int i = 0; i < v.size(); i++) {
cout << v[i] << " ";
}
return 0;
}
2. Algorithms:
STL algorithms are a set of predefined functions that perform operations on containers, such
as searching, sorting, and manipulating data. These algorithms are designed to be used with
STL containers and iterators.
Some commonly used algorithms in STL include:
Sorting algorithms: sort(), reverse()
Searching algorithms: find(), binary_search()
Modification algorithms: copy(), swap(), replace()
Non-modifying algorithms: count(), equal(), min_element()
Example using sort() and find() algorithms:
cpp
Copy code
#include <iostream>
#include <vector>
#include <algorithm> // For algorithms like sort and find
using namespace std;
int main() {
vector<int> v = {5, 1, 8, 4, 2};
sort(v.begin(), v.end()); // Sort the vector
for(int i : v) {
cout << i << " ";
}
cout << endl;
int main() {
vector<int> v = {1, 2, 3, 4, 5};
vector<int>::iterator it; // Declare an iterator for vector
int main() {
vector<int> v = {10, 20, 5, 15};
sort(v.begin(), v.end(), Compare()); // Use functor with sort
for(int i : v) {
cout << i << " ";
}
return 0;
}
I) Iterators
ii)Container adaptors
iii)Vector