C ++ 지역 변수에 대한 참조 반환


117

i를 반환해야하는 경우 다음 코드 (func1 ())가 정확합니까? 지역 변수에 대한 참조를 반환 할 때 문제가 있다는 것을 읽은 것을 기억합니다. func2 ()와 어떻게 다릅니 까?

int& func1()
{
    int i;
    i = 1;
    return i;
}

int* func2()
{
    int* p;
    p = new int;
    *p = 1;
    return p;
}

1
당신이에서 func1을 변경하면 ()가 :-) 동일 동적으로 할당 된 메모리를 사용하는int& i = * new int;
마틴 뉴욕

답변:


193

이 코드 조각 :

int& func1()
{
    int i;
    i = 1;
    return i;
}

수명이 함수 호출 범위로 제한된 객체에 대한 별칭 (참조)을 반환하므로 작동하지 않습니다. 즉 func1(), int i이제는 존재하지 않는 객체를 참조하기 때문에 함수에서 반환 된 참조를 쓸모 없게 만듭니다.

int main()
{
    int& p = func1();
    /* p is garbage */
}

두 번째 버전은 변수가 함수 호출의 수명에 제한되지 않는 무료 저장소에 할당되기 때문에 작동합니다. 그러나 delete할당 된 int.

int* func2()
{
    int* p;
    p = new int;
    *p = 1;
    return p;
}

int main()
{
    int* p = func2();
    /* pointee still exists */
    delete p; // get rid of it
}

일반적으로 포인터를 일부 RAII 클래스 및 / 또는 팩토리 함수로 래핑하여 delete직접 수행 할 필요가 없습니다 .

두 경우 모두 값 자체를 반환 할 수 있습니다 (제공 한 예제가 아마도 인위적 이었음을 알고 있습니다).

int func3()
{
    return 1;
}

int main()
{
    int v = func3();
    // do whatever you want with the returned value
}

func3()오늘날 거의 모든 컴파일러가 어떤 형태의 반환 값 최적화를 구현하기 때문에 동일한 방식 으로 원시 값을 반환 하는 방식으로 큰 객체를 반환하는 것이 좋습니다 .

class big_object 
{ 
public:
    big_object(/* constructor arguments */);
    ~big_object();
    big_object(const big_object& rhs);
    big_object& operator=(const big_object& rhs);
    /* public methods */
private:
    /* data members */
};

big_object func4()
{
    return big_object(/* constructor arguments */);
}

int main()
{
     // no copy is actually made, if your compiler supports RVO
    big_object o = func4();    
}

흥미롭게도 임시 참조를 const 참조에 바인딩하는 것은 완벽하게 합법적 인 C ++ 입니다.

int main()
{
    // This works! The returned temporary will last as long as the reference exists
    const big_object& o = func4();    
    // This does *not* work! It's not legal C++ because reference is not const.
    // big_object& o = func4();  
}

2
아름다운 설명. : hattip : 세 번째 코드 조각에서 int* p = func2(); delete p;지금 삭제하고 있습니다. 'p'를 삭제하면 함수 func2()정의 "내부"에 할당 된 메모리 도 삭제 되었음을 의미 합니까?
Aquarius_Girl

2
@Anisha Kaul : 네. 메모리는 내부에 할당 func2()되고 다음 줄에서 외부에서 해제되었습니다. 하지만 RAII의 일부 변형을 대신 사용하겠다고 말한 것처럼 메모리를 처리하는 다소 오류가 발생하기 쉬운 방법입니다. 그건 그렇고, 당신은 C ++를 배우는 것처럼 들립니다. 배울 수 있는 좋은 입문 C ++ 책 을 선택하는 것이 좋습니다 . 또한 질문이있는 경우 나중에 참조 할 수 있도록 언제든지 Stack Overflow에 질문을 게시 할 수 있습니다. 댓글은 완전히 새로운 질문을하기위한 것이 아닙니다.
In silico 2011-08-27

이제 이해했습니다. 제대로하셨습니다! 함수가 포인터를 반환하고 있으며 해당 함수 외부에서 가리키는 메모리를 삭제했습니다. 이제 명확하고 링크에 감사드립니다.
Aquarius_Girl

그리고 당신은 대답을 편집 했습니까 ?? : mad : 나는 그것을 쉽게 놓칠 수 있었다. ;);)
Aquarius_Girl

@Anisha Kaul : 아니요. 내 답변을 마지막으로 수정 한 것은 내 게시물 아래의 타임 스탬프에 따라 1 월 10 일이었습니다.
In silico

18

로컬 변수는 스택의 메모리이며, 해당 메모리는 범위를 벗어날 때 자동으로 무효화되지 않습니다. 중첩 된 함수 (메모리의 스택에서 더 높음)에서이 메모리에 액세스하는 것이 완벽하게 안전합니다.

함수가 반환되고 종료되면 상황이 위험 해집니다. 일반적으로 돌아올 때 메모리는 삭제되거나 덮어 쓰여지지 않습니다. 이는 해당 주소의 메모리에 여전히 데이터가 포함되어 있음을 의미합니다. 포인터가 유효한 것처럼 보입니다.

다른 함수가 스택을 만들고 덮어 쓸 때까지. 이것이 한동안 작동 할 수있는 이유입니다. 특히 깊이 중첩 된 함수 세트 또는 정말 크기가 크거나 많은 로컬 객체가있는 함수가 해당 스택 메모리에 다시 도달하면 갑자기 작동이 중단됩니다.

동일한 프로그램 부분에 다시 도달하여 이전 지역 함수 변수를 새 함수 변수로 덮어 쓸 수도 있습니다. 이 모든 것은 매우 위험하며 극도로 낙담해야합니다. 로컬 개체에 대한 포인터를 사용하지 마십시오!


2

기억해야 할 점은 이러한 간단한 규칙이며 매개 변수와 반환 유형 모두에 적용됩니다.

  • 값-해당 항목의 사본을 만듭니다.
  • 포인터-해당 항목의 주소를 나타냅니다.
  • 참조-말 그대로 문제의 항목입니다.

각각에 대한 시간과 장소가 있으므로 그들을 알고 있는지 확인하십시오. 여기에 표시된대로 지역 변수는 함수 범위에서 지역적으로 살아있는 시간으로 제한됩니다. 귀하의 예에서 반환 유형 int*과 반환 &i은 똑같이 잘못되었을 것입니다. 이 경우에는 더 나을 것입니다 ...

void func1(int& oValue)
{
    oValue = 1;
}

이렇게하면 전달 된 매개 변수의 값이 직접 변경됩니다. 이 코드는 ...

void func1(int oValue)
{
    oValue = 1;
}

하지 않을 것입니다. oValue함수 호출에 대한 로컬 값을 변경합니다 . 그 이유는 실제로 자체가 oValue아닌 의 "로컬"복사본 만 변경하기 때문 oValue입니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.