[수정]동적 할당한 객체를 해제했을때 해제한 걸 모르고 다시 이용할 때..

wmcst의 이미지

가령..
Human *joy = new Human("kim");
joy->do();
..
이런식으로 joy를 잘 쓰다가
delete joy를 했습니다.
그리고 나중에
joy->do()를 또 하게 됐을 때 faild를 의미하는 -1을 리턴하게 하고 싶은데.. 이렇게 하려면 어떻게 해야하나요?
joy에게 할당한 영역자체를 delete를 함으로서 이미 반환했는데 joy->do()를 호출할 경우 에러 처리에 대해서 묻고 있는 겁니다..

답변 부탁드릴게요.
=========================================================================================
[내용 추가]
전제를 하나 빼먹었는데..제가 묻는 경우는 joy를 삭제한 후 나중에 또 사용하려 할 경우를 미리 예방하는 방법(delete를 하고 NULL을 습관적으로 넣는 방법 등..)이 아니라.. 즉 프로그래밍의 좋은 습관을 묻는게 아닙니다..;;
joy를 delete하게 되고 .. 아무런 조치를 하지 않은 채 나중에 delete한 joy의 메소드 joy->do()를 호출하게 되었을 때의 얘기입니다.
(즉 소위 좋은 프로그래밍을 하지 않았을 경우죠..)이러한 억지스런 상황을 굳이 묻는 이유는 .. 그것 까진 설명안할게요..^^;;
그리고 프로그래밍은 linux플랫폼에서 g++ 컴파일러를 사용할 경우입니다.

Rica님의 답변중에
'delete 이후에 항상 NULL을 대입할 것', '현재 쓰는 환경에서 유효하지 않은 객체에 대한 비 virtual 멤버 함수 호출이 허용될 것', 'virtual 멤버함수를 쓰지 않을 것' 이 세 가지 조건이 만족된다면 Human의 멤버함수 안에서 this를 검사함으로써 원하시는 바를 이룰 수 있겠습니다만.. 별로 권하고 싶지는 않네요.
라는 글 중에 Human의 멤버함수 안에서 this를 검사할 수 있다고 하셨는데..'현재 쓰는 환경에서 유효하지 않은 객체에 대한 비 virtual 멤버 함수 호출이 허용될 것'이라는 조건에서 이것이 허용되는 환경에는 무엇이 있죠?

그리고 제가 질문한건 delete 한후 NULL을 넣지 않았을 때의 얘기지만 그냥 지금 실험 좀 하다가 또 의문점이..
void Human::do()
{
if(this == NULL) return;
}
main(){
Human *joy = new Human("kim");
delete joy;
joy = NULL; // 질문에서 묻는 내용은 아니지만 여기다 NULL을 넣었을 경우
joy->do();
}
가 제대로 되는 이유는 무엇이죠? 혹 이것이 Rica님이 말한 세가지 조건이 만족해서 일까요? 흠.. 궁금하네요..
만약 joy = NULL을 하지 않고 do()에다 if(this == NULL) return; 코드를 넣었다면 아마 나중에 세그먼트 폴트가 날 수가 있겠죠..
그럼 결론은...어찌하든 joy에다 NULL을 넣지 않고는 방법이 없다..가 될 것 같네요...흠..

hokim의 이미지

delete 한 joy의 멤버함수를 호출하면 세그멘트 폴트가 발생하기 때문에 호출자체가 안됩니다.
차라리 joy의 주소값을 확인하는게 낫지 않을까요? new 연산자로 동적할당을 하게 되면
포인터는 0이 아닌 특정 주소값을 가지게 되다가 delete를 하게 되면 주소값을 0으로 가지게 되기때문에
동적할당된 객체가 존재하는지의 여부를 주소값을 통해서 알 수 있습니다.

Rica의 이미지

삭제한 객체에 대한 멤버함수의 호출까지는 됩니다. 그 뒤에 멤버변수에 액세스하려고 할 때 세그폴트가 나지요.
(MSVC는 그렇습니다만 표준에는 undefined로 명시되어 있을 것 같네요)

그리고 virtual 멤버함수의 경우에는 유효하지 않은 객체에 대한 호출은 할 수 없습니다. 바로 세그폴트 납니다.

"delete를 하게 되면 주소값을 0으로 가지게 되기때문에"
-> 그렇지는 않습니다. delete하면 포인터가 가리키는 메모리를 해제하지만 포인터 자체의 값은 변하지 않습니다. (따라서, 포인터가 더이상 유효한 메모리를 가리키지 않게 될 때 NULL을 대입해주는 것이 좋은 습관으로 권장됩니다)

'delete 이후에 항상 NULL을 대입할 것', '현재 쓰는 환경에서 유효하지 않은 객체에 대한 비 virtual 멤버 함수 호출이 허용될 것', 'virtual 멤버함수를 쓰지 않을 것' 이 세 가지 조건이 만족된다면 Human의 멤버함수 안에서 this를 검사함으로써 원하시는 바를 이룰 수 있겠습니다만.. 별로 권하고 싶지는 않네요.

'delete 이후에 항상 NULL을 대입할 것', '호출전에 포인터가 NULL인지 확인할 것' 이 두 가지 조건을 지키시는 게 훨씬 나을 것 같습니다.

ssehoony의 이미지

assert 에 대해 아시나요?
모르신다면 assert()를 조사해 보시고요.

delete 를 한후에는 항상 해당 포인터를 NULL 로 셋팅하도록 하세요.
매크로를 만드셔도 좋구요.

그럼 다음 함수에서 사용할 때는
assert 를 이용해서 사용하는 포인터들의 유효성을 검사하세요.

void print_int(int* pInt)
{
	assert(pInt != NULL);
 
	printf("%d\n", *pInt);
}
 
int main()
{
	int* pInt = new int(10);
 
	delete pInt;
	pInt = NULL;	
 
	print_int(pInt);
}

이런식으로요.
-1 을 리턴하는 것은 아니지만
assert() 를 이용하면 release 용으로 컴파일 했을 때도 좋고 디버깅하기도 좋습니다.
처음 적응 할땐 힘들지 모르지만, 함수의 모든 파라미터는 assert 로 한번 꼭 체크 해준다고
생각하고 코딩하시면 보다 견고한 프로그램을 작성하실 수 있을 듯 합니다.

IDNed의 이미지

표준대로 정확히 따르자면, delete한 후 객체의 사용은 그 자체부터가 undefined입니다.

따라서 모든 코드에 포터블한 표준 준수를 원하신다면 절대 불가능합니다.

실제적으로는 거의 대부분의 구현에서 다음과 같은 현상을 낳습니다.
1. Human::do()가 virtual과 전혀 관련 없을때
함수는 호출됩니다. 다만 이때 함수에게 넘겨진 this 값이 무효하므로 this를 이용한 멤버 접근(내부적으로 함수 안에서의 모든 멤버 접근은 this를 이용하는 것으로 구현되는 게 통상적입니다)시 Access Violation(POSIX에서는 99% SegFault?)이 일어날 수 있죠.
2. virtual과 관련시..
만일 do()가 virtual인 경우는 vtbl에서 do함수의 위치를 찾아야 합니다.
객체가 소멸되면 vtbl, vptr 다 소멸된 후이니 함수 위치 자체를 얻을 수 없고, 이로 인해 소멸된 객체에서 vptr을 찾으려는 접근 자체가 Access Violation입니다.

어쨌거나 중요한 점은 방법이 없다는 것입니다. -0-

thyoo의 이미지

vtbl은 Instance가 공유하므로 스태이틱(또는 글로벌)입니다.
___________________________________
Less is More (Robert Browning)

___________________________________
Less is More (Robert Browning)

체스맨의 이미지

방법이 없다고 생각하지는 않습니다.

예를 들어, 윈도우즈의 많은 API 함수들은 부적절한 핸들을 넣어주면 세그폴트가 나지 않고, 부적절한 핸들 오류를 지정합니다. GetLastError 로 확인할 수 있습니다.

한가지 방법은 signal 함수로 SIGSEGV 를 가로채서 오류를 복구하는 방법이 있을 것이고, 또 다른 방법은 빠른 탐색이 가능한 핸들 관리 자료 구조 ( 해쉬나 맵 )를 유지하는 방법이 있을 겁니다.

첫번째 방법은 완전한 오류 복구가 관건입니다.

두번째 방법은 빠른 탐색이 관건이 될 것이고, 특정한 인스턴스에 접근하는데 적절한 절차 (탐색 관련 )가 필요할 것입니다.

또 다른 방법이 있다면 알려주셨으면 합니다.

Orion Project : http://orionids.org

ssehoony의 이미지

다른 오버헤드를 증가시키면서까지도 해결할려고 한다면 당연히 좋은 방법이 있습니다.
윗분의 답중 해결방법이 없다는 것은 "좋은 해결방법"이 없다 정도로 해석하면 될듯 하네요.

쉬운 방법은 smart pointer 를 사용하면 됩니다.
동적할당한 객체를 모두 smart pointer를 사용해서 직접적인 delete 안하면 되지요.

그리고, 세그먼트폴트가 이미 발생한 상황이라면 데이터를 복구한다는 것은 거의 불가능이라고 할 수 있을 듯 한데요.
복구하기 위한 좋은 알고리즘이나 구조가 없을 듯 합니다.
이런게 가능하다면 정말 좋은텐데요.

Rica의 이미지

> Human의 멤버함수 안에서 this를 검사할 수 있다고 하셨는데..
> '현재 쓰는 환경에서 유효하지 않은 객체에 대한 비 virtual 멤버 함수 호출이 허용될 것'
> 이라는 조건에서 이것이 허용되는 환경에는 무엇이 있죠?

저는 g++와 MSVC를 써 보았는데 양쪽 다 허용되더군요. 표준에는 이 동작의 결과가 undefined라는 의미에서 써 둔 것입니다. :)

> 제대로 되는 이유는 무엇이죠? 혹 이것이 Rica님이 말한 세가지 조건이 만족해서 일까요? 흠.. 궁금하네요..

그런 셈이지요. 사실 비정적(non-static) 멤버함수 호출이란 건, 숨겨진 인자로 객체의 주소(this)를 넘기는 식으로 구현되어 있다고 봐도 되거든요. 호출하고자 하는 함수가 가상함수가 아닌 경우에는, 컴파일 시점에 어느 함수를 호출해야 할 지 알고 있으니까, vtbl의 참조를 하지 않고, 따라서 객체의 주소가 가리키는 곳을 참조하는 일 없이 주소값 그대로를 this로 넘기는 거죠. 따라서 호출이 무사히 되는 거고요.

댓글 달기

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
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.