我有一个类,我正在尝试返回它的实例,但是在我返回它之前调用了析构函数,当它在以后的函数中确实超出范围时,它的析构函数再次被调用导致休息。我编辑它来修复错误,但我想知道它是否应该调用它的析构函数。
CBuffer BufferReader::read(const int size){
const auto raw = read_raw();
skip(size);
return CBuffer(raw, size, true);
// Dstructor is called in this example
CBuffer out(read_raw(), size, true);
skip(size);
return std::move(out);
}
答案 0 :(得分:4)
您将返回一份CBuffer。函数内部创建的原始CBuffer在函数退出时被破坏 - 至少在逻辑上是这样。通常,编译器会将副本作为优化删除,除非禁用该优化(例如,通过进行调试构建)。
使用std::move
并没有真正改变这一点。通过从原始CBuffer
中窃取内容,可以更有效地完成创建的副本。原始CBuffer
仍然需要销毁,它可能不包含任何实际数据。移动构造函数总是需要将原始对象保持在有效状态,因此可以安全地销毁它。
例如:
struct CBuffer {
char* data;
CBuffer(CBuffer&& original)
: data(original.data)
{
original.data = nullptr;
}
~CBuffer() { delete [] data; } // data may be null, but that is fine
// delete will not do anything in that case.
};
如果您可以使用std::vector
,那就更好了,因为它会为您处理这些细节。
答案 1 :(得分:0)
std::move
template<class T>
T&& std::move(T&& arg)
{
return static_cast<T&&>(arg);
}
(它有点复杂,但我的目标是易读性)
出口处实际发生的事情是:
CBuffer rv(std::move(out));
return rv;
这是调用CBuffer的移动构造函数。
很多人陷入了思考std :: move实际执行移动的陷阱,恕我直言,它的名字很差。
如果以这种方式编写代码:
CBuffer BufferReader::read(const int size)
{
CBuffer out(read_raw(), size, true);
skip(size);
return out;
}
然后大多数编译器应该能够执行返回值优化&#39 ;;也就是说,他们会悄悄地假装你做了类似的事情:
void BufferReader::read(const int size, CBuffer* out)
{
new (out) CBuffer(read_raw(), size, true); //in-place new
skip(size);
}
并在您拥有的呼叫网站
CBuffer x = reader.read(size);
他们假装你正在写
CBuffer* x = (CBuffer*)alloca(sizeof(CBuffer));
reader.read(size, &x);
再次,稍微简化一下,上面的代码是在堆栈上分配一个CBuffer然后就地在被调用者中初始化它。这样可以在回报中保存执行副本。