Перераспределить память для стека и указателя кучи - PullRequest
0 голосов
/ 07 июня 2019

Я разработал класс очереди блокировки следующим образом

class Blocking_queue
{
public:
    Blocking_queue();

    int put(void* elem, size_t elem_size);
    int take(void* event);

    unsigned int get_size();

private:

    typedef struct element
    {
        void* elem;
        size_t elem_size;
        struct element* next;
    }element_t;

    std::mutex m_lock;
    std::condition_variable m_condition;
    unsigned int m_size;
    element_t* m_head;
    element_t* m_tail;

};

Я хочу, чтобы класс был как можно более универсальным, поэтому я использую указатель void, который выделяется при добавлении элемента в очередь и освобождается при удалении из него.

int Blocking_queue::take(void* event)
{
    element_t* new_head = NULL;
    int ret = 0;

    // Queue empty
    if(nullptr == m_head)
    {
        // Wait for an element to be added to the queue
        std::unique_lock<std::mutex> unique_lock(m_lock);
        m_condition.wait(unique_lock);
    }

    if(nullptr == realloc(event, m_head->elem_size))
    {
        ret = -1;
    }
    else
    {
        // Take element from queue
        memcpy(event, m_head->elem, m_head->elem_size);
        ret = m_head->elem_size;
        new_head = m_head->next;
        free(m_head->elem);
        free(m_head);
        m_head = new_head;
        if(nullptr == m_head)
        {
            m_tail = nullptr;
        }
        m_size -= 1;
    }
    return ret;
}

Если очередь пуста, функция take() ожидает m_condition, пока не будет добавлен новый элемент.

Необходимо указатель event для копирования содержимого элемента перед его освобождением.

Чтобы быть уверенным, что данный указатель имеет правильный размер для копирования содержимого элемента, я перераспределяю указатель с его размером.

Проблема, с которой я столкнулся, заключается в том, что она не позволяет передавать переменную локали функции, потому что она расположена в стеке.

Так что, если я сделаю что-то подобное

void function()
{
    unsigned int event = 0;

    queue->take(&event);
}

У меня будет ошибка invalid old size на realloc.

Так что, если я передам нулевой указатель или переменную, выделенную из кучи, это будет работать, но если я передам адрес переменной стека, это не будет.

Есть ли способ разрешить передачу адреса переменной стека в функцию take()?

1 Ответ

7 голосов
/ 08 июня 2019

Есть ли способ разрешить передачу адреса переменной стека в take () функция?

Короткий ответ - нет. malloc() / free() / realloc() может работать только с выделенной памятью кучи; они не будут работать с выделенной стеком памятью.

Что касается того, как вы могли бы обойти эту проблему, я думаю, что она потребует некоторого редизайна. Мое первое предложение - бежать как можно дальше от (void *) - указатели void крайне небезопасны и их трудно использовать правильно, потому что компилятор ничего не знает о том, на что они указывают, и поэтому не может генерировать ошибки, когда программист что-то делает неправильно; это приводит к множеству проблем во время выполнения. Это скорее конструкция языка C, все еще поддерживаемая в C ++ для обеспечения совместимости с C, но у C ++ есть лучшие и более безопасные способы сделать то же самое.

В частности, если ожидается, что все элементы данных вашей очереди должны быть одного типа, то очевидная вещь, которую нужно сделать, - это сделать ваш класс Blocking_queue шаблонным с этим типом в качестве аргумента шаблона; тогда пользователь может указать, например, Blocking_queue<MyFavoriteDataType> и используйте любой тип, который ему нравится, и предоставьте простую в использовании семантику по значению (аналогичную, например, предоставляемую, например, std::vector и друзьями)

Если вы хотите разрешить смешивание элементов данных разных типов, то лучше всего повторить вышеизложенное, но определить общий базовый класс для объектов, а затем создать экземпляр объекта Blocking_queue<std::shared_ptr<TheCommonBaseClass> >, который будет принимать разделяемые указатели на любой выделенный в куче объект любого подкласса этого базового класса. (Если вам действительно нужно передать разделяемые указатели объектам, выделенным в стеке, вы можете сделать это, определив пользовательский распределитель для разделяемого указателя, но учтите, что это открывает дверь к проблемам несоответствия объекта времени жизни, поскольку объекты стека могут быть уничтожены до того, как они будут удалены из очереди)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...