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

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

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

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

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

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

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



답변

포인터를 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공유 소유권 또는 다른 스마트 포인터 구현을 살펴 보는 것이 좋습니다 .


답변

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

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

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


답변

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

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


답변

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

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

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

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

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

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

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

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

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

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

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


답변

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

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

Foo * p2 = p;

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

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


답변

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

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

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


답변

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

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