I am new to programming and currently trying to learn data structures. I am currently trying to implement an array of queues of type array, however when deallocating memory using the delete [] queues statement, the program runs into a Heap corruption error. The issue seem to deal with the inner for loop + while loop, but I'm a bit confused. I have looked at other existing questions, which point me to a memory allocation problem. However, when I fill the queue array up to size N/2 I still run into the Heap corruption error. What is triggering this issue?
void queue_client(){
int M = 2;
int N = 10;
//create an array of queues
Queue_array<int>* queues = new Queue_array<int>[M];
//initialize each queue in the array of queues
for (int i = 0; i < N; i++) {
    queues[i].set_array(N);
}
for (int i = 0; i < N; i++){
    //randonly pick number to select and put in queue
    int in = rand() % M, out = rand() % M;
    queues[in].put(i);
    std::cout << i << " in" <<std::endl;
    //if current queues is not empty pop element out
    if (!queues[out].empty()) {
        std::cout << queues[out].get() << " out" << std::endl;
    }
    for (int k = 0; k < M; k++) {
        //Queue_array<int> q = queues[k];
        std::cout << k << ": ";
        while (!queues[k].empty()) {
            std::cout << queues[k].get() << " ";
        }
    }
}
//deallocate memory from array queue
delete[] queues;
}
Attached is the queue array class. The issue seems to be with my queue_array class, possibly the destructor?
#include<iostream>
template <class Item>
class Queue_array
{
private:
    Item* q;
    int N, head, tail;
    void deleteList() {
            delete [] q;
            std::cout << "destructor" << std::endl;
    }
public:
    Queue_array(int maxN);
    Queue_array();
    ~Queue_array();
    Queue_array& operator=(Queue_array& rhs);
    int empty();
    void put(Item item);
    Item get();
    void print_elements();
    int error(int);
    bool search_element(Item);
    int get_size();
    void set_array(int);
};
template <class Item>
Queue_array<Item>::Queue_array(int maxN) {
    q = new Item[maxN+1];
    N = maxN+1;
    head = N;
    tail = 0;
}
template <class Item>
Queue_array<Item>::Queue_array() {
    q = 0;
    N = 0;
    head = N;
    tail = 0;
}
template <class Item>
Queue_array<Item>::~Queue_array() {
    std::cout << "destructor invoked" << std::endl;
    deleteList();
}
template <class Item>
Queue_array<Item>& Queue_array<Item>::operator=(Queue_array& rhs) {
    //if both are the same queues return the queue
    if (this == &rhs) {
        return *this;
    }
    //clear all elements in current queue
    deleteList();
    
    this->set_array(rhs.get_size());
    //pop all elements from rhs and put into current queue 
    while (!rhs.empty()) {
        put(rhs.get());
    }
    return *this;
}
template<class Item>
void Queue_array<Item>::set_array(int val) {
    N = val+1;
    q = new Item[val+1];
    head = N;
    tail = 0;
}
template <class Item>
int Queue_array<Item>::empty() {
    return (head % N == tail);
}
template<class Item>
void Queue_array<Item>::put(Item item) {
    if (!error(2))
    {
    q[tail++] = item;
    tail = tail % N;
    }
}
template<class Item>
Item Queue_array<Item>::get() {
    if (!error(1))
    {
    head = head % N;
    return q[head++];
    }
}
template<class Item>
void Queue_array<Item>::print_elements() {
    std::cout <<"-----------------------" <<std::endl;
    if (tail > head) {
        //std::cout << "Head < Tail ";
        for (int i = head; i < tail; i++) {
            std::cout  << q[i] << " ";
        }
        std::cout << std::endl;
    }
    else {
        //std::cout << "Tail < Head ";
        int i;
        
        //if tail is less than head, that means array indexes has wrapped around.
        //break print loops into two halves, from head to N and 0 to tails
        for (i = head; i < N; i++) {
            std::cout  << q[i] << " ";
        }
        
        for (i = 0; i < tail; i++) {
            std::cout << q[i] << " ";
        }
        std::cout << std::endl;
    }
    std::cout << "-----------------------" <<std::endl;
}
template<class Item>
int Queue_array<Item>::error(int mode) {
    
    //if all elements are popped and head = tail
    if (empty() && (mode==1)) {
        //if first element is detected return 0
        if (head == N) {
            return 0;
        }
        else {
            std::cout << "Error, queue is empty." << std::endl;
            return 1;
        }
    }
    //if tail wraps back to head (tail leads head, queue is full)
    else if ((((tail%N) +1) == head) && (mode ==2)) {
        //if tail is at last node, don't insert node
        std::cout << "Error, queue is full." << std::endl;
        return 1;
    }
    else {
        return 0;
    }
}
template<class Item>
bool Queue_array<Item>::search_element(Item x) {
    int start = head;
    if (start == N)
        start = 0;
    for (int i = start; i <= tail; i++) {
        if (q[i] == x) {
            return 1;
        }
    }
    return 0;
}
template<class Item>
int Queue_array<Item>::get_size() {
    return N;
}
I made tested the queue array and the destructor is being invoked correctly. The test client simply puts N int values in each of the M Queues.
void test_4_71() {
    //test if destructor is working.
    int M = 2;
    int N = 10;
    //create an array of queues
    Queue_array<int>* queues = new Queue_array<int>[M];
    //initialize each queue in the array of queues
    for (int i = 0; i < M; i++) {
        queues[i].set_array(N);
    }
    //push 0,1,2,...10 to arrays 1 and 2
    for (int i = 0; i < M; i++) {
        for (int j = 0; j < N; j++)
            queues[i].put(j);
        std::cout << i << " in" << std::endl;
    }
    //pop all elements from arrays 1 and 2
    for (int i = 0; i < M; i++) {
        while (!queues[i].empty()) {
            std::cout << queues[i].get() << " ";
        }
        std::cout << std::endl;
    }
    //deallocate memory from array queue
    delete[]queues;
    //std::cout << queues[0].get() << "test get" << std::endl;
}
