簡體   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