2015-01-02 3 views
3

Я все еще немного запутался в правильном способе использования пользовательского делетера с shared_ptr. У меня есть класс ResourceManager, который отслеживает распределения ресурсов, и я изменил свой интерфейс для поддержки автоматического освобождения используемых ресурсов, делая метод Release частное и Выделяют метод возвращения ResourceHolder:Как правильно использовать пользовательский деблокиратор shared_ptr?

// ResourceManager.cpp: 
public: 
    ResourceHolder<Resource> Allocate(args); 

private: 
    void Release(Resource*); 

И в ResourceHolder класса I реализовать так:

// ResourceHolder.h 
template <typename T> 
class ResourceHolder 
{ 
public: 
    ResourceHolder(
     _In_ T* resource, 
     _In_ const std::function<void(T*)>& cleanupFunction) 
     : _cleanupFunction(cleanupFunction) 
     , _resource(resource, [&](T* resource) 
     { 
      cleanup(resource); 
     }) // Uses a custom deleter to release the resource. 
    { 
    } 

private: 
    std::function<void(T*)> _cleanupFunction; 
    std::shared_ptr<T> _resource; 
}; 

// ResourceManager::Allocate() 
... 
return ResourceHolder<Resource>(new Resource(),[this](Resource* r) { Release(r); }); 
  1. в моем методе очистки, я должен удалить T? Всегда ли это безопасно?

    if (nullptr != T) delete T; 
    
  2. Что произойдет, если cleanup() может выдать исключение? Могу ли я позволить этому избежать каких-либо условий при определенных обстоятельствах или я всегда должен его предотвращать?

  3. My ResourceManager не имеет зависимости от библиотеки трассировки, которую я использую, поэтому я выбрал обратный вызов, который вызывающий может предоставить через свой конструктор, и который будет вызван в методе release. Так что мой релиз выглядит следующим образом:

    void Release(Resource* r) 
    { 
        shared_ptr<std::Exception> exc = nullptr; 
        try 
        { 
         // Do cleanup. 
        } 
        catch(Exception* ex) 
        { 
         exc.reset(ex); 
        } 
    
        if (nullptr != r) delete r; 
    
        // Is it now safe to throw? 
        if (nullptr != m_callback) 
         m_callback(args, exc); 
    } 
    
    void Callback(args, shared_ptr<std::Exception> ex) 
    { 
        // Emit telemetry, including exception information. 
    
        // If throwing here is ok, what is the correct way to throw exception here? 
        if (nullptr != ex) 
        { 
         throw ex; 
        } 
    } 
    

Является ли это подход к разработке звука?

+0

_ «Является ли это обоснованным проектом?» _ - «Выпуск» можно вызывать в контексте уничтожения объектов. Так как исключение уже существует, исключение, происходящее на этом этапе, может быть серьезной проблемой. –

+0

Но обернуть все в блок catch try и сделать Callback a nothow() будет в порядке? –

ответ

1

В моем методе очистки мне нужно удалить T? Всегда ли это безопасно?

Если указатель ссылается на объект инстанцированный с new то вам нужно позвонить delete в противном случае вы в конечном итоге с утечкой памяти и непредсказуемое поведение.

Что произойдет, если очистка() может вызвать исключение? Могу ли я позволить этому избежать каких-либо условий при определенных обстоятельствах или я всегда должен его предотвращать?

Этого не должно быть, и вы должны приложить все усилия, чтобы этого не произошло. Однако, если код очистки делает, выбросьте исключение, вы должны поймать его, обработать соответствующим образом, и есть. Причина заключается в том, что пользовательский делектор может быть вызван в контексте деструктора, и всегда существует возможность вызова деструктора, когда исключение уже распространяется. Если исключение уже выполняется, а другое неперехваченное исключение прекращается, приложение прекращается. Другими словами, обрабатывать пользовательский код удаления и очистки, как если бы он был деструктором, и следовать тем же правилам и правилам обработки исключений.

Effective C++ Пункт # 8 - Не допускать исключения из уходящих деструкторов

деструкторы никогда не должны испускать исключения. Если функции, вызываемые в деструкторе, могут выбрасываться, деструктор должен поймать любые исключения, затем проглотить их или прекратить выполнение программы.

§ 15.1/7 C++ Standard [исключая.бросать]

Если механизм обработки исключений, после завершения оценки выражения должны быть выброшены, но до того, как исключение перехватывается, вызывает функцию, которая выходит через исключение, std::terminate называется.

-

Является ли это подход к разработке звука?

За исключением того, как вы в настоящее время намерены обрабатывать исключения, я не вижу в этом ничего плохого. Единственные реальные изменения, которые вам нужно сделать, - это то, как вы вызываете обратный вызов и как обратный вызов обрабатывает исключение, переданное ему. Полученный код после изменений может выглядеть примерно так.

void Release(Resource* r) 
{ 
    try 
    { 
     // Do cleanup. 
    } 
    catch (Exception& ex) 
    { 
     // Report to callback 
     if (nullptr != m_callback) 
      m_callback(args, &ex); 

     // Handle exception completely or terminate 

     // Done 
     return; 
    } 

    // No exceptions, call with nullptr 
    if (nullptr != m_callback) 
     m_callback(args, nullptr); 
} 

void Callback(args, const Exception* ex) 
{ 
    // Emit telemetry, including exception information. 

    // DO NOT RETHROW ex 
} 
Смежные вопросы