c++ 생성자에서 exception을 throw 하도록 했을 때, exception이

vudghkzm의 이미지

c++에서 생성자에서 멤버 값을 초기화 할 때, 어떤 오류가 발생했을 경우 exception을 throw 하도록 하였습니다. 예를 들어 다음과 같이 말이죠.

class A
{
public:
  ...
  A(int a);
  ...
}

A::A(int a)
{
  ...
  if( a <0 )
    throw 1;
  ...
}

그리고 메인에서는 아래와 같이 처리하였습니다.

...
A* a;

try
{
  a = new A(-1);
}
catch(int e)
{
  cout << e << endl;
}

이 경우 catch 문으로 넘어옴과 동시에 a가 delete된다고 알고 있었는데, 알아본 결과 catch 문으로 넘어와도 a는 delete 안되더군요.
생성자에서 exception을 throw 하도록 처리해놓으면, exception이 발생함과 동시에 객체가 유효하지 않게 처리된다고 알고 있었습니다만, 제가 잘 못 알고 있던 건가요?
아니면 제가 exception을 제대로 사용하지 못하고 잇는 건가요?

익명 사용자의 이미지

vudghkzm wrote:

catch 문으로 넘어옴과 동시에 a가 delete된다고 알고 있었는데, 알아본 결과 catch 문으로 넘어와도 a는 delete 안되더군요.
생성자에서 exception을 throw 하도록 처리해놓으면, exception이 발생함과 동시에 객체가 유효하지 않게 처리된다고 알고 있었습니다만, 제가 잘 못 알고 있던 건가요?

혹시 stack에 쌓인 객체에 대해서 오해를 하신게 아닌지?
heap에 할당 된 놈은 명시적으로 지워주셔야 합니다.

bugiii의 이미지

지금과 같은 상황은 std::auto_ptr 이 적용될 수 있을 것 같습니다. 간단히 말씀드려서 동적 객체를 스택에 만들어지는 로컬 객체처럼 사용할 수 있도록 해줍니다. 자동으로 스코프를 벋어나면 delete 해준다는 것이죠.

C++ 자체에서 자동으로 무엇인가를 해준다는 것은 거의 없는 편입니다. 여러가지 라이브러리와 클래스, 템플릿 등으로 지원되는 경우가 많습니다. 좋은 텍스트들이 많이 나와 있으니까 좋은 책들 골라서 참고하시면 도움이 되시리라 생각됩니다.

p.s. 포천쪽에 웍샵와서 저녁거리 사러 나와, PC 방에 잠시 들러서 하는 일이라곤 kldp 답변이라니... -_-;

doldori의 이미지

저도 auto_ptr 같은 것을 생각해 봤는데 지금 상황은 그보다 더 미묘한 것 같습니다.
예를 들어 auto_ptr<A> a(new A(0)) 이 수행되는 과정을 쪼개보면

(1) ::operator new(sizeof(A))에 의하여 raw memory 할당
(2) A의 생성자에 의하여 할당된 메모리에 A 개체를 생성
(3) auto_ptr이 이 메모리를 참조

그런데 A 생성자에서 예외가 발생할 경우 (2)가 완료되지 않으므로 auto_ptr도
생성되지 않을 것이고 따라서 auto_ptr의 소멸자도 호출되지 않을 것입니다.
흠... 생각보다 어려운 문제로군요. 고민 좀 해봐야겠습니다.

progcom의 이미지

생성자에서 예외를 던지면(throw), '생성하려던 객체의 메모리'는 정상적으로 헤제됩니다. (긴가민가해서 방금 메모리 주소 수십번 찍어보면서 실험해봤습니다 -_-)

그리고, new 하던 도중에 예외가 발생했으므로, a가 새 주소를 가리키지도 않고, 당연히 delete할 것도 없고, 할 수도 없습니다.
a는 따로 선언한 포인터니 남아있는 것이지, 새로 할당하려던 (그러다가 예외 발생이라 해제된) 메모리와는 연관이 없습니다.

doldori의 이미지

clc++에 문의한 결과 위의 (2)에서 예외가 발생하더라도 ::operator delete()가
호출되는 것이 보장된다는 답을 얻었습니다. 표준에 이런 구절이 있군요.

Quote:
5.3.4/17
If any part of the object initialization described above terminates
by throwing an exception and a suitable deallocation function can be
found, the deallocation function is called to free the memory in which
the object was being constructed, after which the exception continues
to propagate in the context of the new-expression. [...]

즉 bugiii님 말씀대로 auto_ptr과 같은 스마트 포인터를 사용하는 것이 가장 좋겠습니다.

ps. 괜히 혼자서 북 치고 장구 치고 해서 죄송합니다. ^^;

JosephJang의 이미지

vudghkzm wrote:

이 경우 catch 문으로 넘어옴과 동시에 a가 delete된다고 알고 있었는데, 알아본 결과 catch 문으로 넘어와도 a는 delete 안되더군요.
생성자에서 exception을 throw 하도록 처리해놓으면, exception이 발생함과 동시에 객체가 유효하지 않게 처리된다고 알고 있었습니다만, 제가 잘 못 알고 있던 건가요?
아니면 제가 exception을 제대로 사용하지 못하고 잇는 건가요?

무슨 근거로 delete가 안된다고 판단하셨는지는 모르겠지만, delete 됩니다. global operator delete나, class-specific operator delete를 정의해서 테스트해보시기 바랍니다.

그리고 C++ Standard의 15.2절에서도 이 상황에 대해서 언급하고 있습니다.

Quote:

An object that is partially constructed or partially destroyed will have destructors executed for all of its fully constructed subobjects, that is, for subobjects for which the constructor has completed execution and the destructor has not yet begun execution. Should a constructor for an element of an automatic array throw an exception, only the constructed elements of that array will be destroyed. If the object or array was allocated in a new-expression, the matching deallocation function (3.7.3.2, 5.3.4, 12.5), if any, is called to free the storage occupied by the object.
kihongss의 이미지

vudghkzm wrote:
c++에서 생성자에서 멤버 값을 초기화 할 때, 어떤 오류가 발생했을 경우 exception을 throw 하도록 하였습니다. 예를 들어 다음과 같이 말이죠.

class A
{
public:
  ...
  A(int a);
  ...
}

A::A(int a)
{
  ...
  if( a <0 )
    throw 1;
  ...
}

그리고 메인에서는 아래와 같이 처리하였습니다.

...
A* a;

try
{
  a = new A(-1);
}
catch(int e)
{
  cout << e << endl;
}

이 경우 catch 문으로 넘어옴과 동시에 a가 delete된다고 알고 있었는데, 알아본 결과 catch 문으로 넘어와도 a는 delete 안되더군요.
생성자에서 exception을 throw 하도록 처리해놓으면, exception이 발생함과 동시에 객체가 유효하지 않게 처리된다고 알고 있었습니다만, 제가 잘 못 알고 있던 건가요?
아니면 제가 exception을 제대로 사용하지 못하고 잇는 건가요?

객체가 생성되기 위해서는
생성자에서 예외가 발생하지 않고 괄호 } 까지
코드가 실행되어야합니다.
위의 코드에서는 괄호 } 까지 못가고 생성자에서
Exception이 발생한 경우라 객체 자체가 안생긴거죠.
(메모리 allocation 자체가 안되었답니다. :D )
따라서, 객체를 위해 메모리 할당도 안 일어났으니,
당연히 delete할 필요도 없죠.

JosephJang wrote:
vudghkzm wrote:

이 경우 catch 문으로 넘어옴과 동시에 a가 delete된다고 알고 있었는데, 알아본 결과 catch 문으로 넘어와도 a는 delete 안되더군요.
생성자에서 exception을 throw 하도록 처리해놓으면, exception이 발생함과 동시에 객체가 유효하지 않게 처리된다고 알고 있었습니다만, 제가 잘 못 알고 있던 건가요?
아니면 제가 exception을 제대로 사용하지 못하고 잇는 건가요?

무슨 근거로 delete가 안된다고 판단하셨는지는 모르겠지만, delete 됩니다. global operator delete나, class-specific operator delete를 정의해서 테스트해보시기 바랍니다.

그리고 C++ Standard의 15.2절에서도 이 상황에 대해서 언급하고 있습니다.

Quote:

An object that is partially constructed or partially destroyed will have destructors executed for all of its fully constructed subobjects, that is, for subobjects for which the constructor has completed execution and the destructor has not yet begun execution. Should a constructor for an element of an automatic array throw an exception, only the constructed elements of that array will be destroyed. If the object or array was allocated in a new-expression, the matching deallocation function (3.7.3.2, 5.3.4, 12.5), if any, is called to free the storage occupied by the object.

delete 된다고 하셨는데, 소멸자를 정의하셔서
뭔가 찍어보시길 소멸자 자체가 호출되지 않는답니다.
JosephJang의 이미지

kihongss wrote:

객체가 생성되기 위해서는
생성자에서 예외가 발생하지 않고 괄호 } 까지
코드가 실행되어야합니다.
위의 코드에서는 괄호 } 까지 못가고 생성자에서
Exception이 발생한 경우라 객체 자체가 안생긴거죠.

맞습니다. Object lifetime의 정의는 constructor가 끝난 시점에서부터 destructor가 시작할 때까지입니다.
Herb Sutter도 exception safety에 대해서 설명하면서 이에 대한 언급을 하고 있습니다.
http://www.gotw.ca/gotw/066.htm
kihongss wrote:

(메모리 allocation 자체가 안되었답니다. :D )
따라서, 객체를 위해 메모리 할당도 안 일어났으니,
당연히 delete할 필요도 없죠.

이 부분은 틀렸습니다.
new에 의해 memory allocation이 일어난 후에 constructor가 불리고, 따라서, constructor에서 exception이 발생한다면, 표준에서 언급한 것처럼 (memory를 free해주기 위해) 적절한 delete가 불리게 됩니다.
kihongss wrote:

delete 된다고 하셨는데, 소멸자를 정의하셔서
뭔가 찍어보시길 소멸자 자체가 호출되지 않는답니다.

위에서 말씀드렸듯이 소멸자는 불리지 않습니다. 하지만 delete는 호출됩니다. (operator delete를 정의해서 테스트해보세요.)
kihongss의 이미지

JosephJang wrote:
kihongss wrote:

객체가 생성되기 위해서는
생성자에서 예외가 발생하지 않고 괄호 } 까지
코드가 실행되어야합니다.
위의 코드에서는 괄호 } 까지 못가고 생성자에서
Exception이 발생한 경우라 객체 자체가 안생긴거죠.

맞습니다. Object lifetime의 정의는 constructor가 끝난 시점에서부터 destructor가 시작할 때까지입니다.
Herb Sutter도 exception safety에 대해서 설명하면서 이에 대한 언급을 하고 있습니다.
http://www.gotw.ca/gotw/066.htm
kihongss wrote:

(메모리 allocation 자체가 안되었답니다. :D )
따라서, 객체를 위해 메모리 할당도 안 일어났으니,
당연히 delete할 필요도 없죠.

이 부분은 틀렸습니다.
new에 의해 memory allocation이 일어난 후에 constructor가 불리고, 따라서, constructor에서 exception이 발생한다면, 표준에서 언급한 것처럼 (memory를 free해주기 위해) 적절한 delete가 불리게 됩니다.
kihongss wrote:

delete 된다고 하셨는데, 소멸자를 정의하셔서
뭔가 찍어보시길 소멸자 자체가 호출되지 않는답니다.

위에서 말씀드렸듯이 소멸자는 불리지 않습니다. 하지만 delete는 호출됩니다. (operator delete를 정의해서 테스트해보세요.)

그렇군요.
operator delete 가 호출되더라도, 소멸자가 호출되지 않는군요.

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.