(создание вики-сообщества как включения комментария dyp re 3.8 / 7 очень важно; хотя мой предыдущий анализ был правильным, я бы сказал примерно то же самое о коде, который был сломан, не обращая внимания на 3.8 / 7 сам)
Const *q,*p = new Const(1);
new (p) Const(2);
Строка new(p) Const(2);
перезаписывает объект, созданный с помощью Const(1)
.
memcpy (&q, &p, sizeof p);
Это эквивалент q = p;
.
cout << q->i;
Это обращается к члену q->i
, который будет 2
.
Несколько примечательных вещей:
std::memcpy
- уродливый способ присвоить p
_11 _... это законно, хотя в версиях 3.9 / 3:
Для любого тривиально копируемого типа T
, если два указателя на T
указывают на разные T
объекты obj1
и obj2
, где ни obj1
, ни obj2
не являются подобъектами базового класса, если базовые байты (1.7), составляющие obj1
, копируются в obj2
, obj2
должен впоследствии будут иметь то же значение, что и obj1
. [ Пример:
T* t1p;
T* t2p;
// provided that t2p points to an initialized object ...
std::memcpy(t1p, t2p, sizeof(T));
// at this point, every subobject of trivially copyable type in *t1p contains
// the same value as the corresponding subobject in *t2p
Замена старого Const(1)
объекта на Const(2)
разрешена, если программа не зависит от побочных эффектов первого деструктора, а это не так.
(как dyp отмечено в комментариях ниже) постоянный доступ к объекту Const(2)
с использованием p
является незаконным согласно третьему пункту 3.8 / 7:
указатель, указывающий на исходный объект [...], может использоваться для управления новым объектом, если ...
- тип исходного объекта не соответствует
const
и, если это тип класса, не содержит каких-либо нестатических элементов данных, чей тип соответствует const
или ссылочному типу ...
- использование
q
, а не p
для доступа к i
, по-видимому, необходимо, чтобы избежать оптимизации компилятора, основанной на предполагаемом знании i
.
Что касается вашего комментария:
Обратите внимание, что после создания второго Const
, p
семантически (намеренно?) Не указывает на новый объект, а первый отсутствует, поэтому его можно использовать как void*
.
Учитывая, что вы разместили новый объект по адресу, содержащемуся в p
, p
наверняка указывает на вновь созданный объект, и очень намеренно, но его нельзя использовать для управления этим объектом в версии 3.8 / 7, как указано выше.
Учитывая, что у вас, кажется, есть понятие семантического указания, которое не определено в C ++, истинность этой части утверждения находится в вашем собственном сознании.
'после создания второго Const
, _40 _... можно использовать как void*
' не имеет смысла ... он не более удобен, чем что-либо прежде.
Но второй объект создается по тому же адресу, поэтому битовая комбинация p
представляет адрес нового объекта.
Конечно, но ваши комментарии показывают, что вы думаете, что битовый шаблон каким-то образом отличается от значения указателя применительно к присваиванию с =
, что неверно.
new (p) Const(2)
стереть старый объект, хранящийся в p
, поэтому указатель больше не действителен, кроме как указатель на хранилище (void*
).
стирание - странный термин для этого ... перезапись была бы более значимой. Как dyp отмечалось и объяснялось выше, 3.8 / 7 говорит, что вы не должны манипулировать объектом, на который указывает p
после размещения new, но значение и тип указателя не зависят от placmeent new. Подобно тому, как вы можете вызвать f(void*)
с указателем на любой тип, объект размещения new
не должен знать или заботиться о типе выражения p
.
После p->~Const()
или memset (p, 0, sizeof *p)
становится ясно, что p
не указывает на действительный объект, поэтому p
можно использовать только как указатель на хранилище (void*
или char*
), например, для восстановления другого объекта. В этот момент p->get0()
не допускается.
По большей части это правда, если под p
можно использовать только значение, вы имеете в виду значение p
в то время, а не сам указатель (который, конечно, также может быть назначен). И вы пытаетесь быть немного умнее с этим void*
/ char*
- p
остается Const*
, даже если он используется только при размещении new, которое не заботится о типе указателя.
Я хочу восстановить значение p
как Const*
.
Значение p
не изменилось после его первой инициализации. Размещение-new
использует значение, но не изменяет его. Восстанавливать нечего, ничего не потеряно. Тем не менее, dyp подчеркнула необходимость не использовать p
для управления объектом, поэтому, хотя значение не было потеряно, его также нельзя использовать напрямую, как хотелось бы.
person
Community
schedule
17.08.2015
memcpy
должно быть эквивалентноq = p
простому назначению указателя, не так ли? - person Daniel Jour   schedule 17.08.2015p
больше не является допустимым указателем на объект типаConst
. - person curiousguy   schedule 17.08.2015p
- это простой тип значения, очень похожий на целое число. Значение*p
зависит от состояния программы, но значениеp
является самодостаточным. - person Chris Beck   schedule 17.08.2015void *p=malloc(1); free(p); printf("%p", p);
- это UB, потому что вы даже не можете распечатать значениеp
после того, как оно было передано вfree
. - person 6502   schedule 17.08.2015int
недействителен в том смысле, что читать из него UB. Но после инициализации он остается в силе. - person juanchopanza   schedule 17.08.2015int
может быть недопустимым, но значениеint
не может, AFAIK. - person curiousguy   schedule 17.08.2015memcpy
вместоq=p
, это ничего не меняет, вы все равно копируете неопределенное значение в любом случае, так что вся этаmemcpy
вещь является неверным направлением - person Chris Beck   schedule 17.08.2015p
остается действительным и остается указывающим на новый объект. Если вы замедлитесь, чтобы подумать, это должно быть интуитивно очевидно, а если нет, я могу лишь повторить совет Manu343726. - person Tony Delroy   schedule 17.08.2015