2012-01-18 2 views
19

나는 배치 새을 사용할 때 소멸자를 수동으로 호출해야한다는 것을 다소간 읽었습니다. 내가 운영자 delete 일반적으로 소멸자를 호출하고 메모리 할당을 취소 알고있는 바로, 지금까지배치에 의해 할당 된 메모리를 올바르게 해제하는 방법은 무엇입니까?

// Allocate memory ourself 
char* pMemory = new char[ sizeof(MyClass)]; 

// Construct the object ourself 
MyClass* pMyClass = new(pMemory) MyClass(); 

// The destruction of object is our duty. 
pMyClass->~MyClass(); 

:

는 folowing 코드를 고려? 그러면 대신 delete을 사용하지 않으시겠습니까? 첫 번째 경우에
delete pMyClass; //what's wrong with that? 

우리는 우리가 이런 소멸자 호출 한 후 nullptr에 pMyClass을 설정하는 강제 :

pMyClass->~MyClass(); 
pMyClass = nullptr; // is that correct? 

을하지만 소멸자, 바로 메모리 할당을 해제하지 않았다? 그래서 메모리 누수가 될까요?

저는 혼란 스럽습니다. 설명 할 수 있습니까? 이 잘못

+1

+1 좋은 질문, 나는 또한 그것을 이해하고 싶습니다. – AraK

+12

기술적으로, 배치 new는 메모리를 할당하지 않습니다. 그러나이 컨텍스트의 세부 사항입니다. – Griwes

+1

@Griwes : +1 아마 근본적인 지점입니다. –

답변

32

new 연산자를 사용하면 메모리를 할당하는 operator new 함수를 호출 한 다음 new라는 위치를 사용하여 해당 메모리에 개체를 만듭니다. delete 연산자는 개체의 소멸자를 호출 한 다음 operator delete을 호출합니다. 그래, 이름이 혼란 스럽네.

//normal version     calls these two functions 
MyClass* pMemory = new MyClass; void* pMemory = operator new(sizeof(MyClass)); 
            MyClass* pMyClass = new(pMemory) MyClass(); 
//normal version     calls these two functions 
delete pMemory;     pMyClass->~MyClass(); 
            operator delete(pMemory); 

귀하의 경우 수동으로 배치를 새로 사용 했으므로 수동으로 소멸자를 호출해야합니다. 수동으로 메모리를 할당 했으므로 수동으로 메모리를 릴리스해야합니다. new과 함께 할당하는 경우에는 해당 delete이 있어야합니다. 항상.

는하지만, 새로운 배치가 내부 버퍼로뿐만 아니라 (다른 시나리오), 버퍼가 어디를 작동하도록 설계되지 당신이 그들에 operator delete를 호출해서는 안 이유입니다, operator new 할당.

#include <type_traits> 

struct buffer_struct { 
    std::aligned_storage<sizeof(MyClass)>::type buffer; 
}; 
int main() { 
    buffer_struct a; 
    MyClass* pMyClass = new (&a.buffer) MyClass(); //created inside buffer_struct a 
    //stuff 
    pMyClass->~MyClass(); //can't use delete, because there's no `new`. 
    return 0; 
} 
buffer_struct 클래스의 목적은 mainMyClass의 건설/파괴을 담당하면서 두 사람이 (거의 *) 서로 완전히 분리 얼마나주의, 어떤 방법으로 스토리지를 생성하고 파괴하는 것입니다

. 우리가해야 할 *

는 저장 당신은 delete 연산자와 operator delete을 구별 할 필요가 충분히

+2

이것은 질문의 근원입니다. "그러나 배치 새로 만들기는 내부 버퍼를 위해 설계되었으므로 자체 버퍼는 새 버퍼로 할당되지 않으므로 삭제를 호출해서는 안됩니다." 나중에 같은 유형의 다른 객체에 버퍼를 사용하기 때문에 메모리를 확보 할 필요가 없습니다. 인스턴스가 정리 될 수 있도록 소멸자를 호출합니다. –

+0

좋은 예입니다. 우리가 호출하는 버퍼를 삭제하려면 : delete [] a.buffer가 dinamicaly라면 호출하고, 어떻게 MyClass의 소멸자를 호출할까요? – codekiddy

+0

@codekiddy :'a.buffer'는 동적으로 할당되지 않기 때문에 할당을 해제 할 필요가 없습니다. 내 샘플에서는 자동으로 할당되므로 버퍼는 모든면에서 완전 자동입니다. 유일한 관심사는 클래스의 새로운 배치이며, 그 샘플 코드처럼 그 클래스의 소멸자를 호출하는 것입니다. –

9

한 가지 이유 : 당신은 위의 두 가지를 모두 수행 할 수

delete[] pMemory; 

:

delete pMyClass; 

는 배열이기 때문에 당신이 delete[]pMemory을 삭제해야한다는 것입니다.

마찬가지로, malloc()을 사용하여 메모리를 할당하고, 객체를 생성하기위한 새로운 배치, 그리고 delete을 사용하여 메모리를 삭제하고 해제 할 수 없는지 묻습니다. 이는 malloc()free()과 일치해야하며 malloc()delete이 아닐 수 있습니다.

실제로는 배치 소멸자와 명시 적 소멸자 호출이 거의 사용되지 않습니다. 표준 라이브러리 구현 (또는 주석에 언급 된 다른 시스템 수준 프로그래밍)에 의해 내부적으로 사용될 수 있지만 일반 프로그래머는이를 사용하지 않습니다. 나는 C++을 수년간 생산 코드 용으로 사용 해본 적이 없다.

+0

그냥 언급하자면 : 배치'new'는 OS 개발이나 C++에서 비슷한 일을 할 때 자주 사용됩니다. – Griwes

+0

안녕하세요. 그래서 우리는 acctualy delete [] pMemory가 소멸자를 호출하고 메모리 죄를 없애기 때문에 수동으로 소멸자를 호출 할 필요가 없습니까? – codekiddy

+2

@codekiddy :'delete [] pMemory'는 당신의 객체를위한 소멸자를 호출하지 않습니다. –

4

큰 수 있습니다. 특히 당신이 배치 새로운 사용하는 경우, 당신은 명시 적으로 소멸자를 호출하고 낮은 - 인이 operator delete를 사용하는

X *x = static_cast<X*>(::operator new(sizeof(X))); 
new(x) X; 
x->~X(); 
::operator delete(x); 

주, 즉, 메모리를 해제하는 operator delete (아닌 delete 운영자)를 호출 레벨보다 작고 delete 연산자이며 소멸자에 대해서는 걱정할 필요가 없습니다 (기본적으로 free과 약간 같습니다). 이것을 delete 연산자와 비교하면 내부적으로 소멸자를 호출하고 operator delete을 호출하는 것과 같습니다.

::operator new::operator delete을 사용하여 버퍼를 할당하고 할당을 해제 할 필요가 없습니다. 새 배치와 관련하여 버퍼가 손상되거나 파손되는 방식은 중요하지 않습니다. 요점은 메모리 할당과 객체 수명 문제를 분리하는 것입니다.

덧붙여 말하자면,이 응용 프로그램을 사용하면 메모리 사용을 신중하게 관리하기 위해 큰 블록의 메모리를 할당해야하는 게임과 같이 될 수 있습니다. 그런 다음 이미 획득 한 메모리에 객체를 구성 할 수 있습니다.

또 다른 가능한 사용법은 최적화 된 작고 고정 크기의 개체 할당 자입니다.

+0

+1 해 주셔서 감사합니다. – codekiddy

3

여러 MyClass 객체를 메모리 블록 내에 생성하는 것이 상상하면 이해하기 쉽습니다. 이 경우

, 그것은 같은 갈 것이다 : 새로운 문자를 사용하여 메모리의 거대 블록을 할당

  1. 을 [10 *는 sizeof (MyClass의)] 또는의 malloc (10 *는 sizeof (MyClass에))
  2. 해당 메모리 내에 10 개의 MyClass 객체를 생성하려면 new 배치를 사용하십시오.
  3. 무언가를하십시오.
  4. 각 개체의 소멸자를 호출하십시오.
  5. delete [] 또는 free()를 사용하여 큰 메모리 블록의 할당을 해제하십시오.

이 당신이 이 경우 등

컴파일러, 또는 OS를 작성하는 경우 당신이 할 수있는 일의 종류, 난 당신이 별도의 "소멸자"이 필요한 이유는 분명 희망 및 "삭제"단계는 이유가 없으므로 전화 삭제됩니다.그러나 은 일반적으로 메모리 할당을 해제해야합니다 (무료, 삭제, 거대한 정적 배열의 경우 아무 것도하지 않고 배열이 다른 개체의 일부인 경우 정상적으로 종료하는 등). 그렇지 않은 경우 유출 될거야.

Greg가 말했듯이이 경우 [delete]를 사용해야하므로 새 []로 배열을 할당했기 때문에 delete를 사용할 수 없습니다.

또한 MyClass에 대한 삭제를 재정의하지 않았다고 가정해야합니다. 그렇지 않으면 "완전히"호환되지 않는 완전히 다른 작업을 수행하게됩니다.

그래서 나는 당신이 설명하는 것처럼 "삭제"를하고 싶지는 않을 것이라고 생각하지만 작동 할 수 있습니까? 이것은 기본적으로 "나는 소멸자가없는 두 개의 관련없는 유형이 있습니다. 하나의 유형에 대한 포인터를 새로 작성한 다음 다른 유형에 대한 포인터를 통해 해당 메모리를 삭제할 수 있습니까?" 다시 말하면, "컴파일러는 할당 된 모든 것들을 하나의 큰 목록으로 가지고 있거나 다른 유형을 위해 다른 일을 할 수 있습니까?"

확실하지 않습니다. 사양 읽기는 말한다 : [delete 연산자의] 피연산자의 정적 유형의 동적 유형과 다른 경우

5.3.5

이 ..., 정적 타입이 피연산자의의 기본 클래스한다 동적 유형이고 정적 유형은 가상 소멸자를 가져야하거나 동작이 정의되지 않았습니다.

나는 그 의미한다고 생각 "두 관련이없는 유형을 사용하는 경우 (이것은 가상 소멸자를 통해 다형 클래스 개체를 삭제해도 괜찮은지) 작동하지 않습니다."

아니, 그러지 마라. 필자는 컴파일러가 형식이 아닌 주소 만보고 (두 가지 유형 모두 주소를 잘못 관리하는 다중 상속 클래스 인 경우) 실제로 시도해 볼 수도 있지만 시도하지는 않습니다.

관련 문제