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


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 deleteMyClass. 다른 하나는 글로벌 전화하기 전에 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.