为堆栈和堆指针重新分配内存
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 问题的大门,因为堆栈对象在从队列中移除之前可能会被销毁)
我开发了一个阻塞队列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 问题的大门,因为堆栈对象在从队列中移除之前可能会被销毁)