2014-07-22 72 views
1

当我有一个类,我试图返回它的实例调用,但调用析构函数之前,我可以恢复它,这和当它确实掉出的范围在后面的功能是析构函数被再次调用并导致中断。我编辑它来修复错误,但我想知道是否应该调用它的析构函数。析构函数返回值

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

理论上返回使得副本返回,所以原理在理论上被破坏。给定一个正确定义的类,这不是一个问题。 – chris

+2

除非[RVO](http://en.wikipedia.org/wiki/Return_value_optimization)进来,它应该在复制/移动后调用析构函数。 –

+0

为什么不展示CBuffer类的基础知识?不是每个函数,而是这里使用的构造函数,复制构造函数,析构函数和关键数据成员?您可能会得到有关如何改进复制构造函数以实现此功能的有用建议。 – NicholasM

回答

3

您正在返回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代替,这是更好,因为它处理这些细节给你。

+0

当我用std :: move返回它时,析构函数仍然被调用,编辑显示std :: move代码,根据我的理解转换成r值并且不应该执行复制。 –

+0

@JoshuaWaring:发生移动时,它只是一种优化的复制方式。它不会改变是否调用析构函数。 –

+0

CBuffer只包含一个int大小和一个void *数据,如果移动被调用,那么数据会发生什么,它会变成一个nullptr?因为CBuffers析构函数试图删除数据。 –

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

那么大多数编译器应该能够执行“返回值优化”;也就是说,他们会悄悄假装你做了等同于:

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,然后在被调用者内部初始化它。这节省了在回报中执行副本。