为堆栈和堆指针重新分配内存

Reallocate memory for both stack and heap pointer

我开发了一个阻塞队列class如下

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;

};

我希望 class 尽可能通用,因此我使用了一个 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 错误。

因此,如果我传递一个空指针或堆分配的变量,它会起作用,但如果我传递一个堆栈变量地址,它就不会。

有没有办法允许将堆栈变量地址传递给 take() 函数?

Is there a way to allow stack variable address to be passed to take() function ?

简短的回答是否定的。 malloc()/free()/realloc() 只能使用 heap-allocated 内存;他们将无法使用 stack-allocated 内存。

至于如何解决这个问题,我认为需要重新设计。我的第一个建议是 运行 尽可能远离 (void *) —— void-pointers 极不安全且难以正确使用,因为编译器对它们指向的内容一无所知,因此当程序员做错事情时不会产生错误;这会导致很多 运行 时间问题。它们更像是一个 C-language 结构,在 C++ 中仍然支持以提供 C 兼容性,但 C++ 有更好、更安全的方法来做同样的事情。

特别是,如果您队列中的所有 data-elements 都应该是同一类型,那么显而易见的事情就是让您的 Blocking_queue class 模板化将该类型作为 template-argument;然后用户可以指定例如Blocking_queue<MyFavoriteDataType> 并使用他喜欢的任何类型,并提供 easy-to-use by-value 语义(类似于 std::vector 和朋友提供的语义)

如果你想允许混合data-elements不同的类型,那么最好的做法还是上面的,但是为对象定义一个公共的base-class,然后你就可以实例化了一个 Blocking_queue<std::shared_ptr<TheCommonBaseClass> > 对象,它将接受 shared-pointers 到该基础 class 的任何子 class 的任何 heap-allocated 对象。 (如果你真的需要将 shared-pointers 传递给 stack-allocated 对象,你可以通过为共享指针定义一个自定义分配器来做到这一点,但请注意,这样做会打开通往 object-lifetime-mismatch 问题的大门,因为堆栈对象在从队列中移除之前可能会被销毁)