C ++中临时的保证寿命?

时间:2009-02-25 05:37:00

标签: c++

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

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对象的析构函数?是吗:

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

我知道C ++保证本地临时变量只要有引用就有效 - 当引用成员变量时这是否适用于父对象?

感谢。

5 个答案:

答案 0 :(得分:103)

在完整表达式结束时调用那种临时数的析构函数。这是最外层的表达,不是任何其他表达的一部分。在您的情况下,在函数返回并评估值之后。所以,它会很好用。

事实上,表达模板的作用是什么:它们可以在表达式中保持对那种临时表的引用,例如

e = a + b * c / d

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

x = y

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

答案 1 :(得分:16)

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

请注意,C ++ 0x稍微改变了一些东西,因为它添加了rvalue引用并移动了语义。基本上通过使用rvalue引用参数(用&amp;&amp;标记)我可以将rvalue“移动”到函数中(而不是复制它),并且rvalue的生命周期可以绑定到它移动到的对象,而不是表达式。有一个非常好的blog post from the MSVC team on that walks through this in great detail,我鼓励人们阅读它。

移动rvalue的教学示例是临时字符串,我将在构造函数中显示赋值。如果我有一个包含字符串成员变量的类MyType,可以在构造函数中使用rvalue初始化它,如下所示:

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

这很好,因为当我使用临时对象声明此类的实例时:

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

发生的事情是我们避免复制和销毁临时对象,并且“hello”直接放在拥有类实例的成员变量中。如果对象的重量比“字符串”重,则额外的复制和析构函数调用可能很重要。

答案 2 :(得分:3)

调用GetString后返回。

答案 3 :(得分:3)

我写的几乎完全是同一个类:

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 ++参考手册指出临时应该在范围的最后清理,所以一些编译器就这样做了。直到2003年,我发现默认情况下Sun的Forte C ++编译器仍然存在这种行为,因此StringBuffer不起作用。但如果任何当前的编译器仍然被破坏,我会感到惊讶。

答案 4 :(得分:2)

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

以下内容应编译:

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