返回值时调用析构函数

时间:2014-07-22 03:44:13

标签: c++

我有一个类,我正在尝试返回它的实例,但是在我返回它之前调用了析构函数,当它在以后的函数中确实超出范围时,它的析构函数再次被调用导致休息。我编辑它来修复错误,但我想知道它是否应该调用它的析构函数。

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);
}

2 个答案:

答案 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然后就地在被调用者中初始化它。这样可以在回报中保存执行副本。