[c++] 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 ++는 로컬 임시 변수에 대한 참조가있는 한 유효하다는 것을 보장한다는 것을 알고 있습니다. 멤버 변수에 대한 참조가있을 때 부모 개체에 적용됩니까?

감사.



답변

그런 종류의 임시에 대한 소멸자는 full-expression의 끝에서 호출됩니다. 그것은 다른 표현의 일부가 아닌 가장 외적인 표현입니다. 그것은 함수가 반환되고 값이 평가 된 후의 경우입니다. 따라서 모두 잘 작동합니다.

사실 식 템플릿이 작동하게 만드는 이유는 다음과 같습니다.

e = a + b * c / d

모든 일시적인 것이 표현 될 때까지 지속되기 때문에

x = y

완전히 평가됩니다. 12.2 Temporary objects표준 에 아주 간결하게 설명되어 있습니다.


답변

litb의 대답은 정확합니다. 임시 객체 (rvalue라고도 함)의 수명은 표현식에 연결되어 있으며 임시 객체의 소멸자는 전체 표현식의 끝에서 호출되며 StringBuffer의 소멸자가 호출 될 때 m_buffer의 소멸자는 다음과 같습니다. 호출되었지만 참조이기 때문에 m_str의 소멸자는 아닙니다.

C ++ 0x는 rvalue 참조를 추가하고 의미 체계를 이동하기 때문에 약간만 변경됩니다. 본질적으로 rvalue 참조 매개 변수 (&&로 표시됨)를 사용하여 rvalue를 함수로 ‘이동’할 수 있으며 (복사하는 대신) rvalue의 수명은 표현식이 아닌 이동하는 객체에 바인딩 될 수 있습니다. 이에 대해 자세히 설명하는 MSVC 팀 의 정말 좋은 블로그 게시물이 있습니다.

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”가 소유하는 클래스 인스턴스의 멤버 변수 안에 직접 배치된다는 것입니다. 객체가 ‘문자열’보다 무거운 경우 추가 복사 및 소멸자 호출이 중요 할 수 있습니다.


답변

GetString에 대한 호출이 반환 된 후.


답변

StringBuffer는 GetString의 범위에 있습니다. GetString의 범위가 끝날 때 (즉, 반환 될 때) 파괴되어야합니다. 또한 C ++가 참조가있는 한 변수가 존재한다는 것을 보장하지 않는다고 생각합니다.

다음을 컴파일해야합니다.

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


답변

나는 거의 똑같은 수업을 썼습니다.

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 ++에 대한 이전 Annotated Reference Manual이 범위의 끝에서 임시 파일을 정리해야한다고 지정했다고 생각하므로 일부 컴파일러는 그렇게했습니다. 2003 년 말에 필자는 Sun의 Forte C ++ 컴파일러에서 기본적으로 동작이 여전히 존재한다는 사실을 발견했기 때문에 StringBuffer가 작동하지 않았습니다. 그러나 현재의 컴파일러가 여전히 그렇게 망가 졌다면 놀랄 것입니다.


답변