Срок хранения заменяемого подобъекта

Что касается кода ниже

int main()
{
    struct S { int i; } s { 42 };
    new (&s.i) int { 43 };
}

[basic.stc] / 2 говорит

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

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

Вот тщательно составленный пример, в котором есть формулировка, говорящая что-то интересное. [basic.stc.inherit] / 1 говорит :

Продолжительность хранения подобъектов и ссылочных элементов - это время хранения их полного объекта.

и [intro.object] / 2 гарантирует, что созданный int объект является подобъектом s:

Если объект создается в хранилище, связанном с подобъектом-членом или элементом массива e (который может находиться или не находиться в пределах его жизненного цикла), созданный объект является подобъектом содержащего объекта e, если:
(требования выполнены , Я не буду их здесь копировать)

Итак, какой срок хранения у вновь созданного int объекта? Динамический или автоматический?


person Language Lawyer    schedule 17.02.2019    source источник
comment
Ваш первый абзац не нужен для вопроса.   -  person Retired Ninja    schedule 17.02.2019
comment
Ах, пропустил.   -  person Ted Lyngmo    schedule 17.02.2019
comment
Да без первого абзаца теперь отличный вопрос!   -  person Christophe    schedule 17.02.2019


Ответы (1)


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

Неопределенность в формулировке вызвана большим количеством случаев, которые необходимо охватить:

int main()
{
    struct Simple { int i; } s { 42 };
    new (&s.i) int { 43 };   // the lifecyle of the newly created object 
                             // will auto, since Simple will be destroyed when
                             // going out of scope
    struct Complex { char s[256]; } c; 
    Simple *p = new (&c.s) Simple;  // the lifecycle of the newly created object 
                            // is dynamic.  You'll need to delete it in time. 
                            // because compiler doesn't know about its true nature
                            // and memory will be lost when going out of scope
}   

К счастью, стандарт достаточно точен, поэтому, если вы предоставите правильный код (т.е. без UB), все компиляторы выдадут один и тот же результат.

Фактически, динамическое создание объекта (размещение нового) не обязательно означает, что жизненный цикл объекта не зависит от области, в которой создается объект. Но вы должны убедиться, что объект будет уничтожен в свое время, и поэтому это считается продолжительностью динамического хранения.

Ключевым моментом здесь является распределение. Только выделение памяти может сделать продолжительность объекта действительно независимой от области, в которой он был создан. Это выражено в стандарте, но, возможно, не так четко, как могло бы быть. Начнем с полного предложения в basic.stc/2:

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

Я понимаю, что последнее предложение применимо только в том случае, если объект еще не охвачен первым предложением. Но пока это личная интерпретация. Так что единственное, что можно сказать наверняка, - это то, что в случае перекрытия потребуется дополнительная осторожность.

Итак, давайте более подробно рассмотрим Продолжительность динамического хранения [basic.stc.dynamic] / 1

Объекты могут создаваться динамически во время выполнения программы (...). Реализация C ++ обеспечивает доступ к динамической памяти и управление ею с помощью глобальных функций выделения, оператора new и оператора new [], а также функций глобального освобождения памяти, оператора delete и оператора delete [ ]. [Примечание: формы без распределения, описанные в 21.6.2.3, не выполняют выделение или освобождение. - конец примечания]

Второе предложение поясняет, что динамическая память означает выделение. Затем следует интересное примечание, которое относится именно к главе [new.delete.placement] / 1:

Эти функции зарезервированы; программа на C ++ не может определять функции, заменяющие версии в стандартной библиотеке C ++. Положения пункта 6.7.4 не применяются к этим формам зарезервированного размещения операторов new и operator delete.

Раздел 6.7.4 - это раздел basic.stc.dynamic. Это означает, что специальное выделение, используемое для нового размещения, не создает динамической памяти.

Тот факт, что динамическое хранение и продолжительность динамического хранения - это не одно и то же, затрудняет выражение всего этого:

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

Здесь вы найдете онлайн-демонстрацию, чтобы поиграть с новым размещением и уничтожением и посмотреть, что происходит, когда охватывающий объект выходит за пределы области видимости. Он использует Tracer класс, который лучше int выделяет различные случаи (включая удаление предыдущего объекта перед вызовом нового размещения).

Заключение: я думаю, что нельзя избежать некоторой двусмысленности и замкнутости ни в одном стандарте с такой долгой историей и с таким большим количеством участников. Но в этом случае вы можете видеть, что сам вопрос имеет больше аспектов, чем вы ожидали в первую очередь.

person Christophe    schedule 17.02.2019
comment
Если я заменю функции глобального распределения для возврата памяти из предопределенного глобального массива символов, будет ли это доступ к динамически выделяемому хранилищу? - person Language Lawyer; 17.02.2019
comment
@LanguageLawyer, это еще один случай. Это продолжительность динамической памяти для созданных таким образом объектов, поскольку вам нужно позаботиться об удалении. Это точно. Спонтанно я бы сказал, что это не динамическое хранилище, поскольку оно находится в глобальной памяти (статическом хранилище), которую вы упаковываете в более мелкие части. С другой стороны, вы получаете точно такое же поведение, как при распределении из бесплатного хранилища. Так что я не уверен. Это сложный вопрос, и в краткосрочной перспективе я бы использовал ответ, подобный Миру Дикого Запада: если вы не можете изменить ситуацию, имеет ли это значение? ;-) - person Christophe; 17.02.2019