1.
Design C++ classes with static members, methods with default arguments, and friend
functions. (for example, design matrix and vector classes with static allocation, and a
friend function to do matrix-vector multiplication)
#include <iostream>
#include <vector>
class Vector {
private:
std::vector<double> elements;
public:
// Constructors
Vector() = default; // Default constructor
Vector(std::initializer_list<double> init) : elements(init) {}
// Method to get the size of the vector
static size_t size(const Vector& v) {
return v.elements.size();
}
// Friend function for matrix-vector multiplication
friend Vector matrixVectorMultiply(const Matrix& mat, const Vector& vec);
};
class Matrix {
private:
std::vector<std::vector<double>> elements;
public:
// Constructors
Matrix() = default; // Default constructor
Matrix(std::initializer_list<std::initializer_list<double>> init) : elements(init) {}
// Method to get the number of rows of the matrix
static size_t rows(const Matrix& mat) {
return mat.elements.size();
}
// Method to get the number of columns of the matrix
static size_t columns(const Matrix& mat) {
return (mat.elements.size() > 0) ? mat.elements[0].size() : 0;
}
// Friend function for matrix-vector multiplication
friend Vector matrixVectorMultiply(const Matrix& mat, const Vector& vec);
};
// Friend function implementation for matrix-vector multiplication
Vector matrixVectorMultiply(const Matrix& mat, const Vector& vec) {
// Check if the matrix and vector dimensions are compatible
if (Matrix::columns(mat) != Vector::size(vec)) {
std::cerr << "Error: Incompatible dimensions for matrix-vector multiplication." << std::endl;
return Vector(); // Return an empty vector in case of error
}
Vector result;
// Perform matrix-vector multiplication
for (size_t i = 0; i < Matrix::rows(mat); ++i) {
double sum = 0.0;
for (size_t j = 0; j < Matrix::columns(mat); ++j) {
sum += mat.elements[i][j] * vec.elements[j];
}
result.elements.push_back(sum);
}
return result;
}
int main() {
// Example usage
Matrix matrix({{1, 2, 3}, {4, 5, 6}});
Vector vector({2, 3, 4});
// Perform matrix-vector multiplication
Vector result = matrixVectorMultiply(matrix, vector);
// Display the result
std::cout << "Result of matrix-vector multiplication:" << std::endl;
for (double value : result.elements) {
std::cout << value << " ";
}
std::cout << std::endl;
return 0;
}
Output:
2. Implement matrix class with dynamic memory allocation and necessary methods. Give
proper constructor, destructor, copy constructor, and overloading of the assignment
operator.
#include <iostream>
#include <stdexcept>
class Matrix {
private:
size_t rows;
size_t columns;
double** elements;
public:
// Constructors
Matrix(size_t rows, size_t columns) : rows(rows), columns(columns) {
// Allocate memory for the matrix
elements = new double*[rows];
for (size_t i = 0; i < rows; ++i) {
elements[i] = new double[columns];
}
// Initialize elements to 0
for (size_t i = 0; i < rows; ++i) {
for (size_t j = 0; j < columns; ++j) {
elements[i][j] = 0.0;
}
}
}
// Destructor
~Matrix() {
// Deallocate memory for the matrix
for (size_t i = 0; i < rows; ++i) {
delete[] elements[i];
}
delete[] elements;
}
// Copy constructor
Matrix(const Matrix& other) : rows(other.rows), columns(other.columns) {
// Allocate memory for the new matrix
elements = new double*[rows];
for (size_t i = 0; i < rows; ++i) {
elements[i] = new double[columns];
}
// Copy elements from the other matrix
for (size_t i = 0; i < rows; ++i) {
for (size_t j = 0; j < columns; ++j) {
elements[i][j] = other.elements[i][j];
}
}
}
// Overloading the assignment operator
Matrix& operator=(const Matrix& other) {
if (this != &other) {
// Deallocate existing memory
for (size_t i = 0; i < rows; ++i) {
delete[] elements[i];
}
delete[] elements;
// Copy size
rows = other.rows;
columns = other.columns;
// Allocate new memory
elements = new double*[rows];
for (size_t i = 0; i < rows; ++i) {
elements[i] = new double[columns];
}
// Copy elements from the other matrix
for (size_t i = 0; i < rows; ++i) {
for (size_t j = 0; j < columns; ++j) {
elements[i][j] = other.elements[i][j];
}
}
}
return *this;
}
// Method to get the number of rows
size_t getRows() const {
return rows;
}
// Method to get the number of columns
size_t getColumns() const {
return columns;
}
// Method to access elements
double& at(size_t row, size_t col) {
if (row >= rows || col >= columns) {
throw std::out_of_range("Matrix indices out of range");
}
return elements[row][col];
}
// Method to display the matrix
void display() const {
for (size_t i = 0; i < rows; ++i) {
for (size_t j = 0; j < columns; ++j) {
std::cout << elements[i][j] << " ";
}
std::cout << std::endl;
}
}
};
int main() {
// Example usage
Matrix mat1(2, 3);
mat1.at(0, 0) = 1.0;
mat1.at(0, 1) = 2.0;
mat1.at(0, 2) = 3.0;
mat1.at(1, 0) = 4.0;
mat1.at(1, 1) = 5.0;
mat1.at(1, 2) = 6.0;
Matrix mat2 = mat1; // Copy constructor
Matrix mat3(3, 2);
mat3 = mat1; // Overloaded assignment operator
// Display matrices
std::cout << "Matrix 1:" << std::endl;
mat1.display();
std::cout << "Matrix 2 (copy of Matrix 1 using copy constructor):" << std::endl;
mat2.display();
std::cout << "Matrix 3 (assigned from Matrix 1 using overloaded assignment operator):" << std::endl;
mat3.display();
return 0;
}
Output:
3. Implement complex number class with necessary operator overloading and type
conversions such as integer to complex , double to complex, complex to double etc.
#include <iostream>
#include <cmath>
class Complex {
private:
double real;
double imag;
public:
// Constructors
Complex() : real(0.0), imag(0.0) {}
Complex(double r, double i) : real(r), imag(i) {}
// Getter functions
double getReal() const { return real; }
double getImag() const { return imag; }
// Operator overloading for basic arithmetic operations
Complex operator+(const Complex& other) const {
return Complex(real + other.real, imag + other.imag);
}
Complex operator-(const Complex& other) const {
return Complex(real - other.real, imag - other.imag);
}
Complex operator*(const Complex& other) const {
return Complex((real * other.real) - (imag * other.imag),
(real * other.imag) + (imag * other.real));
}
Complex operator/(const Complex& other) const {
double denominator = (other.real * other.real) + (other.imag * other.imag);
if (denominator == 0.0) {
// Handle division by zero
throw std::invalid_argument("Division by zero");
}
return Complex(((real * other.real) + (imag * other.imag)) / denominator,
((imag * other.real) - (real * other.imag)) / denominator);
}
// Type conversion from double to complex
Complex(double r) : real(r), imag(0.0) {}
// Type conversion from int to complex
Complex(int r) : real(static_cast<double>(r)), imag(0.0) {}
// Type conversion from complex to double
operator double() const {
return real; // Consideration: You might want to return magnitude or other value.
}
// Stream insertion operator
friend std::ostream& operator<<(std::ostream& os, const Complex& c) {
os << "(" << c.real << " + i" << c.imag << ")";
return os;
}
// Stream extraction operator
friend std::istream& operator>>(std::istream& is, Complex& c) {
char discard;
is >> discard >> c.real >> discard >> c.imag >> discard;
return is;
}
};
int main() {
// Example usage of the Complex class
Complex c1(3.0, 4.0);
Complex c2(1.5, 2.5);
// Arithmetic operations
Complex sum = c1 + c2;
Complex diff = c1 - c2;
Complex product = c1 * c2;
Complex quotient = c1 / c2;
std::cout << "c1: " << c1 << std::endl;
std::cout << "c2: " << c2 << std::endl;
std::cout << "Sum: " << sum << std::endl;
std::cout << "Difference: " << diff << std::endl;
std::cout << "Product: " << product << std::endl;
std::cout << "Quotient: " << quotient << std::endl;
// Type conversions
double c1AsDouble = static_cast<double>(c1);
int c2AsInt = static_cast<int>(c2);
std::cout << "c1 as double: " << c1AsDouble << std::endl;
std::cout << "c2 as int: " << c2AsInt << std::endl;
// Stream extraction and insertion
Complex inputComplex;
std::cout << "Enter a complex number (e.g., (3.0 + i4.0)): ";
std::cin >> inputComplex;
std::cout << "You entered: " << inputComplex << std::endl;
return 0;
}
Output:
4. Overload the new and delete operators to provide a custom dynamic allocation of
memory.
#include <iostream>
#include <new>
class Matrix {
private:
size_t rows;
size_t columns;
double** elements;
public:
// Overloaded new operator for custom memory allocation
void* operator new(std::size_t size) {
std::cout << "Custom new operator called. Size: " << size << std::endl;
return ::operator new(size);
}
// Overloaded delete operator for custom memory deallocation
void operator delete(void* ptr) noexcept {
std::cout << "Custom delete operator called." << std::endl;
::operator delete(ptr);
}
// Constructors, destructor, and other methods remain the same...
// Method to get the number of rows
size_t getRows() const {
return rows;
}
// Method to get the number of columns
size_t getColumns() const {
return columns;
}
// Method to access elements
double& at(size_t row, size_t col) {
if (row >= rows || col >= columns) {
throw std::out_of_range("Matrix indices out of range");
}
return elements[row][col];
}
// Method to display the matrix
void display() const {
for (size_t i = 0; i < rows; ++i) {
for (size_t j = 0; j < columns; ++j) {
std::cout << elements[i][j] << " ";
}
std::cout << std::endl;
}
}
};
int main() {
// Example usage
Matrix* mat = new Matrix(2, 3);
// Access and modify elements
mat->at(0, 0) = 1.0;
mat->at(0, 1) = 2.0;
mat->at(0, 2) = 3.0;
mat->at(1, 0) = 4.0;
mat->at(1, 1) = 5.0;
mat->at(1, 2) = 6.0;
// Display the matrix
std::cout << "Matrix:" << std::endl;
mat->display();
// Delete the matrix
delete mat;
return 0;
}
Output:
5. Develop C++ class hierarchy for various types of inheritances.
#include <iostream>
// Base class
class Animal {
public:
void eat() {
std::cout << "Animal is eating." << std::endl;
}
void sleep() {
std::cout << "Animal is sleeping." << std::endl;
}
};
// Single Inheritance
class Dog : public Animal {
public:
void bark() {
std::cout << "Dog is barking." << std::endl;
}
};
// Multiple Inheritance
class Bird {
public:
void fly() {
std::cout << "Bird is flying." << std::endl;
}
};
class FlyingDog : public Dog, public Bird {
public:
// Inherits both bark() from Dog and fly() from Bird
};
// Hierarchical Inheritance
class Cat : public Animal {
public:
void meow() {
std::cout << "Cat is meowing." << std::endl;
}
};
// Multilevel Inheritance
class Kitten : public Cat {
public:
void play() {
std::cout << "Kitten is playing." << std::endl;
}
};
int main() {
// Single Inheritance example
Dog myDog;
myDog.eat(); // Inherited from Animal
myDog.sleep(); // Inherited from Animal
myDog.bark(); // Specific to Dog
// Multiple Inheritance example
FlyingDog flyingDog;
flyingDog.eat(); // Inherited from Animal
flyingDog.sleep(); // Inherited from Animal
flyingDog.bark(); // Inherited from Dog
flyingDog.fly(); // Inherited from Bird
// Hierarchical Inheritance example
Cat myCat;
myCat.eat(); // Inherited from Animal
myCat.sleep(); // Inherited from Animal
myCat.meow(); // Specific to Cat
// Multilevel Inheritance example
Kitten myKitten;
myKitten.eat(); // Inherited from Animal
myKitten.sleep(); // Inherited from Animal
myKitten.meow(); // Inherited from Cat
myKitten.play(); // Specific to Kitten
return 0;
}
Output:
6. Design a simple test application to demonstrate dynamic polymorphism and RTTI
#include <iostream>
#include <vector>
#include <typeinfo>
// Base class
class Shape {
public:
virtual void draw() const {
std::cout << "Drawing a shape." << std::endl;
}
virtual ~Shape() = default; // Virtual destructor for proper cleanup
};
// Derived class: Circle
class Circle : public Shape {
public:
void draw() const override {
std::cout << "Drawing a circle." << std::endl;
}
};
// Derived class: Rectangle
class Rectangle : public Shape {
public:
void draw() const override {
std::cout << "Drawing a rectangle." << std::endl;
}
};
int main() {
// Create a vector of shapes using dynamic polymorphism
std::vector<Shape*> shapes;
shapes.push_back(new Circle());
shapes.push_back(new Rectangle());
shapes.push_back(new Circle());
shapes.push_back(new Rectangle());
// Draw each shape using dynamic polymorphism
for (const Shape* shape : shapes) {
shape->draw();
}
// Demonstrate RTTI (Run-Time Type Information)
for (const Shape* shape : shapes) {
// Check the type using typeid and dynamic_cast
if (typeid(*shape) == typeid(Circle)) {
const Circle* circle = dynamic_cast<const Circle*>(shape);
if (circle != nullptr) {
std::cout << "Type: Circle" << std::endl;
}
} else if (typeid(*shape) == typeid(Rectangle)) {
const Rectangle* rectangle = dynamic_cast<const Rectangle*>(shape);
if (rectangle != nullptr) {
std::cout << "Type: Rectangle" << std::endl;
}
}
}
// Cleanup: Delete dynamically allocated shapes
for (Shape* shape : shapes) {
delete shape;
}
return 0;
}
Output:
7. Develop a template of the linked-list class and its methods.
#include <iostream>
template <typename T>
class LinkedList {
private:
// Node structure for the linked list
struct Node {
T data;
Node* next;
Node(const T& value) : data(value), next(nullptr) {}
};
Node* head; // Pointer to the head of the linked list
public:
// Constructor
LinkedList() : head(nullptr) {}
// Destructor
~LinkedList() {
clear();
}
// Function to insert a new element at the beginning of the list
void insertFront(const T& value) {
Node* newNode = new Node(value);
newNode->next = head;
head = newNode;
}
// Function to insert a new element at the end of the list
void insertEnd(const T& value) {
Node* newNode = new Node(value);
if (head == nullptr) {
head = newNode;
return;
}
Node* current = head;
while (current->next != nullptr) {
current = current->next;
}
current->next = newNode;
}
// Function to delete the first occurrence of a value in the list
void remove(const T& value) {
if (head == nullptr) {
return;
}
if (head->data == value) {
Node* temp = head;
head = head->next;
delete temp;
return;
}
Node* current = head;
while (current->next != nullptr && current->next->data != value) {
current = current->next;
}
if (current->next != nullptr) {
Node* temp = current->next;
current->next = current->next->next;
delete temp;
}
}
// Function to display the elements of the list
void display() const {
Node* current = head;
while (current != nullptr) {
std::cout << current->data << " ";
current = current->next;
}
std::cout << std::endl;
}
// Function to clear the entire list
void clear() {
while (head != nullptr) {
Node* temp = head;
head = head->next;
delete temp;
}
}
};
int main() {
// Example usage of the LinkedList template
LinkedList<int> intList;
intList.insertEnd(1);
intList.insertEnd(2);
intList.insertFront(0);
intList.display(); // Output: 0 1 2
intList.remove(1);
intList.display(); // Output: 0 2
// Example with strings
LinkedList<std::string> stringList;
stringList.insertEnd("Hello");
stringList.insertEnd("World");
stringList.display(); // Output: Hello World
return 0;
}
Output:
8. Develop templates of standard sorting algorithms such as bubble sort, insertion sort and
quick sort.
➢ Bubble Sort
#include <iostream>
#include <vector>
template <typename T>
void bubbleSort(std::vector<T>& arr) {
size_t n = arr.size();
for (size_t i = 0; i < n - 1; ++i) {
for (size_t j = 0; j < n - i - 1; ++j) {
if (arr[j] > arr[j + 1]) {
std::swap(arr[j], arr[j + 1]);
}
}
}
}
int main() {
// Example usage of bubbleSort template
std::vector<int> intArray = {64, 34, 25, 12, 22, 11, 90};
bubbleSort(intArray);
std::cout << "Sorted array: ";
for (const auto& elem : intArray) {
std::cout << elem << " ";
}
std::cout << std::endl;
return 0;
}
Output:
➢ Insertion Sort
#include <iostream>
#include <vector>
template <typename T>
void insertionSort(std::vector<T>& arr) {
size_t n = arr.size();
for (size_t i = 1; i < n; ++i) {
T key = arr[i];
int j = i - 1;
while (j >= 0 && arr[j] > key) {
arr[j + 1] = arr[j];
--j;
}
arr[j + 1] = key;
}
}
int main() {
// Example usage of insertionSort template
std::vector<int> intArray = {64, 34, 25, 12, 22, 11, 90};
insertionSort(intArray);
std::cout << "Sorted array: ";
for (const auto& elem : intArray) {
std::cout << elem << " ";
}
std::cout << std::endl;
return 0;
}
Output:
➢ Quick Sort
#include <iostream>
#include <vector>
template <typename T>
size_t partition(std::vector<T>& arr, size_t low, size_t high) {
T pivot = arr[high];
size_t i = low - 1;
for (size_t j = low; j < high; ++j) {
if (arr[j] <= pivot) {
++i;
std::swap(arr[i], arr[j]);
}
}
std::swap(arr[i + 1], arr[high]);
return i + 1;
}
template <typename T>
void quickSort(std::vector<T>& arr, size_t low, size_t high) {
if (low < high) {
size_t pivotIndex = partition(arr, low, high);
if (pivotIndex > 0) {
quickSort(arr, low, pivotIndex - 1);
}
quickSort(arr, pivotIndex + 1, high);
}
}
int main() {
// Example usage of quickSort template
std::vector<int> intArray = {64, 34, 25, 12, 22, 11, 90};
quickSort(intArray, 0, intArray.size() - 1);
std::cout << "Sorted array: ";
for (const auto& elem : intArray) {
std::cout << elem << " ";
}
std::cout << std::endl;
return 0;
}
Output:
9. Design stack and queue classes with necessary exception handling.
➢ Stack Class
#include <iostream>
#include <stdexcept>
#include <vector>
template <typename T>
class Stack {
private:
std::vector<T> elements;
public:
// Push an element onto the stack
void push(const T& value) {
elements.push_back(value);
}
// Pop the top element from the stack
void pop() {
if (empty()) {
throw std::underflow_error("Stack is empty. Cannot pop.");
}
elements.pop_back();
}
// Get the top element of the stack
T& top() {
if (empty()) {
throw std::underflow_error("Stack is empty. Cannot get top.");
}
return elements.back();
}
// Check if the stack is empty
bool empty() const {
return elements.empty();
}
// Get the size of the stack
size_t size() const {
return elements.size();
}
};
int main() {
// Example usage of Stack class
Stack<int> intStack;
try {
intStack.push(10);
intStack.push(20);
intStack.push(30);
std::cout << "Top element: " << intStack.top() << std::endl;
intStack.pop();
std::cout << "Top element after pop: " << intStack.top() << std::endl;
while (!intStack.empty()) {
intStack.pop();
}
// Attempting to pop from an empty stack (exception handling)
intStack.pop(); // This should throw an exception
} catch (const std::exception& e) {
std::cerr << "Exception: " << e.what() << std::endl;
}
return 0;
}
Output:
➢ Queue Class
#include <iostream>
#include <stdexcept>
#include <queue>
template <typename T>
class Queue {
private:
std::queue<T> elements;
public:
// Enqueue an element into the queue
void enqueue(const T& value) {
elements.push(value);
}
// Dequeue an element from the front of the queue
void dequeue() {
if (empty()) {
throw std::underflow_error("Queue is empty. Cannot dequeue.");
}
elements.pop();
}
// Get the front element of the queue
T& front() {
if (empty()) {
throw std::underflow_error("Queue is empty. Cannot get front.");
}
return elements.front();
}
// Check if the queue is empty
bool empty() const {
return elements.empty();
}
// Get the size of the queue
size_t size() const {
return elements.size();
}
};
int main() {
// Example usage of Queue class
Queue<int> intQueue;
try {
intQueue.enqueue(10);
intQueue.enqueue(20);
intQueue.enqueue(30);
std::cout << "Front element: " << intQueue.front() << std::endl;
intQueue.dequeue();
std::cout << "Front element after dequeue: " << intQueue.front() << std::endl;
while (!intQueue.empty()) {
intQueue.dequeue();
}
// Attempting to dequeue from an empty queue (exception handling)
intQueue.dequeue(); // This should throw an exception
} catch (const std::exception& e) {
std::cerr << "Exception: " << e.what() << std::endl;
}
return 0;
}
Output:
10. Write a C++ program that randomly generates complex numbers (use previously
designed complex class ) and write them two per line in a file along with an operator (+,-
,*, or /). The numbers are written to file in the format (a+ib). Write another program to
read one line at a time from this file.
#include <iostream>
#include <fstream>
#include <cstdlib> // for rand() and srand()
#include <ctime> // for time()
#include <sstream>
#include <vector>
#include <string>
// Complex class definition (assuming you have a complex class)
class Complex {
public:
double real;
double imag;
Complex(double r, double i) : real(r), imag(i) {}
// Overloaded stream insertion operator for formatting
friend std::ostream& operator<<(std::ostream& os, const Complex& c) {
return os << "(" << c.real << "+i" << c.imag << ")";
}
};
// Function to generate a random complex number
Complex generateRandomComplex() {
double realPart = static_cast<double>(rand()) / RAND_MAX * 10.0; // Random real part between 0 and
10
double imagPart = static_cast<double>(rand()) / RAND_MAX * 10.0; // Random imag part between 0
and 10
return Complex(realPart, imagPart);
}
// Function to generate a random operator
char generateRandomOperator() {
char operators[] = {'+', '-', '*', '/'};
return operators[rand() % 4];
}
int main() {
// Seed the random number generator
srand(static_cast<unsigned>(time(nullptr)));
// Open a file for writing
std::ofstream outFile("complex_numbers.txt");
// Write two complex numbers and an operator per line to the file
for (int i = 0; i < 5; ++i) {
Complex c1 = generateRandomComplex();
Complex c2 = generateRandomComplex();
char op = generateRandomOperator();
outFile << c1 << " " << op << " " << c2 << std::endl;
}
// Close the file
outFile.close();
std::cout << "Complex numbers written to file." << std::endl;
return 0;
}
// Complex class definition (assuming you have a complex class)
class Complex {
public:
double real;
double imag;
Complex(double r, double i) : real(r), imag(i) {}
// Overloaded stream insertion operator for formatting
friend std::ostream& operator<<(std::ostream& os, const Complex& c) {
return os << "(" << c.real << "+i" << c.imag << ")";
}
};
// Function to parse a line from the file
void parseLine(const std::string& line) {
std::istringstream iss(line);
Complex c1(0.0, 0.0), c2(0.0, 0.0);
char op;
// Parse the complex numbers and operator from the line
iss >> c1 >> op >> c2;
// Display the parsed values
std::cout << "Complex Number 1: " << c1 << std::endl;
std::cout << "Operator: " << op << std::endl;
std::cout << "Complex Number 2: " << c2 << std::endl;
std::cout << "---------------------------" << std::endl;
}
int main() {
// Open the file for reading
std::ifstream inFile("complex_numbers.txt");
if (!inFile) {
std::cerr << "Error opening file." << std::endl;
return 1;
}
// Read one line at a time and process it
std::string line;
while (std::getline(inFile, line)) {
parseLine(line);
}
// Close the file
inFile.close();
return 0;
}
Output: