아래 코드가 C ++에서 메모리 누출을 유발합니까?
-
02-07-2019 - |
문제
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();
}
위 코드에서 생성자는 던졌습니다. 어떤 물체가 누출되고, 메모리 누출을 어떻게 피할 수 있습니까?
int main()
{
base *temp = new base();
}
위 코드는 어떻습니까? 생성자가 던진 후에 메모리 누출을 어떻게 피할 수 있습니까?
해결책
예, 메모리 누출이 발생합니다. 생성자가 던지면 소멸자가 호출되지 않습니다 (이 경우 동적으로 할당 된 물체를 해방시키는 소멸자가 표시되지 않지만 하나가 있다고 가정하자).
이것은 스마트 포인터를 사용하는 주요 이유입니다. 스마트 포터는 완전한 깃발 물체이기 때문에 예외의 스택이 풀리고 메모리를 풀 수있는 기회를 갖게됩니다.
boost의 scoped_ptr <> 템플릿과 같은 것을 사용하는 경우 클래스가 더 많이 보일 수 있습니다.
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도 동적 메모리 할당을 정리할 수 있습니다).
요약하면 (그리고 이것은 또한 이것은 또한
base* temp = new base();
성명):
생성자 안에 예외가 발생하면 객체의 낙태 된 구성에서 발생했을 수있는 자원 할당을 적절히 처리하는 데 주목해야 할 몇 가지 사항이 있습니다.
- 구성되는 대상의 소멸자는 의지합니다 ~ 아니다 불리다.
- 해당 개체의 클래스에 포함 된 멤버 객체의 소멸자는 호출됩니다.
- 구성된 물체에 대한 메모리가 해제됩니다.
즉, 객체가 자원을 소유하고 있으면 생성자가 던질 때 이미 획득했을 수있는 리소스를 정리할 수있는 2 가지 방법이 있습니다.
- 예외를 잡고 리소스를 해제 한 다음 재검토하십시오. 이것은 정확해지기가 어려울 수 있으며 유지 보수 문제가 될 수 있습니다.
- 물체를 사용하여 자원 수명 (RAII)을 관리하고 해당 객체를 멤버로 사용하십시오. 객체의 생성자가 예외를 던지면, 멤버 객체는 desctructors를 호출하고 수명이 담당하는 자원을 해방시킬 수있는 기회를 갖게됩니다.
다른 팁
두 사람 모두 유출 될 것입니다.
생성 된 객체의 주소를 할당하십시오 명명 된 스마트 포인터는 예외가 발생할 때 호출을받는 스마트 포인터 파괴자 내부에서 삭제되도록 - (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();
}
생성자가 예외를 던지면 연산자에 의해 할당 된 새, 새로운, 메모리를 사용한 일반 메모리 할당의 경우, 새로운 경우 새로 할당 된 메모리가 자동으로 해제됩니다. 개별 회원을 자유롭게하는 이유 (Mike B의 답변에 대한 의견에 대한 응답으로), 자동 해방은 다른 경우가 아니라 새로운 객체의 생성자에 예외가 발생하는 경우에만 적용됩니다. 또한, 해방 된 메모리는 생성자 내부에 말할 수있는 메모리가 아니라 객체 멤버에 할당 된 메모리입니다. 즉, 멤버 변수의 메모리를 해방시킬 것입니다. ㅏ, 파인트, objsomeclass, 그리고 psomeclass, 그러나 메모리가 할당되지 않았습니다 새로운 someclass () 그리고 새로운 int ().
나는 최고 답변이 잘못되었고 여전히 메모리를 누출 할 것이라고 생각합니다. 클래스 멤버를위한 소멸자 ~ 아니다 생성자가 예외를 던지면 (초기화를 완료하지 않았으므로, 일부 구성원이 생성자 호출에 도달 한 적이 없기 때문에) 호출됩니다. 그들의 소멸자는 계급의 소멸자 호출 중에 만 호출됩니다. 그것은 단지 의미가 있습니다.
이 간단한 프로그램은 그것을 보여줍니다.
#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;
}
다음 출력으로 (G ++ 4.5.2 사용) :
A constructor [3]
A constructor [5]
B constructor
terminate called after throwing an instance of 'char const*'
Aborted
생성자가 파트 웨이에 실패하면이를 처리하는 것이 귀하의 책임입니다. 더 나쁜 것은, 기본 클래스의 생성자에서 예외가 발생할 수 있습니다! 이러한 사례를 다루는 방법은 "기능 시도 블록"을 사용하는 것입니다 (그러나 심지어 부분적으로 초기화 된 객체의 파괴를 신중하게 코딩해야합니다).
문제에 대한 올바른 접근 방식은 다음과 같습니다.
#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.
생성자를 던지면 전화를 걸기 전에 온 모든 것을 정리해야합니다. 상속을 사용하거나 파괴자를 던지는 경우 실제로는 안됩니다. 동작은 홀수입니다 (표준은 편리하지 않지만 정의되지 않았을 수도 있습니까?).
예, 그 코드는 메모리 유출됩니다. "새"를 사용하여 할당 된 메모리 블록은 예외가 제기 될 때 해제되지 않습니다. 이것은 뒤에 동기 부여의 일부입니다 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;
}
당신이 "새로운"모든 것을 삭제해야합니다. 그렇지 않으면 메모리 누출이 발생합니다. 그래서이 두 줄 :
psomeclass = new someclass();
pint = new int();
해야하기 때문에 메모리 누출이 발생합니다.
delete pint;
delete psomeclass;
그들이 유출되는 것을 피하기 위해 마침내 블록에서.
또한이 줄 :
base temp = base();
불필요합니다. 당신은 그냥해야합니다 :
base temp;
"= base ()"를 추가하면 불필요합니다.
psomeclass를 삭제해야합니다 ... 정수를 정리할 필요는 없습니다 ...