简体   繁体   English

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

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

Does C++ provide a guarantee for the lifetime of a temporary variable that is created within a function call but not used as a parameter? C++ 是否为在 function 调用中创建但未用作参数的临时变量的生命周期提供保证? Here's an example class:这是一个示例 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;
};

And here's how you would use it:以下是您将如何使用它:

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

When will the destructor for the temporary StringBuffer object get called?什么时候调用临时 StringBuffer object 的析构函数? Is it:是吗:

  • Before the call to GetString?在调用 GetString 之前?
  • After GetString returns? GetString 返回后?
  • Compiler dependent?依赖编译器?

I know that C++ guarantees that a local temporary variable will be valid as long as there's a reference to it - does this apply to parent objects when there's a reference to a member variable?我知道 C++ 保证局部临时变量只要有对它的引用就有效——当有对成员变量的引用时,这是否适用于父对象?

Thanks.谢谢。

The destructor for that sort of temporaries is called at the end of the full-expression.这种临时对象的析构函数在完整表达式的末尾被调用。 That's the most outer expression which is not part of any other expression.这是最外层的表达式,不属于任何其他表达式。 That is in your case after the function returns and the value is evaluated.这是在 function 返回并评估值之后的情况。 So, it will work all nice.所以,一切都会很好。

It's in fact what makes expression templates work: They can keep hold references to that sort of temporaries in an expression like这实际上是使表达式模板起作用的原因:它们可以在表达式中保留对此类临时对象的引用,例如

e = a + b * c / d

Because every temporary will last until the expression因为每个临时的都会持续到表达式

x = y

Is evaluated completely.被完全评估。 It's quite concisely described in 12.2 Temporary objects in the Standard.它在标准中的12.2 Temporary objects中进行了非常简洁的描述。

litb's answer is accurate. litb的答案是准确的。 The lifetime of the temporary object (also known as an rvalue) is tied to the expression and the destructor for the temporary object is called at the end of the full expression and when the destructor on StringBuffer is called, the destructor on m_buffer will also be called, but not the destructor on m_str since it is a reference.临时 object(也称为右值)的生命周期与表达式相关联,临时 object 的析构函数在完整表达式的末尾被调用,当调用 StringBuffer 上的析构函数时,m_buffer 上的析构函数也将是调用,但不是 m_str 上的析构函数,因为它是一个引用。

Note that C++0x changes things just a little bit because it adds rvalue references and move semantics.请注意,C++0x 只是稍微改变了一些东西,因为它添加了右值引用和移动语义。 Essentially by using an rvalue reference parameter (notated with &&) I can 'move' the rvalue into the function (instead of copying it) and the lifetime of the rvalue can be bound to the object it moves into, not the expression.本质上,通过使用右值引用参数(用 && 表示),我可以将右值“移动”到 function(而不是复制它)中,并且右值的生命周期可以绑定到它移动到的 object,而不是表达式。 There is a really good blog post from the MSVC team on that walks through this in great detail and I encourage folks to read it. MSVC 团队有一篇非常好的博客文章详细介绍了这一点,我鼓励人们阅读它。

The pedagogical example for moving rvalue's is temporary strings and I'll show assignment in a constructor.移动右值的教学示例是临时字符串,我将在构造函数中展示赋值。 If I have a class MyType that contains a string member variable, it can be initialized with an rvalue in the constructor like so:如果我有一个包含字符串成员变量的 class MyType,则可以在构造函数中使用右值对其进行初始化,如下所示:

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

This is nice because when I declare an instance of this class with a temporary object:这很好,因为当我使用临时 object 声明此 class 的实例时:

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

what happens is that we avoid copying and destroying the temporary object and "hello" is placed directly inside the owning class instance's member variable.发生的情况是我们避免复制和破坏临时 object 并且“hello”直接放置在拥有 class 实例的成员变量中。 If the object is heavier weight than a 'string' then the extra copy and destructor call can be significant.如果 object 比“字符串”重,那么额外的复制和析构函数调用可能很重要。

After the call to GetString returns.调用 GetString 后返回。

I wrote almost exactly the same class:我写了几乎完全相同的 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); }

Prior to the standard each compiler did it differently.在标准之前,每个编译器的做法都不同。 I believe the old Annotated Reference Manual for C++ specified that temporaries should clean up at the end of the scope, so some compilers did that.我相信 C++ 的旧注释参考手册指定临时文件应该在 scope 的末尾进行清理,所以一些编译器这样做了。 As late as 2003, I found that behaviour still existed by default on Sun's Forte C++ compiler, so StringBuffer didn't work.直到 2003 年,我发现 Sun 的 Forte C++ 编译器的默认行为仍然存在,因此 StringBuffer 不起作用。 But I'd be astonished if any current compiler was still that broken.但是,如果任何当前的编译器仍然那么损坏,我会感到惊讶。

StringBuffer is in the scope of GetString. StringBuffer 在 GetString 的 scope 中。 It should get destroyed at the end of GetString's scope (ie when it returns).它应该在 GetString 的 scope 结束时被销毁(即当它返回时)。 Also, I don't believe that C++ will guarantees that a variable will exist as long as there is reference.另外,我不相信 C++ 会保证只要有引用,变量就会存在。

The following ought to compile:下面应该编译:

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

From cppreference :来自cppreference

All temporary objects are destroyed as the last step in evaluating the full-expression that (lexically) contains the point where they were created , and if multiple temporary objects were created, they are destroyed in the order opposite to the order of creation.所有临时对象都在评估包含它们创建点的完整表达式的最后一步被销毁,如果创建了多个临时对象,它们将按照与创建顺序相反的顺序销毁。 This is true even if that evaluation ends in throwing an exception.即使评估以抛出异常结束也是如此。

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

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