2008-09-29 9 views
18
class someclass {}; 

class base 
{ 
    int a; 
    int *pint; 
    someclass objsomeclass; 
    someclass* psomeclass; 
public: 
    base() 
    { 
     objsomeclass = someclass(); 
     psomeclass = new someclass(); 
     pint = new int(); 
     throw "constructor failed"; 
     a = 43; 
    } 
} 

int main() 
{ 
    base temp(); 
} 

위 코드에서 생성자가 throw됩니다. 어느 객체가 유출 될 것이며, 어떻게 메모리 누수를 피할 수 있습니까?아래 코드로 인해 C++에서 메모리 누수가 발생합니까

int main() 
{ 
    base *temp = new base(); 
} 

위의 코드는 어떻습니까? 생성자가 throw 한 후에 메모리 누수를 피할 수있는 방법은 무엇입니까?

+1

나는 알고있다, 나는 몹시 괴로운 성격을 가지고있다. 나는 그것을 도울 수 없다. 내 2 센트 : 명세서 objsomeclass = someclass(); 불필요합니다. 생성자의 본문에는 objsomeclass가 이미 기본값으로 초기화되어 있습니다. 아래의 objsomeclass (someclass())는 의미가 없습니다. –

+0

동의하지만 someclass에 명시 적 생성자가 있다고 생각합니다. 개체에 초점을 맞추고 싶습니다. 생성자에서 생성되었습니다. – yesraaj

+0

예, 알고 있습니다. 그래서 내가 딱딱한 소리를 질렀다. BTW 생성자 base() public 일 수 있습니다. –

답변

35

예 메모리가 누출됩니다. 생성자가 throw 될 때 소멸자가 호출되지 않습니다 (이 경우 동적으로 할당 된 객체를 해제하는 소멸자는 표시하지 않지만 사용자가 소유하고 있다고 가정 할 수 있음).

스마트 포인터를 사용하는 주된 이유는 스마트 poitners가 본격적인 개체이기 때문에 예외 스택이 unwind 동안 호출 된 소멸자를 얻고 메모리를 확보 할 수있는 기회를 갖기 때문입니다.

class base{ 
    int a; 
    scoped_ptr<int> pint; 
    someclass objsomeclass; 
    scoped_ptr<someclass> psomeclass; 
    base() : 
     pint(new int), 
     objsomeclass(someclass()), 
     psomeclass(new someclass()) 

    { 
     throw "constructor failed"; 
     a = 43; 
    } 
} 

그리고 당신은 어떤 메모리 누수가없는 것 (그리고 기본 dtor는 동적 메모리 할당을 정리 것) : 당신이 부스트의 scoped_ptr를 <> 템플릿 같은 것을 사용하는 경우

, 클래스는 더 같이 볼 수 있었다 .


은 요약하면 (희망이 또한

base* temp = new base(); 

문에 대한 질문에 답) :

수행해야하는 몇 가지가 예외가 생성자 내부에서 발생합니다 객체의 중단 된 구성에서 발생할 수있는 리소스 할당을 적절하게 처리한다는 측면에서 유의하십시오.

  1. 생성되는 개체의 소멸자가 이 아니고이 호출됩니다.
  2. 해당 개체의 클래스에 포함 된 멤버 개체의 소멸자는
  3. 이 호출됩니다. 생성 된 개체의 메모리가 해제됩니다. , 리소스를 해제,

    1. 캐치 예외 :

    이 당신의 객체가 자원을 소유하는 경우, 생성자가 발생하는 경우 이미 취득되었을 수 있습니다 이러한 자원을 정리하기 위해이 방법을 사용할 수 있다는 것을 의미 그리고 나서 다시 사십시오. 이것은 올바르게하기 어려울 수 있으며 유지 관리 문제가 될 수 있습니다.

  4. 개체를 사용하여 RAII (Resource Lifetimes)를 관리하고 이러한 개체를 구성원으로 사용합니다. 객체의 생성자가 예외를 throw하면 멤버 객체에는 desctructor가 호출되고 수명이 만료 된 리소스를 해제 할 수있는 기회가 주어집니다.
+0

부스트에서 끌어 들이지 않는가? 메모리 관리가 꽤 어리 석다. –

+0

아마도 scoped_ptr이 TR1에 있고 C++ 09에있을 것이므로 어쨌든 배워야 할 것이 있습니다. 그리고 scoped_ptr을 가진 Boost 부분은 단지 헤더 다. 마지막으로,이 간단한 예제 대신 auto_ptr을 사용할 수 있지만 auto_ptr은 피해야 할 대상 일 것입니다. –

+0

기본 클래스의 dtor는 한번도 호출됩니다? 아래에 무슨 일이 생길까요 base * temp = new base(); – yesraaj

-2

"새 것"을 모두 삭제해야하거나 메모리 누수가 발생합니다. 그래서이 두 라인 : 당신이 할 필요가 있기 때문에

psomeclass = new someclass(); 
pint = new int(); 

는, 메모리 누수가 발생합니다에서

delete pint; 
delete psomeclass; 

최종적으로 그들에게 유출되는 것을 방지하기 위해 차단합니다. 또한

,이 라인 :

base temp = base(); 

이 필요하지 않습니다. 다음을 수행하면됩니다.

base temp; 

"= base()"를 추가 할 필요가 없습니다.

+1

C++의 "finally"블록과 같은 것은 없습니다 –

+0

사실, C++의 맛에 따라 액세스 할 수도 있고 그렇지 않을 수도 있습니다. 그렇지 않은 경우 , 코드 경로에 관계없이 할당이 삭제되는지 확인해야합니다. – Colen

+1

추가 초기화에 대한 귀하의 의견이 잘못되었습니다. 결과 객체는 한 번만 초기화되며 복사되지 않습니다. –

0

예, 코드가 메모리를 누설합니다. "new"를 사용하여 할당 된 메모리 블록은 예외가 발생할 때 해제되지 않습니다. 이것은 RAII의 동기 부여의 일부입니다.

은 다음과 같이 뭔가를 시도, 메모리 누수를 방지하려면 :

당신이 생성자에 던져 경우
psomeclass = NULL; 
pint = NULL; 
/* So on for any pointers you allocate */ 

try { 
    objsomeclass = someclass(); 
    psomeclass = new someclass(); 
    pint = new int(); 
    throw "constructor failed"; 
    a = 43; 
} 
catch (...) 
{ 
    delete psomeclass; 
    delete pint; 
    throw; 
} 

+0

대신 객체 (스마트 포인터)를 사용하여 포인터를 사용하면 상황이 나아질 것입니다. 예외가 블록에 던져지면 자동 객체가 지워집니다. – yesraaj

+0

스마트 포인터가 더 좋습니다. 또한 'raise'를 대체하십시오. '던지다'와 함께 현재 예외를 되돌리기. –

0

, 당신은 전화가 던져 전에 온 모든 것을 정리해야합니다. 당신이 상속을 사용하거나 소멸자를 던지고 있다면, 정말로 그렇게해서는 안됩니다. 그 행동은 이상합니다. (저의 기준은 편리하지 않지만 정의되지 않았습니까?).

+0

실제로 정의되지 않았는지 확실하지 않지만 예외가 발생하는 경우 스택 해 제 중에 소멸자가 호출되므로 확실히 위험합니다. 다른 *가 * 발생하는 동안 예외를 발생 시키면 내가 아는 모든 C++ 런타임은 응용 프로그램을 종료합니다. –

+0

예외 처리 중에 발생한 소멸자에서 포착되지 않은 예외가 발생하면 std :: terminate()가 호출됩니다.이 호출은 기본적으로 std :: abort()를 호출합니다. 기본 동작은 무시할 수 있습니다. – KTC

+0

기본 동작을 재정의 할 수 있지만 버전을 여전히 응용 프로그램으로 되돌릴 수는 없지만 종료해야합니다. –

5

두 가지 모두 유출됩니다. (RAII) - 그것이 예외가 발생하면 전화를받을 스마트 포인터의 소멸자 내에서 삭제 될 수 있도록

이름 스마트 포인터에 힙 만든 개체의 주소를 할당합니다.

class base { 
    int a; 
    boost::shared_ptr<int> pint; 
    someclass objsomeclass; 
    boost::shared_ptr<someclass> psomeclass; 

    base() : 
     objsomeclass(someclass()), 
     boost::shared_ptr<someclass> psomeclass(new someclass()), 
     boost::shared_ptr<int> pint(new int()) 
    { 
     throw "constructor failed"; 
     a = 43; 
    } 
}; 

이제 psomeclass & 파인트 소멸자는 예외가 생성자에 던져 때 스택이 긴장을 풀 때 호출되며, 그 소멸자는 할당 된 메모리의 할당을 해제합니다. 생성자는 예외가 발생하는 경우 새로운 오퍼레이터에 의해 할당

통상 메모리 할당
int main(){ 
    base *temp = new base(); 
} 

하여 (비 plcaement) 새로운 메모리는 자동으로 해제된다. 마이크 B의 대답에 대한 의견에 대한 응답으로 개별 회원을 자유롭게하는 이유에 대해서는 자동으로 해제되는 것이 다른 경우가 아니라 새로 할당 된 객체의 생성자에서 예외가 발생하는 경우에만 적용됩니다. 또한 해제 된 메모리는 객체 멤버에 할당 된 메모리이며 생성자 내부에서 할당 한 메모리가 아닙니다. 즉() 및 새로운 INT()를이 멤버 변수 , 파인트, objsomeclasspsomeclass에 대한 메모리를 해제 할 수 있지만 메모리는 새로운 someclass에서 할당.

+0

shared_ptr <>은 개체를 소유하고 공유 소유권을 절대로 부여하지 않으면 잔인합니다.std :: auto_ptr <>로 간단하게하기 –

+0

// 질문을 변경했습니다. base * temp = new base(); – yesraaj

+0

그리고 boost :: scoped_ptr <>은 auto_ptr <>보다 낫습니다. 자체 웜 깡통을 가지고 있습니다. –

-3

당신은 ... 정수를 정리하기 위해 필요하지 내가 최고 대답은 잘못 여전히 메모리 누수가 것이라고 생각

RWendi

+0

Dave Moore에 대해 자세히 설명해 주시겠습니까? "정수를 정리할 필요가 없다"는 부분입니까? 그 이유는 Int 메모리 포인터가 클래스 메모리 포인터와 비교할 때 비용이 많이 들지 않기 때문에 청소할 필요가 없다는 것입니다. – RWendi

+0

둘 다 누출됩니다. 비용은 문제가되지 않습니다. 문제는 유출되었는지 아닌지 여부입니다. 그리고 그 코드 덩어리가 수천 또는 수백만 번 실행되면, 그 작은 비용이 합산됩니다. "비용"이 관련성이 있더라도 차이를 만드는 * 포인터 크기가 아니라 지시 대상 엔터티의 크기입니다. 예를 들어, sizeof (someclass) == sizeof (int) 일 수 있습니다. 그리고 당신은 포인터를 지우지 않고 있습니다 - 지적한 엔티티를 지우고 있습니다. –

1

을 psomeclass을 삭제해야합니다. 생성자가 초기화를 완료하지 않았기 때문에 생성자가 예외를 throw하고 일부 멤버가 생성자 호출에 도달하지 못했기 때문에 클래스 멤버의 소멸자가 이 아닌이 호출됩니다. 소멸자는 클래스의 소멸자 호출 중에 만 호출됩니다. 그것은 단지 의미가 있습니다.

이 간단한 프로그램에서이를 보여줍니다. 다음과 같은 출력 (g를 사용하여 ++ 4.5.2)와

#include <stdio.h> 


class A 
{ 
    int x; 

public: 
    A(int x) : x(x) { printf("A constructor [%d]\n", x); } 
    ~A() { printf("A destructor [%d]\n", x); } 
}; 


class B 
{ 
    A a1; 
    A a2; 

public: 
    B() 
    : a1(3), 
     a2(5) 
    { 
     printf("B constructor\n"); 
     throw "failed"; 
    } 
    ~B() { printf("B destructor\n"); } 
}; 


int main() 
{ 
    B b; 

    return 0; 
} 

:

A constructor [3] 
A constructor [5] 
B constructor 
terminate called after throwing an instance of 'char const*' 
Aborted 

생성자가 실패하는 경우는 도중에 다음 그것을 처리하는 것은 귀하 자신의 책임입니다. 더 나쁜 것은 예외가 기본 클래스의 생성자에서 발생할 수 있습니다! 이러한 경우를 처리하는 방법은 "함수 try 블록"을 사용하는 것입니다 (그러나 그래도 부분적으로 초기화 된 객체의 삭제는주의 깊게 작성해야합니다). 당신이 그것을 실행하는 경우

#include <stdio.h> 


class A 
{ 
    int x; 

public: 
    A(int x) : x(x) { printf("A constructor [%d]\n", x); } 
    ~A() { printf("A destructor [%d]\n", x); } 
}; 


class B 
{ 
    A * a1; 
    A * a2; 

public: 
    B() 
    try // <--- Notice this change 
    : a1(NULL), 
     a2(NULL) 
    { 
     printf("B constructor\n"); 
     a1 = new A(3); 
     throw "fail"; 
     a2 = new A(5); 
    } 
    catch (...) { // <--- Notice this change 
     printf("B Cleanup\n"); 
     delete a2; // It's ok if it's NULL. 
     delete a1; // It's ok if it's NULL. 
    } 

    ~B() { printf("B destructor\n"); } 
}; 


int main() 
{ 
    B b; 

    return 0; 
} 

당신은 할당 된 객체가 파괴 해제됩니다 예상 출력을 얻을 것이다 :

문제에 대한 올바른 접근 방식은 다음과 같은 것이 될 것이다.

B constructor 
A constructor [3] 
B Cleanup 
A destructor [3] 
terminate called after throwing an instance of 'char const*' 
Aborted 

추가 공유 기능을 사용하여 스마트 공유 포인터로 해결할 수 있습니다.

class C 
{ 
    std::shared_ptr<someclass> a1; 
    std::shared_ptr<someclass> a2; 

public: 
    C() 
    { 
     std::shared_ptr<someclass> new_a1(new someclass()); 
     std::shared_ptr<someclass> new_a2(new someclass()); 

     // You will reach here only if both allocations succeeded. Exception will free them both since they were allocated as automatic variables on the stack. 
     a1 = new_a1; 
     a2 = new_a2; 
    } 
} 

행운을 빌어 요, Tzvi이 유사한 생성자를 작성.

+0

첫 번째 예제의 예외가 catch되지 않으므로 스택 unwinding이 발생하지 않고 소멸자가 호출되지 않습니다. try catch에서'B b; '를 래핑하면 소멸자가 예상대로 호출됩니다. –