Followers

Wednesday, July 17, 2024

An implementation of queue operations using an array in C++


 

#include <iostream>

using namespace std;

class Queue {

private:

    int front, rear, size;

    int* queueArray;

    int capacity;

public:

    Queue(int capacity) {

        this->capacity = capacity;

        queueArray = new int[capacity];

        front = size = 0;

        rear = capacity - 1;

    }

    ~Queue() {

        delete[] queueArray;

    }

    bool isFull() {

        return (size == capacity);

    }

    bool isEmpty() {

        return (size == 0);

    }

    void enqueue(int item) {

        if (isFull()) {

            cout << "Queue overflow! Cannot enqueue " << item << endl;

            return;

        }

        rear = (rear + 1) % capacity;

        queueArray[rear] = item;

        size++;

        cout << item << " enqueued to queue" << endl;

    }

    int dequeue() {

        if (isEmpty()) {

            cout << "Queue underflow! Cannot dequeue" << endl;

            return -1; // Assuming -1 as error value for queue underflow

        }

        int item = queueArray[front];

        front = (front + 1) % capacity;

        size--;

        return item;

    }


    int frontElement() {

        if (isEmpty()) {

            cout << "Queue is empty! Cannot retrieve front element" << endl;

            return -1; // Assuming -1 as error value for empty queue

        }

        return queueArray[front];

    }

    int rearElement() {

        if (isEmpty()) {

            cout << "Queue is empty! Cannot retrieve rear element" << endl;

            return -1; // Assuming -1 as error value for empty queue

        }

        return queueArray[rear];

    }

};

int main() {

    int capacity;

    cout << "Enter the capacity of the queue: ";

    cin >> capacity;

    Queue queue(capacity);

    queue.enqueue(10);

    queue.enqueue(20);

    queue.enqueue(30);

    queue.enqueue(40);

    cout << "Front element is: " << queue.frontElement() << endl;

    cout << "Rear element is: " << queue.rearElement() << endl;

    cout << "Dequeued element: " << queue.dequeue() << endl;

    cout << "Front element after dequeue: " << queue.frontElement() << endl;

    // Attempt to dequeue from empty queue

    while (!queue.isEmpty()) {

        queue.dequeue();

    }

    queue.dequeue();

    return 0;

}


Explanation:

  1. Queue Class:

    • Contains a constructor to initialize the queue with a given capacity.
    • Contains a destructor to deallocate the memory used by the queue.
    • Implements isFull to check if the queue is full.
    • Implements isEmpty to check if the queue is empty.
    • Implements enqueue to add an element to the queue.
    • Implements dequeue to remove and return the front element from the queue.
    • Implements frontElement to return the front element without removing it.
    • Implements rearElement to return the rear element without removing it.
  2. Main Function:

    • Takes the capacity of the queue from the user.
    • Demonstrates basic queue operations (enqueue, dequeue, frontElement, rearElement).

An implementation of stack operations using an array in C++.


 

    # include <iostream>

    using namespace std;

    class Stack {

        private:

        int top;

        int maxSize;

        int* stackArray;

        public:

        Stack(int size) {

        maxSize = size;

        stackArray = new int[maxSize];

        top = -1;

        }

        ~Stack() {

        delete[] stackArray;

        }

        void push(int value) {

            if (isFull()) {

                cout << "Stack overflow! Cannot push " << value << endl;

            } else {

                stackArray[++top] = value;

            }

        }

    int pop() {

        if (isEmpty()) {

            cout << "Stack underflow! Cannot pop" << endl;

            return -1; // Assuming -1 as error value for stack underflow

        } else {

            return stackArray[top--];

        }

    }

    int peek() {

        if (isEmpty()) {

            cout << "Stack is empty! Cannot peek" << endl;

            return -1; // Assuming -1 as error value for empty stack

            } else {

            return stackArray[top];

            }

        }

        bool isEmpty() {

            return top == -1;

        }

        bool isFull() {

        return top == maxSize - 1;

        }

    };


    int main() {

        int size;

        cout << "Enter the size of the stack: ";

        cin >> size;

        Stack stack(size);

        stack.push(10);

        stack.push(20);

        stack.push(30);

        cout << "Top element is: " << stack.peek() << endl;

        cout << "Elements popped from stack: " << stack.pop() << ", " << stack.pop() << ", " <<  stack.pop() << endl;

    // Attempt to pop from empty stack

        stack.pop();

        return 0;

    }


  • 1. Stack Class:

    • Contains a constructor to initialise the stack with a given size.
    • Contains a destructor to deallocate the memory used by the stack.
    • Implements push to add an element to the stack.
    • Implements pop to remove and return the top element from the stack.
    • Implements peek to return the top element without removing it.
    • Implements isEmpty to check if the stack is empty.
    • Implements isFull to check if the stack is full.
  • 2. Main Function:

    • Takes the size of the stack from the user.
    • Demonstrates basic stack operations (push, pop, peek).
  • Tuesday, April 23, 2024

    Unlocking the Power of Friend Functions in C++: Accessing Private Members with Precision


    In C++, a friend function is a function that is not a member of a class but has access to the private and protected members of that class. This allows the function to operate on the class's private data as if it were a member of the class itself. Friend functions are declared inside a class but defined outside of it. They are useful for accessing or modifying private members of a class without violating encapsulation principles.

    Here's an example to illustrate the concept of friend functions:

    cpp
    #include <iostream> // Forward declaration of the class class MyClass; // Declaration of the friend function void display(const MyClass &obj); // Definition of the class class MyClass { private: int data; public: MyClass(int d) : data(d) {} // Declare display() as a friend function friend void display(const MyClass &obj); }; // Definition of the friend function void display(const MyClass &obj) { std::cout << "Data inside MyClass: " << obj.data << std::endl; } int main() { MyClass obj(10); display(obj); // Accessing private member 'data' using the friend function return 0; }

    In this example:

    • We have a class MyClass with a private member data.
    • We declare a friend function display inside the class MyClass using the friend keyword. This grants display access to the private members of MyClass.
    • The display function is defined outside the class, but it can access the private member data of MyClass.
    • In the main() function, we create an object obj of type MyClass with an initial value of 10.
    • We then call the display function, passing obj as an argument. Inside the display function, we access the private member data of obj and print its value.

    Output:

    cpp
    Data inside MyClass: 10

    This demonstrates how a friend function can access the private members of a class, allowing for greater flexibility in design while still maintaining encapsulation.


    1. Declaration and Definition:

    • Declaration: Friend functions are declared inside the class using the friend keyword, but they are not members of the class.
    • Definition: Friend functions are defined outside the class, just like regular functions.
    cpp
    class MyClass { private: int data; public: friend void display(const MyClass &obj); // Declaration of friend function }; // Definition of friend function void display(const MyClass &obj) { std::cout << "Data inside MyClass: " << obj.data << std::endl; }

    2. Accessing Private Members:

    • Friend functions have access to all private and protected members of the class they are declared as friends of. They can directly access these members without using member access specifiers like . or ->.

    3. Friendship is Not Symmetric:

    • Friendship is not mutual or symmetric. If class A declares class B as a friend, it doesn't imply that class B can access the private members of class A unless class B also declares class A as a friend.

    4. Granular Access Control:

    • Friendship can be granted to individual functions or entire classes. This allows for fine-grained control over which functions or classes can access the private members of a class.

    5. Pros and Cons:

    • Pros:
      • Allows non-member functions or other classes to access private members for specific purposes.
      • Enhances flexibility in design by allowing certain functions or classes special privileges.
    • Cons:
      • Can potentially break encapsulation if misused, leading to less maintainable code.
      • May increase complexity and reduce clarity if overused.

    Example with Multiple Classes:

    cpp
    class B; // Forward declaration of class B class A { private: int data_A; public: A(int d) : data_A(d) {} friend void showA(const A &objA, const B &objB); // Declaring showA as a friend function }; class B { private: int data_B; public: B(int d) : data_B(d) {} friend void showA(const A &objA, const B &objB); // Declaring showA as a friend function }; // Friend function defined outside both classes void showA(const A &objA, const B &objB) { std::cout << "Data inside A: " << objA.data_A << std::endl; std::cout << "Data inside B: " << objB.data_B << std::endl; } int main() { A objA(10); B objB(20); showA(objA, objB); // Accessing private members of both classes using the friend function return 0; }

    In this example, both classes A and B declare the function showA as a friend, allowing it to access their private members. This demonstrates how friend functions can be used across multiple classes to access private members for specific purposes.

    Understanding friend functions and using them judiciously can lead to more robust and flexible designs in C++. However, it's essential to consider the trade-offs and adhere to the principles of encapsulation and information hiding.