memcpy自定义对象带来的问题

引言

c++11前没有右值引用,我们怎么将一个包含资源的临时对象和“将亡值”转移或者交换到自己的变量中呢?我以前的做法是bitwise swap,即将两个对象的内存按位交换。这种做法一直工作得很好,直到最近一段代码在Linux上运行很OK换到windows上就崩溃,我才开始思考这种做法的安全性。

出事代码

公司代码有个线程调度框架,接口像极了WIN32程序窗口回调函数,通过一个整形MSGID来投递和区分消息处理

class TaskModule
{
public:
    virtual ~TaskModule() = default;
    virtual bool post_task(uint32_t msgid, uint64_t param, const void *data, size_t len) = 0;
};

其中,post_taskdatalen参数用于传递大块数据,TaskModule实现类内部创建一块连续内存的任务节点TaskNode,然后将data数据复制到TaskNode后面,代码大致如下:

struct TaskNode
{
     uint32_t msgid = 0;
     uint64_t param = 0;
     size_t len = 0;
     void *data() { return const_cast<void *>(this + 1); }
     const void *data() const { return const_cast<void *>(this + 1); }
};

class TaskModuleImpl : public TaskModule
{
public:
	bool post_task(uint32_t msgid, uint64_t param, const void *data, size_t len) 
	{
		TaskNode *task = reinterpret_cast<TaskNode *>(malloc(sizeof(TaskNode) + len)));
		assert(task);

		task->msgid = msgid;
		task->param = param;
		task->len = len;
		memcpy(task->data(), data, len);
		
		return post_task(task);
	}
private:
    bool post_task(TaskNode *task);
};

为了节省一次内存分配,我使用了replacement new在外面的临时内存上构建对象,然后将临时内存传给post_taskdata,在task执行函数中调用构建对象的析构函数。

template<typename T>
bool post_task(TaskModule *tm, uint32_t msgid, T&& obj)
{
	char buf[sizeof(T)];
	return tm->post_task(msgid, 0, new (buf) T(std::forward<T>(obj)), sizeof(obj));
}

void do_task(uint32_t msgid, void *data, size_t len)
{
	auto task = reinterpret_cast<MyTask *>(data);
	// task->xxxx
	task->~MyTask();
}

这种方式一直工作的很好,但在VC++上一旦MyTask使用了std::stringstd::function,程序就会崩溃,do_task里的程序内存已经不可用。

查找原因

这当然和vc++ std::stringstd::function的实现有关。研究源码之后,发现是std::stringstd::function小内存优化或者SSO使对象的内部指针成员指向了一块内部内存,memcpy以后的对象指针还是指向原对象内存,原对象为栈上变量,离开作用域之后内存不再可用,使用新对象必然就崩溃了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值