2014-10-06 2 views
4

В разговоре следующий код, как был показан, чтобы быть небезопасны, потому что, если конструктор бросков, деструктор не будет называться и ресурсы утечки:Использования делегирования конструктора, чтобы избежать утечек

class TwoResources { 
    TwoResources(int x, int y) 
     : m_a(nullptr), m_b(nullptr) { 
     m_a = new A(x); m_b = new B(y); 
    } 
    ~TwoResources() { 
     delete m_b; delete m_a; 
    } 
    A * m_a; B * m_b; 
}; 

Раствора предложения использовать делегирование конструктора, например, так:

class TwoResources { 
    TwoResources() : m_a(nullptr), m_b(nullptr) { } 
    TwoResources(int x, int y) : TwoResources() { 
     m_a = new A(x); m_b = new B(y); 
    } 
    ~TwoResources() { 
     delete m_b; delete m_a; 
    } 
    A * m_a; B * m_b; 
}; 

Это безопасно потому, что:

C++ 11 15.2 [except.ctor]/2: «если конструктор без делегирования для объекта завершен, и конструктор делегирования для этого объекта существует с исключением, деструктор объекта будет вызван ».

Однако в том же слайде, он говорит:

Просто потому, что вы можете воспользоваться этим правилом не означает, что вы должны!

Если этот код гарантированно надежен, то каковы возможные проблемы с ним?

+0

Это, вероятно, плохая идея, чтобы использовать 'новый/delete' в C++ 11 код вручную. Вы должны использовать интеллектуальные указатели, чтобы избежать таких проблем. – ForEveR

+0

Это разговор STL на GoingNative 2013, правильно? –

+0

Я думаю, что название класса 'TwoResources' намекает на более жизнеспособное решение; один ресурс для каждого класса (или объекта). Если управление этими двумя ресурсами отличается, оберните их в свой класс RAII и включите их в класс более высокого уровня. – Niall

ответ

6

Просто потому, что что-то безопасно, это не значит, что это хорошая идея.

Например, использование этого вызова конструктора делегирования имеет решающее значение для безопасности исключений, но это далеко не ясно для случайного чтения кода, который не знаком с тонкостями языка. Через месяц кто-то, кто смотрит на ваш код, может подумать: «Почему вы устанавливаете его в null, если вы снова устанавливаете его в тело конструктора?» и удалите его. Уч.

Кроме того, когда вы управляете пожизненным ресурсом вручную, вам необходимо написать собственные операторы копирования/перемещения/назначения. Мисс один и хаос результаты. Если вы используете unique_ptr для управления временем жизни, то создатель или оператор-конструктор move-constructor/destructor выполнит правильные действия, и он будет жаловаться, если вы попытаетесь скопировать, не выполнив сам конструктор копирования.

1

Это, конечно, не ясно читать. Кто-то может прийти и изменить ваш код, добавив новые переменные только в один конструктор, не понимая полезности второго. Вы должны думать об использовании std::unique_ptr

class TwoResources 
{ 
    TwoResources(int x, int y) : m_a(new A(x)), m_b(new B(y)) 
    { 
    } 
    std::unique_ptr<A> m_a; 
    std::unique_ptr<B> m_b; 
}; 

Кроме того, взгляните на std::make_unique который является более безопасным, чем вручную с помощью новых и удаление операторов:

class TwoResources 
{ 
    TwoResources(int x, int y):m_a(std::make_unique<A>(x)), m_b(std::make_unique<B>(B(y)) 
    { 
    } 
    std::unique_ptr<A> m_a; 
    std::unique_ptr<B> m_b; 
}; 
Смежные вопросы