포인터를 삭제 한 후 포인터를 NULL로 만드는 것이 좋습니다.


150

먼저 말하고 스마트 포인터를 사용하면 걱정할 필요가 없습니다.

다음 코드의 문제점은 무엇입니까?

Foo * p = new Foo;
// (use p)
delete p;
p = NULL;

이것은 다른 질문 에 대한 답변과 의견으로 시작 되었습니다. 닐 버터 워스 (Neil Butterworth)의 한 의견은 몇 가지 공감대를 만들었습니다.

삭제 후 포인터를 NULL로 설정하는 것은 C ++에서 보편적 인 모범 사례가 아닙니다. 좋은 일이 있고 무의미하고 오류를 숨길 수있는 경우가 있습니다.

도움이되지 않는 상황이 많이 있습니다. 그러나 내 경험으로는 아프지 않습니다. 누군가 나를 밝힙니다.


6
@Andre : 기술적으로 정의되지 않았습니다. 일어날 가능성은 이전과 동일한 메모리에 액세스하지만 이제 다른 것으로 사용될 수 있습니다. 메모리를 두 번 삭제하면 찾기 어려운 방식으로 프로그램 실행이 망가질 수 있습니다. 그러나 delete포인터를 0으로 만드는 것이 좋은 이유 중 하나는 널 포인터에 안전합니다 .
David Thornley

5
@ André Pena, 그것은 정의되지 않았습니다. 종종 반복조차 할 수 없습니다. 디버깅 할 때 오류를보다 잘 보이게하고 더 반복 가능하도록 포인터를 NULL로 설정했습니다.
Mark Ransom

3
@ André : 아무도 모른다. 정의되지 않은 동작입니다. 액세스 위반으로 인해 충돌이 발생하거나 나머지 응용 프로그램에서 사용하는 메모리를 덮어 쓸 수 있습니다. 언어 표준은 어떤 일이 발생하는지 보증하지 않으므로 응용 프로그램이 발생한 후에는 신뢰할 수 없습니다. 그것은 수있는 핵 미사일을 발사하거나 하드 드라이브를 포맷했다. 앱의 메모리가 손상되거나 악마가 코에서 날아갈 수 있습니다. 모든 베팅이 종료되었습니다.
jalf

16
비행 악마는 버그가 아닌 기능입니다.
jball 2009

3
다른 질문은 C에 관한 것이고이 질문은 C ++에 관한 것이기 때문에이 질문은 중복되지 않습니다. 많은 대답은 C ++에서 사용할 수없는 스마트 포인터와 같은 것에 달려 있습니다.
Adrian McCarthy

답변:


86

포인터를 0으로 설정하면 (표준 C ++에서 "널 (null)"임, C에서 NULL로 정의한 NULL이 약간 다름) 이중 삭제시 충돌이 발생하지 않습니다.

다음을 고려하세요:

Foo* foo = 0; // Sets the pointer to 0 (C++ NULL)
delete foo; // Won't do anything

이므로:

Foo* foo = new Foo();
delete foo; // Deletes the object
delete foo; // Undefined behavior 

즉, 삭제 된 포인터를 0으로 설정하지 않으면 이중 삭제를 수행하면 문제가 발생합니다. 삭제 후 포인터를 0으로 설정하는 것에 대한 논쟁은 두 번 삭제 버그를 마스크하고 처리하지 않은 채로 두는 것입니다.

이중 삭제 버그가없는 것이 가장 좋지만 소유권 의미 및 객체 수명주기에 따라 실제로는 달성하기가 어려울 수 있습니다. UB보다 마스크 된 이중 삭제 버그를 선호합니다.

마지막으로, 객체 할당 관리에 대한 추가 정보 인 필자는 필요에 따라 std::unique_ptr엄격한 / 단일 소유권, std::shared_ptr공유 소유권 또는 다른 스마트 포인터 구현을 살펴 보는 것이 좋습니다 .


13
응용 프로그램이 항상 이중 삭제로 중단되는 것은 아닙니다. 두 삭제 사이에 발생하는 상황에 따라 어떤 일이 발생할 수 있습니다. 대부분 힙을 손상시키고 나중에 전혀 관련이없는 코드 조각으로 인해 충돌이 발생할 수 있습니다. segfault는 일반적으로 오류를 자동으로 무시하는 것보다 낫지 만,이 경우 segfault가 보장되지 않으며 의심스러운 유틸리티입니다.
Adam Rosenfield

28
여기서 문제는 이중 삭제가 있다는 것입니다. 포인터를 NULL로 만들면 수정하지 않거나 더 안전하게 만들지 않습니다. 1 년 후 다시 돌아오고 foo가 삭제 된 것을 본 메인 이너. 그는 불행히도 포인터를 다시 사용할 수 있다고 생각하여 두 번째 삭제를 놓칠 수 있습니다 (같은 기능이 아닐 수도 있음). 이제 포인터의 재사용이 두 번째 삭제에 의해 폐기됩니다. 두 번째 삭제 후의 액세스는 이제 중대한 문제입니다.
Martin York

11
포인터가 NULL이중 삭제 버그를 가릴 수 있도록 설정하는 것이 사실 입니다. (일부 사람은이 마스크가 실제로 해결책이라고 생각할 수도 있습니다. 문제의 근본 원인이되지 않기 때문에 그다지 좋지는 않습니다.) 그러나 마스크를 NULL 마스크로 설정하지 않으면 먼 거리 (FAR!) 데이터를 삭제 한 후 액세스하는 일반적인 문제
Adrian McCarthy

AFAIK는, 표준 : : auto_ptr은이 곧 C ++ 표준에서 사용되지 않습니다
rafak

더 이상 사용되지 않는다고 말하지는 않지만 아이디어가 사라진 것처럼 들립니다. 오히려 이동 의미론으로 수행하려는 작업을 수행 unique_ptr하는 로 대체되고 auto_ptr있습니다.
GManNickG 2009

55

확실히 지적한 내용을 삭제 한 후 포인터를 NULL로 설정하면 아프지 않지만보다 근본적인 문제에 대한 약간의 반창고입니다. 왜 처음에 포인터를 사용합니까? 두 가지 일반적인 이유를 볼 수 있습니다.

  • 단순히 힙에 할당 된 것을 원했습니다. 이 경우 RAII 객체로 감싸는 것이 훨씬 안전하고 깨끗했을 것입니다. 더 이상 객체가 필요하지 않으면 RAII 객체의 범위를 종료하십시오. 그것이 std::vector작동 하는 방식이며, 할당 해제 된 메모리에 포인터를 실수로 남겨 두는 문제를 해결합니다. 포인터가 없습니다.
  • 또는 복잡한 공유 소유권 의미론을 원했을 수도 있습니다. 에서 반환 된 포인터 는 호출 된 포인터 new와 같지 않을 수 있습니다 delete. 그 동안 여러 개체가 동시에 개체를 사용했을 수 있습니다. 이 경우 공유 포인터 또는 이와 유사한 것이 바람직했을 것입니다.

제 경험에 따르면 사용자 코드에 포인터를두면 잘못하고 있습니다. 처음에는 쓰레기를 가리 키도록 포인터가 없어야합니다. 왜 그 유효성을 보장해야 할 책임이 없는가? 뾰족한 물체가 스코프를 끝낼 때 왜 그 범위가 끝나지 않습니까?


15
그래서 당신은 처음에는 생생한 포인터가되어서는 안된다는 주장을하고 있으며, 그 포인터와 관련된 어떤 것도 "좋은 습관"이라는 용어로 축복받지 말아야합니까? 그럴 수 있지.
Mark Ransom

7
글쎄요. 나는 원시 포인터와 관련된 것은 좋은 습관이라고 할 수 없다고 말하지 않을 것입니다 . 규칙이 아니라 예외입니다. 일반적으로 포인터의 존재는 더 깊은 수준에서 문제가 있음을 나타내는 지표입니다.
jalf December

3
그러나 즉각적인 질문에 대답하기 위해 아니요, 포인터를 null로 설정하면 어떻게 오류 가 발생할 수 있는지 알 수 없습니다 .
jalf

7
동의하지 않습니다. 포인터를 사용하는 것이 좋은 경우가 있습니다. 예를 들어, 스택에는 2 개의 변수가 있으며 그중 하나를 선택하려고합니다. 또는 선택적 변수를 함수에 전달하려고합니다. 나는 당신과 함께 원시 포인터를 사용해서는 안된다고 말합니다 new.
rlbond 2009

4
포인터가 범위 밖으로 사라 때, 나는 표시되지 않습니다 아무것도 그것을 다루는이나 사람의 힘이 필요합니다.
jalf

43

더 나은 모범 사례가 있습니다. 가능하면 변수 범위를 끝내십시오!

{
    Foo* pFoo = new Foo;
    // use pFoo
    delete pFoo;
}

17
예, RAII는 당신의 친구입니다. 클래스로 감싸면 훨씬 간단 해집니다. 또는 STL을 사용하여 메모리를 직접 처리하지 마십시오!
Brian

24
그렇습니다, 그것이 최선의 선택입니다. 그래도 질문에 대답하지 않습니다.
Mark Ransom

3
이것은 함수 범위 기간을 사용한 부산물 인 것으로 보이며 실제로 문제를 해결하지는 않습니다. 포인터를 사용할 때 일반적으로 여러 레이어의 복사본을 전달하면 문제를 해결하기위한 방법이 실제로 의미가 없습니다. 좋은 디자인이 오류를 격리하는 데 도움이된다는 데 동의하지만, 그 방법이 그 목적을위한 주요 수단이라고 생각하지 않습니다.
San Jacinto

2
생각해보십시오.이 작업을 수행 할 수 있다면 힙을 잊어 버리고 모든 메모리를 스택에서 가져 오는 것이 어떻습니까?
San Jacinto

4
내 예는 의도적으로 최소입니다. 예를 들어, new 대신에 factory에 의해 객체가 생성 될 수 있으며,이 경우 스택으로 이동할 수 없습니다. 또는 범위의 시작 부분에서 생성되지 않았지만 일부 구조에 있습니다. 내가 설명하고있는 것은이 접근법이 컴파일 타임에 포인터의 오용을 발견하는 반면, NULL이면 런타임에 오용을 발견 할 것이다 .
Don Neufeld

31

나는 그것이 가리키는 객체를 삭제 한 후에 항상 포인터를 NULL(now nullptr) 로 설정했습니다 .

  1. 사용 가능한 메모리에 대한 많은 참조를 포착하는 데 도움이 될 수 있습니다 (널 포인터의 참조에 플랫폼 결함이 있다고 가정).

  2. 예를 들어 포인터 사본이 놓여 있으면 사용 가능한 메모리에 대한 모든 참조를 포착하지 않습니다. 그러나 일부는 없음보다 낫습니다.

  3. 이중 삭제를 숨기지 만 이미 사용 가능한 메모리에 액세스하는 것보다 훨씬 덜 일반적입니다.

  4. 대부분의 경우 컴파일러는이를 최적화하려고합니다. 따라서 그것이 불필요하다는 주장은 설득력이 없습니다.

  5. 이미 RAII를 사용하고 있다면 delete코드에 s 가 많지 않으므로 추가 할당으로 인해 혼란이 발생한다는 주장은 설득력이 없습니다.

  6. 디버깅 할 때 오래된 포인터가 아닌 null 값을 보는 것이 종종 편리합니다.

  7. 그래도 문제가 해결되지 않으면 대신 스마트 포인터 또는 참조를 사용하십시오.

또한 리소스가 해제 될 때 (일반적으로 리소스를 캡슐화하기 위해 작성된 RAII 래퍼의 소멸자에만 있음) 다른 유형의 리소스 핸들을 리소스 없음 값으로 설정합니다.

나는 큰 (9 백만 개의 진술) 상용 제품 (주로 C)을 작업했다. 어느 시점에서, 우리는 메모리가 해제 될 때 매크로 매직을 사용하여 포인터를 널 아웃했습니다. 이것은 즉시 수정 된 많은 숨어있는 버그를 즉시 노출 시켰습니다. 내가 기억할 수있는 한, 더블 프리 버그는 없었습니다.

업데이트 : Microsoft는 이것이 보안을위한 좋은 방법이라고 믿고 SDL 정책에 권장합니다. / SDL 옵션을 사용하여 컴파일하면 MSVC ++ 11은 삭제 된 포인터를 자동으로 (많은 상황에서) 스톰 핑합니다 .


12

먼저,이 주제와 밀접하게 관련된 주제에 대한 기존의 질문이 많이 있습니다. 예를 들어 delete가 포인터를 NULL로 설정하지 않는 이유는 무엇입니까? .

코드에서 발생하는 문제 (p 사용). 예를 들어, 어딘가에 다음과 같은 코드가있는 경우 :

Foo * p2 = p;

그런 다음 p2를 걱정으로 설정하면 p를 NULL로 설정하면 거의 달성되지 않습니다.

이것은 포인터를 NULL로 설정하는 것이 항상 의미가 없다고 말하는 것은 아닙니다. 예를 들어, p가 수명이 p를 포함하는 클래스와 정확히 같지 않은 자원을 가리키는 멤버 변수 인 경우 p를 NULL로 설정하면 자원의 유무를 나타내는 유용한 방법이 될 수 있습니다.


1
도움이되지 않을 때가 있지만 동의 할 때 적극적으로 해를 끼칠 수 있음에 동의합니다. 당신의 의도입니까, 아니면 잘못 읽었습니까?
Mark Ransom

1
포인터의 사본이 있는지 여부는 포인터 변수를 NULL로 설정 해야하는지 여부와 관련이 없습니다. 이 설정을 NULL로 설정하는 것은 저녁 식사를 마친 후에 설거지를 청소하는 것과 같은 근거에서 좋은 습관입니다. 코드가 가질 수있는 모든 버그에 대한 보호 책은 아니지만 코드의 건강을 향상시킵니다.
Franci Penov

2
@Franci 많은 사람들이 당신과 동의하지 않는 것 같습니다. 원본을 삭제 한 후 사본을 사용하려고하면 사본이 있는지 여부가 중요합니다.

3
프랜차, 차이가있어 다시 사용하기 때문에 설거지를합니다. 포인터를 삭제 한 후에는 포인터가 필요하지 않습니다. 마지막으로해야합니다. 더 나은 방법은 상황을 완전히 피하는 것입니다.
GManNickG 2009

1
변수를 재사용 할 수 있지만 더 이상 방어 적 프로그래밍이 아닙니다. 바로 문제에 대한 솔루션을 설계 한 방법입니다. OP는이 방어 스타일이 우리가 노력해야 할 것인지 아닌지에 대해 논의하고있다. 이상적으로는 귀하의 질문에 그렇습니다! 포인터를 삭제 한 후에는 포인터를 사용하지 마십시오!
GManNickG 2009

7

뒤에 더 많은 코드가 있으면 delete예. 생성자에서 또는 메소드 나 함수의 끝에서 포인터가 삭제되면 아니오.

이 비유의 요점은 프로그래머가 런타임 동안 객체가 이미 삭제되었음을 상기시키는 것입니다.

더 나은 방법은 대상 객체를 자동으로 삭제하는 스마트 포인터 (공유 또는 범위)를 사용하는 것입니다.


모든 사람 (원래의 질문자를 포함하여)은 똑똑한 포인터가 갈 길에 동의합니다. 코드가 진화합니다. 처음 삭제 한 후 삭제 후 더 많은 코드가 없을 수 있지만 시간이 지남에 따라 변경 될 수 있습니다. 과제를 제출하면 그 시점에 도움이됩니다 (그리고 그 동안 거의 비용이 들지 않습니다).
Adrian McCarthy

3

다른 사람들이 말했듯이, delete ptr; ptr = 0;악마가 코에서 날아 가지 않게 할 것입니다. 그러나 ptr일종의 플래그로 사용하도록 권장합니다 . 코드가 지저분 해지고 delete포인터가로 설정됩니다 NULL. 다음 단계는 if (arg == NULL) return;실수로 NULL포인터를 사용하지 않도록 코드 를 분산 시키는 것 입니다. 점검 NULL이 오브젝트 또는 프로그램의 상태를 점검하는 기본 수단이 되면 문제점이 발생합니다 .

포인터를 플래그로 사용하는 것에 대한 코드 냄새가 있지만 확실하지 않습니다.


9
포인터를 플래그로 사용하면 아무런 문제가 없습니다. 포인터를 사용하고 NULL있고 유효한 값이 아닌 경우 참조를 대신 사용해야합니다.
Adrian McCarthy

2

질문을 약간 바꾸겠습니다.

초기화되지 않은 포인터를 사용 하시겠습니까? 당신은 NULL로 설정하지 않았거나 그것이 가리키는 메모리를 할당하지 않은 것을 알고 있습니까?

포인터를 NULL로 설정하는 단계를 건너 뛸 수있는 두 가지 시나리오가 있습니다.

  • 포인터 변수가 즉시 범위를 벗어남
  • 포인터의 의미를 오버로드했으며 그 값을 메모리 포인터뿐만 아니라 키 또는 원시 값으로 사용하고 있습니다. 그러나이 방법은 다른 문제로 어려움을 겪고 있습니다.

한편 포인터를 NULL로 설정하면 오류가 숨길 수 있다고 주장하면 다른 버그를 숨길 수 있으므로 버그를 수정해서는 안된다는 주장과 같은 소리가 들립니다. 포인터가 NULL로 설정되지 않은 경우 표시 될 수있는 유일한 버그는 포인터를 사용하려고하는 버그 일 것입니다. 그러나 NULL로 설정하면 실제로 사용 가능한 메모리와 함께 사용하는 경우 표시되는 것과 정확히 동일한 버그가 발생합니까?


(A) "버그를 수정해서는 안된다고 주장하는 것처럼 들린다"NULL 포인터를 설정하지 않는 것은 버그가 아니다. (B) "그러나 NULL로 설정하면 실제로는 정확히 같은 버그가 발생합니다"아니오 . NULL로 설정하면 double delete가 숨겨 집니다. (C) 요약 : NULL로 설정하면 이중 삭제가 숨겨 지지만 오래된 참조는 노출됩니다. NULL로 설정하지 않으면 오래된 참조를 숨길 수 있지만 이중 삭제는 노출됩니다. 양측은 실제 문제가 오래된 참조 및 이중 삭제를 수정하는 것임을 동의합니다.
Mooing Duck

2

포인터를 삭제 한 후 포인터를 NULL로 설정하거나 설정하지 않는 다른 제약 조건이 없다면 ( Neil Butterworth 가 그러한 제약 조건 중 하나를 언급했습니다 ) 내 개인적인 취향은 그대로 두는 것입니다.

나에게있어 질문은 "이것이 좋은 생각입니까?"가 아닙니다. 그러나 "이 작업을 수행하면 어떤 행동을 막거나 성공할 수 있습니까?" 예를 들어, 다른 코드에서 포인터를 더 이상 사용할 수 없다는 것을 알 수 있다면 다른 코드가 해제 된 포인터를 해제하려고 시도하는 이유는 무엇입니까? 일반적으로 버그입니다.

또한 사후 디버깅을 방해 할뿐만 아니라 필요 이상으로 많은 작업을 수행합니다. 메모리를 필요로하지 않은 채로 메모리를 적게 접촉할수록 왜 충돌이 발생했는지 쉽게 파악할 수 있습니다. 여러 번 나는 메모리가 버그를 진단하고 고치기 위해 특정 버그가 발생했을 때와 비슷한 상태라는 사실에 의존했다.


2

삭제 후 명시 적으로 null은 독자에게 포인터가 개념적인 선택 사항 임을 나타냅니다 . 내가 그 일을하는 것을 보았을 때, 소스의 모든 곳에서 포인터가 사용된다는 것이 걱정되기 시작합니다. NULL에 대해 먼저 테스트해야합니다.

그것이 실제로 의미하는 바라면 boost :: optional 과 같은 것을 사용하여 소스에서 명시 적으로 만드는 것이 좋습니다.

optional<Foo*> p (new Foo);
// (use p.get(), but must test p for truth first!...)
delete p.get();
p = optional<Foo*>();

그러나 사람들이 포인터가 "나쁘게 사라졌다"는 사실을 정말로 알고 싶다면, 가장 좋은 방법은 범위를 벗어나는 것입니다. 그런 다음 런타임에 잘못된 역 참조가 발생하지 않도록 컴파일러를 사용하고 있습니다.

그것은 모든 C ++ 욕탕에있는 아기입니다. :)


2

적절한 오류 검사 기능이있는 잘 구조화 된 프로그램 에서는 널을 할당 하지 않을 이유가 없습니다 . 0이 맥락에서 보편적으로 인정되는 잘못된 가치로 독립 열심히 실패하고 곧 실패하십시오.

할당에 대한 많은 주장 은 버그를 숨기거나 제어 흐름을 복잡하게 만들 수 있다고0 제안합니다 . 근본적으로, 이는 업스트림 오류 (오류가 아니라 죄송합니다) 또는 프로그래머를위한 또 다른 오류입니다. 아마도 프로그램 흐름이 너무 복잡해 졌다는 표시 일 수도 있습니다.

프로그래머가 특별한 값으로 null 일 수있는 포인터의 사용을 도입하고 그 주위에 필요한 모든 닷지를 쓰려면 의도적으로 도입 한 복잡한 문제입니다. 검역소가 양호할수록 오용 사례가 더 빨리 발견되며 다른 프로그램으로 전파 될 가능성이 줄어 듭니다.

이러한 경우를 피하기 위해 C ++ 기능을 사용하여 잘 구성된 프로그램을 설계 할 수 있습니다. 참조를 사용하거나 "널 또는 유효하지 않은 인수를 전달 / 사용하는 것은 오류입니다"라고 말할 수 있습니다. 스마트 포인터와 같은 컨테이너에도 동일하게 적용 할 수 있습니다. 일관되고 올바른 동작을 늘리면 이러한 버그가 사라지지 않습니다.

거기에서 널 포인터가 존재할 수있는 (또는 허용되는) 범위와 컨텍스트는 매우 제한적입니다.

그렇지 않은 포인터에도 동일하게 적용될 수 있습니다 const. 포인터의 값을 따르는 것은 범위가 너무 작고 부적절한 사용이 확인되고 잘 정의되어 있기 때문에 사소한 것입니다. 빠른 읽기 후에 툴셋과 엔지니어가 프로그램을 수행 할 수 없거나 부적절한 오류 확인 또는 일관되지 않은 / 프로그램 흐름이있는 경우 다른 큰 문제가있는 것입니다.

마지막으로 컴파일러와 환경에는 오류 (스 크라이 빙), 사용 가능한 메모리에 대한 액세스 감지 및 기타 관련 UB를 포착하려는 시간에 대한 보호 장치가있을 수 있습니다. 기존 프로그램에 영향을주지 않고 유사한 진단 프로그램을 프로그램에 도입 할 수도 있습니다.


1

이미 질문에 넣은 내용을 확장하겠습니다.

다음은 글 머리 기호 형식으로 질문에 넣은 내용입니다.


삭제 후 포인터를 NULL로 설정하는 것은 C ++에서 보편적 인 모범 사례가 아닙니다. 다음과 같은 경우가 있습니다.

  • 하는 것이 좋습니다
  • 무의미하고 오류를 숨길 수있는 시간.

그러나 이것이 나쁜 경우 는 없습니다 ! 당신은 할 수 없습니다 명시 적으로 널링하여 더 많은 버그를 소개합니다, 당신은하지 않습니다 누출 메모리를, 당신은되지 않습니다 정의되지 않은 동작이 원인 일 수 있습니다.

따라서 의심스러운 경우 null로 설정하십시오.

당신이 명시 적으로 포인터를 null로 설정해야한다고 생각한다면, 나 에게이 방법을 충분히 분할하지 않은 것처럼 들리고, "추출 방법"이라는 리팩토링 접근법을 살펴보고 메소드를 별도의 부품.


나는 "이것이 나쁠 때가 없다"는 것에 동의하지 않습니다. 이 관용구가 도입하는 절단기의 양을 고려하십시오. 모든 단위에 헤더가 포함되어있어 무언가를 삭제하고 모든 삭제 위치가 약간 덜 간단 해집니다.
GManNickG 2009

나쁜시기가 있습니다. 삭제하지 말아야 할 때 삭제 된 null 포인터를 역 참조하려고하면 충돌하지 않으며 해당 버그가 '숨겨져 있습니다'. 그들이 여전히 임의의 값을 가진 삭제 된 포인터를 역 참조하면, 당신은 눈에 띄고 버그를 쉽게 볼 수 있습니다.
Carson Myers

@ 카슨 : 내 경험은 정 반대입니다 : nullptr을 역 참조하면 거의 모든 방법으로 응용 프로그램이 중단되고 디버거가 잡을 수 있습니다. 매달려있는 포인터를 참조하면 일반적으로 문제가 발생하지 않지만 종종 잘못된 결과 또는 다른 오류가 발생합니다.
MikeMB

@MikeMB 나는 완전히이에 대한 나의 견해는 과거 ~ 6.5 년 동안 실질적으로 변경, 동의
카슨 마이어스

프로그래머라는 관점에서 우리는 6 ~ 7 년 전에 다른 사람이었습니다
.

1

예.

그것이 할 수있는 유일한 "유해"는 비 효율성 (불필요한 저장 작업)을 프로그램에 도입하는 것입니다. 그러나이 오버 헤드는 대부분의 경우 메모리 블록 할당 및 해제 비용과 관련하여 중요하지 않습니다.

당신이 그것을하지 않으면, 당신은 것입니다 성가신 포인터 derefernce 버그 어느 날이있다.

나는 항상 매크로를 삭제에 사용합니다.

#define SAFEDELETE(ptr) { delete(ptr); ptr = NULL; }

(그리고 배열, free (), 해제 핸들과 비슷 함)

호출 코드의 포인터를 참조하는 "self delete"메소드를 작성할 수도 있으므로 호출 코드의 포인터를 NULL로 강제합니다. 예를 들어 많은 객체의 하위 트리를 삭제하려면

static void TreeItem::DeleteSubtree(TreeItem *&rootObject)
{
    if (rootObject == NULL)
        return;

    rootObject->UnlinkFromParent();

    for (int i = 0; i < numChildren)
       DeleteSubtree(rootObject->child[i]);

    delete rootObject;
    rootObject = NULL;
}

편집하다

예, 이러한 기술은 매크로 사용에 대한 일부 규칙을 위반합니다 (예, 템플릿과 동일한 결과를 얻을 수 있습니다). 그러나 수년 동안 사용하여 죽은 메모리에 액세스 한 적이 없었 습니다. 직면 할 수있는 문제를 디버깅하는 데 가장 많은 시간이 소요됩니다. 실제로 수년 동안 그들은 내가 소개 한 모든 팀에서 whjole 클래스의 버그를 효과적으로 제거했습니다.

위의 방법을 구현할 수있는 많은 방법이 있습니다. 발신자의 포인터를 NULL로하지 않는 메모리를 해제하는 수단을 제공하는 대신 사람들이 객체를 삭제하면 포인터를 NULL로 강제하는 아이디어를 설명하려고합니다. .

물론, 위의 예는 자동 포인터를 향한 단계 일뿐입니다. OP가 자동 포인터를 사용하지 않는 경우에 대해 구체적으로 요구했기 때문에 제안하지 않았습니다.


2
매크로는 특히 정상적인 기능처럼 보일 때 나쁜 생각입니다. 이렇게하려면 템플릿 기능을 사용하십시오.

2
와우 ... 포인터 anObject->Delete(anObject)무효화 와 같은 것을 본 적이 없습니다 anObject. 그냥 무섭다. TreeItem::Delete(anObject)최소한 강제로 수행 할 수 있도록 정적 메소드를 작성해야합니다 .
D.Shawley

죄송합니다. 올바른 대문자 "이것은 매크로입니다"형식을 사용하는 대신 함수로 입력했습니다. 수정했습니다. 나 자신을 더 잘 설명 할 수있는 주석도 추가했습니다.
Jason Williams

그리고 당신 말이 맞아요, 내 빨리 사라진 예는 쓰레기였습니다! 고정 :-). 포인터를 삭제하는 모든 코드는 다른 사람 (호출자)이 해당 포인터를 소유하더라도 포인터를 NULL로 설정해야합니다. 따라서 항상 포인터에 대한 참조를 전달하여 삭제 시점에서 NULL로 강제 설정할 수 있습니다.
Jason Williams

1

"행복해야 할 때와 무의미하고 오류를 숨길 수있는 때가있다"

두 가지 문제가 있습니다. 간단한 코드 :

delete myObj;
myobj = 0

멀티 스레드 환경에서 라이너가됩니다.

lock(myObjMutex); 
delete myObj;
myobj = 0
unlock(myObjMutex);

Don Neufeld의 "모범 사례"가 항상 적용되는 것은 아닙니다. 예를 들어 한 자동차 프로젝트에서 소멸자에서도 포인터를 0으로 설정해야했습니다. 안전에 중요한 소프트웨어에서는 이러한 규칙이 드물지 않다고 생각할 수 있습니다. 코드에서 각 포인터 사용에 대해 팀 / 코드 검사기를 설득하는 것보다이 포인터를 따르는 것이 더 쉽고 현명합니다.

또 다른 위험은 예외 사용 코드에서이 기술에 의존하는 것입니다.

try{  
   delete myObj; //exception in destructor
   myObj=0
}
catch
{
   //myObj=0; <- possibly resource-leak
}

if (myObj)
  // use myObj <--undefined behaviour

이러한 코드에서 리소스 누수를 생성하고 문제를 연기하거나 프로세스가 중단됩니다.

따라서이 두 가지 문제가 저의 머리를 통해 자연스럽게 발생하기 때문에 (Herb Sutter는 더 많은 것을 말해 줄 것입니다) "스마트 포인터 사용을 피하고 정상적인 포인터로 안전하게 작업하는 방법"과 같은 모든 질문을 쓸모없는 것으로 만듭니다.


4 라이너가 3 라이너보다 훨씬 더 복잡한 방법 (어쨌든 lock_guards를 사용해야 함)과 소멸자가 당신을 던지면 어쨌든 어려움을 겪고 있습니다.
MikeMB

이 답변을 처음 보았을 때 소멸자에서 포인터를 null로 지정하려는 이유를 이해하지 못했지만 이제는 포인터를 소유 한 객체 가 삭제 된 후 사용되는 경우입니다.
Mark Ransom


0

포인터를 다시 사용하기 전에 (재 참조, 함수에 전달하는 등) 포인터를 다시 할당하려는 경우 포인터 NULL을 만드는 것은 추가 작업입니다. 그러나 다시 사용하기 전에 재 할당되는지 여부를 확신 할 수없는 경우 NULL로 설정하는 것이 좋습니다.

많은 사람들이 말했듯이 스마트 포인터를 사용하는 것이 훨씬 쉽습니다.

편집 : Thomas Matthews 가이 이전 답변 에서 말했듯 포인터가 소멸자에서 삭제되면 객체가 이미 파괴되어 다시 사용되지 않으므로 NULL을 할당 할 필요가 없습니다.


0

단일 함수 (또는 객체)에서 재사용 하는 합법적 인 시나리오 가있는 드문 경우에 유용하게 삭제 된 후 포인터를 NULL로 설정하는 것을 상상할 수 있습니다 . 그렇지 않으면 의미가 없습니다-포인터는 존재하는 한 의미있는 것을 가리켜 야합니다-기간.


0

코드가 응용 프로그램의 성능에서 가장 중요한 부분이 아닌 경우 코드를 단순하게 유지하고 shared_ptr을 사용하십시오.

shared_ptr<Foo> p(new Foo);
//No more need to call delete

참조 카운팅을 수행하며 스레드로부터 안전합니다. tr1 (std :: tr1 네임 스페이스, #include <memory>)에서 찾거나 컴파일러에서 제공하지 않으면 부스트에서 가져옵니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.