Умные указатели исключают необходимость двухфазного построения? - PullRequest
6 голосов
/ 09 ноября 2011

Двухфазное построение имеет следующую форму:

struct something {
    something ()
      : p1(NULL)
      , p2(NULL)
    {   }

    ~something () {
        if (p1) delete p1;
        if (p2) delete p2;
    }

    void initialize () {
        p1 = new int(2);
        p2 = new int(5);    // May throw if allocation fails!
    }

    int* p1;
    int* p2;
};

Точка, в которой наивный конструктор (который не отслеживает ошибки выделения) будет пропускать память: частично построенный деструктор объектаникогда не вызывается.

Мой вопрос: является ли следующий код безопасным, и, как следствие, умные указатели устраняют двухфазное построение?

struct something {
    something ()
      : p1(new int(2))
      , p2(new int(5))
    {   }

    std::unique_ptr<int> p1;
    std::unique_ptr<int> p2;
};

Ответы [ 3 ]

5 голосов
/ 09 ноября 2011

Да, ваш новый код в порядке.Тем не менее, обратите внимание, что в более сложных случаях возможна небольшая вероятность:

#include <memory>

struct foo {
  foo(std::shared_ptr<int> a, std::shared_ptr<int> b) { }
};

struct bar {
  foo f;
  bar() : f(std::shared_ptr<int>(new int), std::shared_ptr<int>(new int)) { }
};

int main() {
  bar b;
}

не будет безопасным, однако, поскольку порядок вычисления аргументов конструктора foo в списке инициализаторов barне указаноСоответствующий компилятор может предпочесть выполнить глубину или ширину первого порядка оценки (или что-нибудь еще, если они все были правильно оценены в конце).Это означает, что если первый new int завершился успешно, но второй бросил до того, как объекты shared_ptr были сконструированы, то первое выделение, которое должно быть выполнено, может все еще утечь.

Если вы обнаружите, что хотите это сделать, естьдва возможных решения, помимо простого возврата к двухфазному построению: первое могло бы быть рефакторингом, второе состояло бы в том, чтобы создать shared_ptr индивидуально сначала как элементы bar, до f.Что из этого является наиболее подходящим - это решение, которое, по моему мнению, необходимо делать в каждом конкретном случае.

5 голосов
/ 09 ноября 2011

Мой вопрос: следующий код безопасен,

Да, все будет в порядке.

struct something {
    something ()
      : p(new int(5))
    {   }

    std::unique_ptr<int> p;
};

Обратите внимание, что наивный код

struct something {
    something ()
      : p(new int(5))
    {   }

    int* p;
};

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

struct something {
    something ()
      : p(new int(5)), q(new int)
    {   }

    int *p, *q;
};

что не будет. Умные указатели также сработали бы в этом случае.

0 голосов
/ 02 декабря 2011

Вам вообще не нужно двухфазное построение, если вы просто обрабатываете исключение.Это путь RIAA.

struct something {
    something ()
      : p1(NULL)
      , p2(NULL)
    {   
        p1 = new int(2);
        try {
            p2 = new int(5);    // May throw if allocation fails!
        } catch (std::bad_alloc&) {
            delete p1;  //cleanup 
            throw;  //rethrow
        }
    }

    ~something () {
        delete p1;
        delete p2;
    }

    int* p1;
    int* p2;
};
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...