2015-01-28 2 views
-4

previous question에서 계속 호출되지 :는 소멸자 스마트 또는 원시 포인터

를이 코드에서 애플과 과일의 소멸자는 전혀 호출되지 않습니다. 나는 둘 다에 std::cerr 진술을 가지고 있으며 실행되지 않는 애플의 코드를 정리하고있다. 삭제 전화가 충분하다고 생각 했나요? RAII를 제대로하고 있습니까? 또한 원시 포인터를 std :: unique_ptr 및 동일한 결과로 대체했습니다.

int32_t Fruit::frutificate(const Settings& settings) { 
    Fruit *listener; 
    if (settings.has_domain_socket()) { 
    listener = new Apple(settings); 
    } else { 
    listener = new Orange(settings); 
    } 
    int r = uv_run(listener->loop, UV_RUN_DEFAULT); 
    delete listener; 
    return r; 
} 

업데이트 : 모든 클래스는 가상 소멸자를 가지고있다.

+7

'과일'에 가상 소멸자가 있습니까? 그렇지 않으면 정의되지 않은 동작이 발생합니다 (그렇지 않으면 올바른 소멸자를 호출하지 못했음을 나타냄). –

+0

@lapinrigolo 보여주는 코드는 인스턴스를 구성하고 파기하는 측면에서 정확합니다. 'listener.loop'를'listener-> loop'로 바꿔야 할 수도 있습니다. – quamrana

+0

또한'std :: unique_ptr'을 사용해야합니다. 'uv_run'이'noexcept'인지는 중요하지 않습니다. – Deduplicator

답변

6

첫 번째로, 즉각적인 문제는 거의 확실합니다. ~Fruit()virtual이 아닙니다. 그것을 (virtual ~Fruit() = default 또는 virtual ~Fruit() {}에서 class Fruit까지) 추가하면 게시 된 코드가 마술처럼 작동하기 시작할 것입니다.

그러나 이는 사용자 코드가 아닐 수 있습니다. 그냥 노력하고 있습니다. 음.

코드를 개선 할 수있는 부분이 많이 있습니다. 첫 번째 개선으로 우리는 unique_ptr를 사용합니다 : (@Deduplicator 댓글에 위에서 언급 한 바와 같이) 리스너의 수명이 묶여 있도록 RAII를 사용

int32_t Fruit::frutificate(const Settings& settings) { 
    std::unique_ptr<Fruit> listener; 
    if (settings.has_domain_socket()) { 
    listener.reset(new Apple(settings)); 
    } else { 
    listener.reset(new Orange(settings)); 
    } 
    int r = uv_run(listener->loop, UV_RUN_DEFAULT); 
    return r; 
} 

합니다. 훨씬 더 좋고, 더 이상 수동 인 delete (우연히 또는 예외로 놓칠 수 있음). C에서

14 ++의 .reset(new Blah(whatever))= std::make_unique<Blah>(whatever);로 대체 할 수 있으며, 지금 당신의 코드는 결코 명시 적으로 얻을 수있는 좋은 습관이다, newdelete 호출합니다. 그러나 귀하의 코드는 C++ 11 태그이므로 위의 C++ 11 버전을 그대로 두겠습니다.

더 좋을지라도 최선을 다할 수 있습니다. 무료 저장소 (힙)를 전혀 사용할 필요가 없습니다.

무료 저장 사용을 방지하는 간단한 방법은 다음과 같습니다 uv_run 코드를 반복 단점이있다 (따라서 버그를 낳을 수)

int32_t Fruit::frutificate(const Settings& settings) { 
    if (settings.has_domain_socket()) { 
    return uv_run(Apple(settings).loop, UV_RUN_DEFAULT); 
    } else { 
    return uv_run(Orange(settings).loop, UV_RUN_DEFAULT); 
    } 
} 

(위의 의견에 @Jarod에서 도난). 우리는 람다와 함께이 문제를 해결할 수 있습니다 : 우리는 다음 람다에 공통 코드를 고려하고,

int32_t Fruit::frutificate(const Settings& settings) { 
    auto fruit_the_uv = [&](Fruit&& fruit) { 
    return uv_run(fruit.loop, UV_RUN_DEFAULT); 
    }; 
    if (settings.has_domain_socket()) { 
    return fruit_the_uv(Apple(settings)); 
    } else { 
    return fruit_the_uv(Orange(settings)); 
    } 
} 

두 가지에를 호출합니다. 우리가 임시 과일을 전달할 때 rvalue reference를 사용했습니다.

플러스 fruit_the_uv은 읽을 때마다 90srap song을 상기시킵니다. 그리고 그것은 더하기입니다.

+0

아니, 모든 소멸자가 가상입니다. 나는 당신의 람다 (lambda)로이 문제를 해결할 수 있지만, 지금은 소멸자가 작동하지 않는 이유에 대해 궁금합니다. – ruipacheco

+0

@lapinrigolo 왜냐하면 당신은 임시 속성을 전달하고 있기 때문에 임시 속성은 왼쪽 값 참조에 바인딩되지 않기 때문입니다. 그래서 나는 rvalue reference를 사용했습니다. 'const '가 작동한다면 (즉,'uv_run'과'const'' 루프가 작동하는 경우), 대신 그것을 사용할 수 있습니다. C++ 14에서는 전달 참조 ('auto &&')를 사용하고있을 것입니다. 그래서'Fruit'은 명시적인 소멸자를 가지고 있으며'virtual'입니까? '과일 '에 대한 실제 소멸자를 게시하십시오. – Yakk

+0

'virtual ~ Fruit();'- 구현이 비어 있습니다. – ruipacheco