2012-01-18 3 views
1

예를 들어, 어떤 오래된 C 물건 (예 : pthreads 또는 기타)을 사용해야하는 클래스가 있다고 가정 해 봅시다. 이유 중 하나 또는 다른 이유로, 나는 의 malloc()과 같이, 내 생성자에서 호출 소멸자에 대한 잘못된 정보의 많은 거기 밖으로있다 것 같다소멸자와 Malloc'd 회원

class Foo 
{ 
    public: 
    Foo() 
    { 
     someMutex = malloc(sizeof(pthread_mutex_t)); 
     pthread_mutex_init(someMutex); 
     someString = new string("yay string!"); 
    } 

    private: 
    pthread_mutex_t * someMutex; 
    string * someString; 
} 

. 명시 적으로 정의 된 소멸자의 예가 포인터 기반 멤버에서 delete을 호출하는 것을 계속 보았습니다.하지만 메모리를 관리하는 클래스의 소멸자를 명시 적으로 정의 할 필요는 없습니다. 내가 소멸자를 필요로하는 것은 파일 처리 정리와 같은 것입니다.

따라서 다음 내 질문에 이르게 : 심지어 someMutex 불구하고 malloc 할당이 아니라 C++ new 명령을 암시 적으로 정의 소멸자는 여전히 돌볼 것, 또는 내가 어떻게해야합니까했다?

또 다른 질문을 해결할 수 있습니다. 위의 클래스에서 deletesomeString에 호출하기 위해 소멸자를 명시 적으로 정의해야합니까, 아니면 나를 돌보아야합니까?

+4

"메모리 관리를 위해 클래스의 소멸자를 명시 적으로 정의 할 필요가 없다는 것을 계속 읽고 있습니다."- 올바르지 않습니다. –

+0

@Oli 설명을 주셔서 감사합니다! – Ben

+0

생성자가 객체의 메모리를 할당하는 것과 같은 방법으로 객체의 메모리를 해제하기위한 소멸자를 작성할 필요가 없습니다. 핵심은 바로 * 얕은 * 할당 및 해제입니다. 클래스 내에 * 추가 ​​* 메모리를 할당하면 생성자에서이를 소멸자에서 해제해야합니다. (이것은 생성자에서'new' 호출이 여러 개있을 때 재미 있습니다. 나중에 하나가 예외를 던집니다.) 여러분이 읽은 것은'~ Foo() {delete this; }' –

답변

7

정리를 수행하기 위해 소멸자를 정의해야 할뿐만 아니라 복사본 생성자와 복사 할당 연산자를 선언하여 클래스의 올바른 복사를 보장해야합니다 (선택적으로 정의해야 함).

암시 적으로 정의 된 소멸자는 멤버 변수를 파괴합니다. 예를 들어, string 유형의 멤버 변수가있는 경우 소멸자는 해당 변수를 모두 자체적으로 파괴합니다. 그러나 포인터에 대한 소멸자 (예 : string*)는 아무 작업도하지 않습니다. 포인터를 가리키는 객체를 파기해야합니다.

또한이 클래스에 대한 복사 작업을 정의하거나 적어도 컴파일러에서 제공하는 기본 복사 작업 생성을 억제해야합니다. 왜? 기본적으로 복사 작업은 각 멤버 변수를 복사하기 만합니다.그래서, 경우 예를 들어, 당신은 쓸 수 있었다 :

{ 
    Foo x; 
    Foo y(x); 
} // Uh oh 
모두 x

y은 블록의 끝에서 파괴된다. 이 시점에서 xy은 동일한 동적 할당 뮤텍스와 문자열을 가리키고 있으므로 뮤텍스와 문자열은 두 번 삭제됩니다 (x에 한 번, y에 한 번).


더 나은 옵션은 수동 메모리 할당을 전혀 사용하지 않는 것입니다. 대신 someString을 클래스의 직접 구성원 (즉, string someString;으로 선언)으로 설정하거나 일생의 스마트 포인터 (예 : unique_ptr 또는 shared_ptr)를 사용해야합니다. 마찬가지로 클래스가 복사 불가능한 경우가 아니면 mutex의 수명을 관리하기 위해 사용자 정의 삭제자를 가진 스마트 포인터를 사용해야합니다.이 경우 뮤텍스를 클래스의 직접 구성원으로 만들 수 있습니다.

+0

우수 답변! 당신은 내가 이미 의심했던 것을 확인했지만 좋은 설명을 찾지 못했습니다. – Ben

3

예, 소멸자를 정의하고 객체 (someMutex 및 someString)를 삭제해야합니다.

그러나 someMutexmalloc을 할당 했으므로 free으로 해제해야합니다.

섞이지 않도록주의하십시오.

기억

  • delete[]
1

아니, 소멸자 shouldn '와 해방, new[] 할당 delete

  • 와 해방, new 할당 free
  • 와 해방, malloc 할당 그 데이터를 지우지 마십시오 (어딘가에 할당 된 메모리에 대한 포인터 일 수 있습니다. e 귀하의 응용 프로그램에서 lse). 따라서 자신의 소멸자를 작성해야합니다.

    그리고 한 가지 더. malloc으로 메모리를 할당 한 후에는 free()으로 해제해야합니다.

  • 0

    소멸자를 정의 할 필요가 있는지 여부는 현재 개체가 생성 된 개체를 소유하는지 아니면 다른 개체가 관리 할 수 ​​있도록 만들어야하는지에 따라 다릅니다.

    malloc()으로 힙 메모리를 할당 할 때 free()으로 해제해야합니다. new으로 개체를 만들 때 delete으로 개체를 삭제해야합니다. new[]으로 배열을 만들 때 delete[]으로 배열을 삭제해야합니다.

    암시 적 소멸자는 멤버 변수를 파괴하지만 포인터의 경우 포인터가 메모리에 할당되므로 스스로를 복구하지만 할당 된 메모리는 malloc으로 처리하지 않습니다.

    "스마트 포인터"(http://en.wikipedia.org/wiki/Smart_pointer)를 사용하는 또 다른 옵션은 현재 개체가 삭제되거나 범위를 벗어날 때 뾰족한 개체를 실제로 삭제합니다.

    2

    클래스에 string에 대한 포인터를 저장하는 대신 "스택 의미"를 사용하여 string의 인스턴스를 데이터 멤버로 저장하면됩니다.

    또한, 대신 "원시"pthread_mutex_t에 대한 포인터를 저장하는, 내가 RAII을 (생성자의 pthread_mutex_t을 만들고, 소멸자에서 그것을 파괴)를 사용하여이 pthread_mutex_t 자원을 포장하기 위해 C++ 클래스를 정의 할 것, 그리고이 C++ 클래스의 인스턴스를 Foo의 데이터 멤버로 저장합니다. 이런 식으로

    // 
    // C++ RAII wrapper on raw C pthread_mutex_t resource. 
    // 
    class PThreadMutex 
    { 
    public: 
    
        // Creates a pthread_mutex_t. 
        PThreadMutex() 
        { 
        pthread_mutex_init(&m_mutex, ...); 
        // Check for errors, and throw exceptions on errors 
        } 
    
        // Destroys a pthread_mutex_t 
        ~PThreadMutex() 
        { 
        pthread_mutex_destroy(&m_mutex); 
        } 
    
    
        // Other member functions 
        // ... 
    
    
        // May define move constructor and move assignment operator for C++11 
        // ... 
    
    private: 
        pthread_mutex_t m_mutex; 
    }; 
    
    
    
    class Foo 
    { 
    public: 
    
        Foo() 
        : m_someString("yay string!") 
        // m_someMutex initialized by its default constructor 
        { 
        } 
    
    
        ~Foo() 
        { 
        // Nothing to do: C++ compiler will call the destructors 
        // of class data members, releasing their associated resources. 
        } 
    
    
    private: 
        // 
        // Class "building blocks": 
        // 
        PThreadMutex m_someMutex; 
        string m_someString; 
    }; 
    

    , Foo에 대한 컴파일러 생성 소멸자는 자동으로 리소스를 해제, 각 데이터 멤버의 소멸자를 호출합니다. 일반적

    각각 "원시"C 자원 (pthread_mutex_t, FILE * 등)은 C에 싸서되어야 ++ 클래스 RAII를 사용하고, (이들이 가지 "빌딩 블록"것처럼)이 클래스의 인스턴스가 사용되어야 다른 클래스의 데이터 멤버로. 코드를 단순화하고 예외 안전 코드를 작성하는 데 도움이됩니다. 이 패턴을 사용하면 좋은 수준의 코드 안전성과 합성 가능성을 얻을 수 있습니다.

    +0

    절대 추천! 비록 포인터가 제대로 초기화되지 않았기 때문에 제공된 예제에 약간의 어려움이있을 것이라고 생각합니다. 어쩌면 내가 한 걸음 더 나아가서이 클래스가 pthread_mutex_t 자체를 저장하게 할 것이다. – Ben

    +0

    @Ben : 고마워, 나는 포인터 문제를 해결하기 위해 나의 대답을 편집했다. –