운영자 삭제에서 소멸자가 호출되지 않는 이유는 무엇입니까?


16

나는 그것 ::delete의 수업 을 요청 operator delete했습니다. 그러나 소멸자는 호출되지 않습니다.

과부하 된 클래스 MyClass를 정의했습니다 operator delete. 글로벌 operator delete도 과부하 상태입니다. 오버로드 operator delete의이 MyClassv로드 세계를 호출합니다 operator delete.

class MyClass
{
public:
    MyClass() { printf("Constructing MyClass...\n"); }
    virtual ~MyClass() { printf("Destroying MyClass...\n"); }

    void* operator new(size_t size)
    {
        printf("Newing MyClass...\n");
        void* p = ::new MyClass();
        printf("End of newing MyClass...\n");
        return p;
    }

    void operator delete(void* p)
    {
        printf("Deleting MyClass...\n");
        ::delete p;    // Why is the destructor not called here?
        printf("End of deleting MyClass...\n");
    }
};

void* operator new(size_t size)
{
    printf("Global newing...\n");
    return malloc(size);
}

void operator delete(void* p)
{
    printf("Global deleting...\n");
    free(p);
}

int main(int argc, char** argv)
{
    MyClass* myClass = new MyClass();
    delete myClass;

    return EXIT_SUCCESS;
}

출력은 다음과 같습니다.

Newing MyClass...
Global newing...
Constructing MyClass...
End of newing MyClass...
Constructing MyClass...
Destroying MyClass...
Deleting MyClass...
Global deleting...
End of deleting MyClass...

실제 :

의 오버로드 operator delete를 호출하기 전에 소멸자에 대한 호출이 하나만 있습니다 MyClass.

예상 :

소멸자를 두 번 호출합니다. 오버로드 된 호출하기 전에 하나 operator delete의 MyClass. 다른 하나는 글로벌 전화하기 전에 operator delete.


6
MyClass::operator new()(최소한) size바이트 의 원시 메모리를 할당해야 합니다. 의 인스턴스를 완전히 구성하려고 시도해서는 안됩니다 MyClass. 의 생성자는 다음 MyClass에 실행 MyClass::operator new()됩니다. 그런 다음 deletein 식은 main()소멸자를 호출하고 소멸자를 다시 호출하지 않고 메모리를 해제합니다. ::delete p표현은 객체의 유형에 대한 정보가 없습니다 p이후에 포인트를 pA는 void *그래서 소멸자를 호출 할 수 없습니다.
— Peter


2
이미 귀하에게 제공된 답변은 정확하지만 궁금합니다. 왜 new를 무시하고 삭제하려고합니까? 일반적인 사용 사례는 사용자 지정 메모리 관리 (GC, 기본 malloc ()에서 제공되지 않는 메모리 등)를 구현하는 것입니다. 어쩌면 달성하려는 것에 잘못된 도구를 사용하고있을 것입니다.
— noamtm

2
::delete p;유형 *p이 삭제되는 객체의 유형과 같지 않기 때문에 정의되지 않은 동작이 발생 합니다 (가상 소멸자가있는 기본 클래스도 아님)
— MM

@MM 주요 컴파일러는 대부분 경고 만하므로 void*피연산자가 명시 적으로 잘못 구성되어 있음을 알지 못했습니다 . [expr.delete] / 1 : " 피연산자는 객체 유형 또는 클래스 유형에 대한 포인터 여야합니다. [...] 이는 void가 객체 유형이 아니기 때문에 void 유형의 포인터를 사용하여 객체를 삭제할 수 없음을 의미합니다 . * "@OP 답변을 수정했습니다.
— 호두

답변:


17

당신은 오용 operator new하고 operator delete있습니다. 이 연산자는 할당 및 할당 해제 기능입니다. 객체를 생성하거나 파괴 할 책임이 없습니다. 객체를 배치 할 메모리를 제공하는 것만 담당합니다.

이 함수의 글로벌 버전은 ::operator new및 ::operator delete입니다. ::new와 ::delete같은 새로운 / 삭제-표현식입니다 new/ delete그에서,와는 다른 ::new과 ::delete의지 바이 패스 클래스 별 operator new/ operator delete과부하.

new / delete-expressions는 생성 / 파괴 및 할당 / 할당 해제 (적절한 호출 operator new또는 operator delete시공 전 또는 파기 후).

당신의 과부하가 할당 / 해제 부분에 대해서만 책임이 있기 때문에, 호출해야 ::operator new하고 ::operator delete대신 ::new하고 ::delete.

delete에서이 delete myClass;소멸자를 호출 할 책임이있다.

::delete p;때문에 소멸자를 호출하지 않습니다 p유형이 void*때문에 표현이 전화를 무엇 소멸자 알 수 없다. delete-expression에 as 피연산자를 ::operator delete사용하는 것은 좋지 않지만 (아래 편집 참조) 메모리를 할당 해제 하기 위해 교체 된 것을 호출합니다 .void*

::new MyClass();교체 ::operator new된 메모리를 호출하여 메모리를 할당하고 그 안에 객체를 생성합니다. 이 객체에 대한 포인터는 void*에서 new-expression에 대해 반환 MyClass* myClass = new MyClass();됩니다. 그러면 이 메모리에 다른 객체가 생성 되어 소멸자를 호출하지 않고 이전 객체의 수명이 끝납니다.


편집하다:

질문에 대한 @MM의 의견 덕분에 void*피연산자 ::delete가 실제로 잘못 형성 되었음을 깨달았습니다 . ( [expr.delete] / 1 ) 그러나 주요 컴파일러는 오류가 아니라 이것에 대해서만 경고하기로 결정한 것 같습니다. 그것을 사용, 잘못 형성되었다 전에 ::deleteA의 void*한 이미 정의되지 않은 동작, 볼 이 질문을 .

따라서 프로그램이 잘못 구성되어 있으며 코드가 여전히 컴파일 할 수 있다면 위에서 설명한 내용을 실제로 수행한다고 보장 할 수 없습니다.


@SanderDeDycker가 그의 답변 아래에 지적한 바와 같이, MyClass객체의 소멸자를 호출하지 않고 이미 객체를 포함하고있는 메모리에 다른 객체를 구성함으로써 먼저 [basic.life] / 5 를 위반 하므로 정의되지 않은 동작이 있습니다 . 프로그램은 소멸자의 부작용에 달려 있습니다. 이 경우 printf소멸자 의 진술은 부작용이 있습니다.


오용은 이러한 작업자의 작동 방식을 확인하기위한 것입니다. 그러나 답변 주셔서 감사합니다. 내 문제를 해결하는 유일한 대답 인 것 같습니다.
— 5

13

클래스 별 과부하가 잘못 수행되었습니다. 이것은 출력에서 ​​볼 수 있습니다 : 생성자가 두 번 호출됩니다!

클래스 별 operator new에서 글로벌 연산자를 직접 호출하십시오.

return ::operator new(size);

마찬가지로 class-specific operator delete에서 다음을 수행하십시오.

::operator delete(p);

자세한 내용은 operator new참조 페이지를 참조하십시오.


나는 new 연산자에서 :: new를 호출하여 생성자가 두 번 호출된다는 것을 알고 있습니다. 내 질문은 연산자 delete에서 :: delete를 호출 할 때 소멸자가 호출되지 않는 이유는 무엇입니까?
— 5

1
@expinc : 소멸자를 먼저 호출하지 않고 의도적으로 생성자를 두 번째로 호출 하는 것은 정말 나쁜 생각입니다. 사소하지 않은 소멸자 (귀하와 같은)의 경우 정의되지 않은 행동 영역으로 향하고 있습니다 (소멸자의 부작용에 의존하는 경우). [basic.life] §5 . 이러지 마
— Sander De Dycker

1

CPP 참조를 참조 하십시오 .

operator delete, operator delete[]

일치하여 이전에 할당 된 스토리지를 할당 해제합니다 operator new. 이러한 할당 해제 함수는 동적 저장 기간을 사용하여 객체를 소멸 (또는 구성하지 못한) 한 후 메모리를 할당 해제하기 위해 delete-expression 및 new-expression에 의해 호출됩니다. 정규 함수 호출 구문을 사용하여 호출 할 수도 있습니다.

삭제 (및 신규)는 '메모리 관리'부분에 대해서만 책임이 있습니다.

따라서 객체의 인스턴스를 정리하기 위해 소멸자가 한 번만 호출되는 것이 분명합니다. 두 번 호출되면 모든 소멸자가 이미 호출되었는지 확인해야합니다.


1
인스턴스를 삭제 한 후 소멸자를 보여주는 자체 로그에서 볼 수 있듯이 delete 연산자는 여전히 암시 적으로 소멸자를 호출해야합니다. 여기서 문제는 클래스 삭제 재정의가 :: delete를 호출하여 전역 삭제 재정의로 이어진다는 것입니다. 이 전역 삭제 재정의는 단순히 메모리를 해제하므로 소멸자를 다시 호출하지 않습니다.
— Pickle Rick

참조에 따르면 삭제는 AFTER 객체 해체라고합니다.
— Mario The Spoon

예, 클래스 삭제 후에 전체 삭제가 호출됩니다. 여기에는 두 가지 재정의가 있습니다.
— Pickle Rick

2
@PickleRick-삭제 표현식 이 소멸자 (소멸자가있는 유형에 대한 포인터를 제공 한 것으로 가정) 또는 소멸자 세트 (배열 양식)를 호출해야 한다는 것이 사실이지만 operator delete()함수는 삭제 표현식과 동일하지 않습니다. 소멸자는 operator delete()함수가 호출 되기 전에 호출됩니다.
— Peter

1
qoute에 헤더를 추가하면 도움이 될 것입니다. 현재 그것이 무엇을 의미하는지는 확실하지 않습니다. "저장소 삭제 ..."-누가 저장소를 할당 해제합니까?
— idclev 463035818
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.