2009-01-07 6 views
2

재귀 C++ 프로그램에서 메모리를 할당하고 할당을 해제하는 데 문제가 있습니다. 따라서 자동 메모리 관리 솔루션을 사용하지 않고서도 필자가 경험하고있는 메모리 누수를 해결할 수있는 사람이 있는지 궁금합니다.재귀 C++ 호출의 메모리 할당

다음 코드는 근본적으로 문제를 설명합니다 (인위적인 예이지만 실수 나 간략화를 수정하십시오).

class Number { 
    public: 
     Number() { value = 1; }; 
     Number& operator + (const Number& n1) const { 
      Number result = value + n1.value; 
      return result; 
     }; 
     int value; 
}; 

두 함수는 재귀를 수행 할 :

다수의 클래스

은 숫자의 값을 보유하는

당신이 같이 Recurse 기능에 할당 된 메모리가 유출 볼 수
Number& recurse(const Number& v1) { 
    Number* result = new Number(); 
    Number one = Number(); 
    *result = *result + recurse(one); 
    return *result; 
} 

int main(...) { 
    Number answer = Number(); 
    answer = recurse(result); 
} 

, 재귀의 본질에 기반하여이 메모리를 어디에서 자유롭게 할 수 있는지 모르겠습니다.

+0

"실수 나 간략한 부분을 수정하십시오."- 질문을 편집 할 수 있도록해야합니다. –

답변

12

문제는 여기에 있습니다 :

Number& operator + (const Number& n1) const { 
    Number result = value + n1.value; 
    return result; 
}; 

참조로 지역 변수 (result)를 반환하고, 그 큰 NO-NO입니다. 지역 변수는 스택에 할당되고 함수가 종료되면 변수는 사라집니다. 지역 변수에 대한 참조를 반환하는 것은 다른 곳에서 사용되는 스택에 포인터를 반환하는 것이므로 많은 불량을 일으킬 수 있습니다.

대신 수행해야하는 작업은 값순으로 반환하는 것입니다 (반환 유형을 Number&에서 Number으로 변경하면됩니다). 적절한 복사 생성자가 있는지 확인하거나 컴파일러의 자동 생성 된 복사 생성자가 필요에 맞게 작성되었는지 확인하십시오. 즉, operator+이 반환되면 복사본을 만듭니다 (종종 최적화 된 상태로 만들 수 있음). 포인터 또는 참조가 포함되어 있지 않으므로 손상된 반환 값을 얻을 수 없습니다.

메모리 누수를 해결하려면 boost::shared_ptr과 같은 스마트 포인터를 사용할 수 있습니다. 또는 포인터와 동적 메모리를 모두 버리고 recurse()의 값으로 결과를 반환하면됩니다.

3

당신이 시작 힙에 메모리를 할당하고 왜 표시되지 않습니다 : 단지 당신이 변수가 때의 기능을 정리 될 것이라고 보장하고있는 스택에 할당함으로써

Number& recurse(const Number& v1) { 
    Number result; 
    Number one; 

    // I assume there is a step here to determine if the recursion should stop 

    result += recurse(one); 
    return result; 
} 

보고.

그렇지 않으면 당신은 스마트 포인터의 일종을 사용해야 할 것 같아요.

0

스마트 포인터는 친구입니다. 적어도 auto_ptr에 대한 빠른 읽기를 수행하십시오.

또한 다른 문제 (더 이상 존재하지 않는 값에 대한 참조를 반환)에 대한 Adam Rosenfield의 의견을 읽어보십시오.

2

그래서 코드에서 Adam Rosenfield가 지적한 로컬 변수의 주소를 반환하는 것 외에 다른 세 가지 문제가 있습니다.

먼저 재귀 함수가 절대 종료되지 않습니다. recurse()의 어느 시점에서 recurse()를 다시 호출하지 않고 반환하는 값을 확인해야합니다. 그것은 재귀의 근본적인 부분입니다. 전달 된 인수 v1도 사용되지 않습니다.

두 번째로 연산자 +()가 실제로 작동하지 않습니다. Number() 객체에 int를 할당하는 방법은 없습니다.

세 번째로, 메인에서는 선언되지 않은 result라는 것을 전달합니다.

이러한 오류를 잊어 버리면 스택 오버플로를 피하기 위해 힙에있는 모든 개체를 할당하려는 것으로 가정합니다.이 함수는 여러 번 반복되거나 실제 개체가 Number보다 훨씬 큽니다. 이 경우 recurse() 내부의 힙에 return 변수를 할당하여 호출자가 반환 된 객체를 삭제하도록합니다. 따라서 recurse()와 main()에서 recurse()를 호출 한 후에는 반환 된 값을 삭제해야합니다. 호출자에게 참조 대신 포인터를 반환한다는 것을 나타내는 규칙입니다. 따라서 recurse()와 main()은 다음과 같이 보일 것입니다 :

Number* recurse(const Number& v1) { 
    Number* result = new Number(); 
    Number one; 
    if(v1.value >= 2) { 
     Number temp; 
     temp.value = v1.value - 1; 
     Number* partialResult = recurse(temp); //capture the object to delete 
     *result = *partialResult + one; 
     delete partialResult;     //delete the object 
    } 
    return result; 
} 

int main() {  
    Number result; 
    result.value = 15; 
    Number *answer; 
    answer = recurse(result); 
    delete answer; 
} 

참고 : 실제로 반복 계산되는 것은 무의미합니다. 나는 의도가 무엇인지 모르지만 그것은 단지 작동하는 것입니다.

+0

예외는 아닙니다. 원하는 경우 partialResult에 auto_ptr <>을 사용합니다. –

1

동적으로 메모리를 할당하는 이유가 있습니까?

Number recurse(const Number& v1) { 
    Number result; 
    Number one; 
    retun result + recurse(one); 
} 


또한 당신이 값 V1

를 사용하지 않는 통지하지만 큰 실수는 재귀 NO 이스케이프 절을 가지고 있다는 것입니다.
기본적으로 메모리가 부족한 무한 재귀가 실제로 발생합니다.