繁体   English   中英

保证 C++ 中的临时生命周期?

[英]Guaranteed lifetime of temporary in C++?

C++ 是否为在 function 调用中创建但未用作参数的临时变量的生命周期提供保证? 这是一个示例 class:

class StringBuffer
{
public:
    StringBuffer(std::string & str) : m_str(str)
    {
        m_buffer.push_back(0);
    }
    ~StringBuffer()
    {
        m_str = &m_buffer[0];
    }
    char * Size(int maxlength)
    {
        m_buffer.resize(maxlength + 1, 0);
        return &m_buffer[0];
    }
private:
    std::string & m_str;
    std::vector<char> m_buffer;
};

以下是您将如何使用它:

// this is from a crusty old API that can't be changed
void GetString(char * str, int maxlength);

std::string mystring;
GetString(StringBuffer(mystring).Size(MAXLEN), MAXLEN);

什么时候调用临时 StringBuffer object 的析构函数? 是吗:

  • 在调用 GetString 之前?
  • GetString 返回后?
  • 依赖编译器?

我知道 C++ 保证局部临时变量只要有对它的引用就有效——当有对成员变量的引用时,这是否适用于父对象?

谢谢。

这种临时对象的析构函数在完整表达式的末尾被调用。 这是最外层的表达式,不属于任何其他表达式。 这是在 function 返回并评估值之后的情况。 所以,一切都会很好。

这实际上是使表达式模板起作用的原因:它们可以在表达式中保留对此类临时对象的引用,例如

e = a + b * c / d

因为每个临时的都会持续到表达式

x = y

被完全评估。 它在标准中的12.2 Temporary objects中进行了非常简洁的描述。

litb的答案是准确的。 临时 object(也称为右值)的生命周期与表达式相关联,临时 object 的析构函数在完整表达式的末尾被调用,当调用 StringBuffer 上的析构函数时,m_buffer 上的析构函数也将是调用,但不是 m_str 上的析构函数,因为它是一个引用。

请注意,C++0x 只是稍微改变了一些东西,因为它添加了右值引用和移动语义。 本质上,通过使用右值引用参数(用 && 表示),我可以将右值“移动”到 function(而不是复制它)中,并且右值的生命周期可以绑定到它移动到的 object,而不是表达式。 MSVC 团队有一篇非常好的博客文章详细介绍了这一点,我鼓励人们阅读它。

移动右值的教学示例是临时字符串,我将在构造函数中展示赋值。 如果我有一个包含字符串成员变量的 class MyType,则可以在构造函数中使用右值对其进行初始化,如下所示:

class MyType{
   const std::string m_name;
public:
   MyType(const std::string&& name):m_name(name){};
}

这很好,因为当我使用临时 object 声明此 class 的实例时:

void foo(){
    MyType instance("hello");
}

发生的情况是我们避免复制和破坏临时 object 并且“hello”直接放置在拥有 class 实例的成员变量中。 如果 object 比“字符串”重,那么额外的复制和析构函数调用可能很重要。

调用 GetString 后返回。

我写了几乎完全相同的 class:

template <class C>
class _StringBuffer
{
    typename std::basic_string<C> &m_str;
    typename std::vector<C> m_buffer;

public:
    _StringBuffer(std::basic_string<C> &str, size_t nSize)
        : m_str(str), m_buffer(nSize + 1) { get()[nSize] = (C)0; }

    ~_StringBuffer()
        { commit(); }

    C *get()
        { return &(m_buffer[0]); }

    operator C *()
        { return get(); }

    void commit()
    {
        if (m_buffer.size() != 0)
        {
            size_t l = std::char_traits<C>::length(get());
            m_str.assign(get(), l);    
            m_buffer.resize(0);
        }
    }

    void abort()
        { m_buffer.resize(0); }
};

template <class C>
inline _StringBuffer<C> StringBuffer(typename std::basic_string<C> &str, size_t nSize)
    { return _StringBuffer<C>(str, nSize); }

在标准之前,每个编译器的做法都不同。 我相信 C++ 的旧注释参考手册指定临时文件应该在 scope 的末尾进行清理,所以一些编译器这样做了。 直到 2003 年,我发现 Sun 的 Forte C++ 编译器的默认行为仍然存在,因此 StringBuffer 不起作用。 但是,如果任何当前的编译器仍然那么损坏,我会感到惊讶。

StringBuffer 在 GetString 的 scope 中。 它应该在 GetString 的 scope 结束时被销毁(即当它返回时)。 另外,我不相信 C++ 会保证只要有引用,变量就会存在。

下面应该编译:

Object* obj = new Object;
Object& ref = &(*obj);
delete obj;

来自cppreference

所有临时对象都在评估包含它们创建点的完整表达式的最后一步被销毁,如果创建了多个临时对象,它们将按照与创建顺序相反的顺序销毁。 即使评估以抛出异常结束也是如此。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM