2015-08-17 2 views
3

Является ли следующее определение?Переписывание объекта с объектом того же типа

#include <iostream> 
#include <string.h> 

using namespace std; 

struct Const { 
    const int i; 
    Const (int i) : i(i) {} 
    int get0() { return 0; } // best accessor ever! 
}; 

int main() { 
    Const *q,*p = new Const(1); 
    new (p) Const(2); 
    memcpy (&q, &p, sizeof p); 
    cout << q->i; 
    return 0; 
} 

Обратите внимание, что после завершения строительства второго Const, p не семантически (намеренно?) Указывает на новый объект, а первый нет, поэтому она может использоваться «как void*». Но второй объект построен по тому же адресу, поэтому битовая диаграмма p представляет адрес нового объекта.

К.П

new (p) Const(2) Стирание старый объект, хранящийся в p, поэтому указатель не является действительным больше, кроме как указатель на хранение (void*).

Я хочу восстановить стоимость p как Const*.

КОММЕНТАРИЙ 2

После того, как обе p->~Const() или memset (p, 0, sizeof *p) ясно, что p не указывает на действительный объект, так что p может быть использован только в качестве указателя на хранение (void* или char*), например, чтобы реконструировать другой объект , В этот момент p->get0() не допускается.

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

Моя интуиция такова: В любом случае старый объект ушел, а p указывает на старый объект, а не на новый.

Я ищу подтверждение или опровержение на основе стандарта.

ТАКЖЕ

Я спросил по существу, один и тот же вопрос о указателях в C и C++:

Пожалуйста, прочитайте это обсуждение прежде чем ответить «это смешно ».

+3

«memcpy» должен быть эквивалентен простому назначению указателя 'q = p', не так ли? –

+0

@ DanielJour Я так не думаю. 'p' больше не является верным указателем на объект типа' Const'. – curiousguy

+0

Почему? Он указывает на правильно выровненную область памяти, в которой был создан объект правильного типа. (Кстати, вы, вероятно, должны добавить некоторые операции удаления) –

ответ

4

(создание сообщества-вики, как включение DYP свой комментарий повторно 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 к q ... это законно, хотя при 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 -qualified, и, если тип класса, не содержит какой-либо нестатический membe данных г, тип которого является const -qualified или ссылочный тип ...
  • с использованием q - а не p - для доступа к i предположительно необходимо избегать оптимизаций компилятора на основе предполагаемого знания i.

Что касается Вашего комментария:

Обратите внимание, что после завершения строительства второго Const, p не делает семантически (? Намеренно) указывает на новый объект, а первый нет, поэтому она годна к употреблению " как void* ".

Учитывая вам места размещения нового объекта по адресу, указанному в p, p, безусловно, действительно, указывает на вновь созданный объект, и очень намеренно, но он не может быть использован для управления этим объектом при 3,8/7 как указано выше.

Учитывая, что у вас есть понятие «семантически указывая», которое не определено в C++, истинность этой части утверждения в вашем собственном сознании.

«после строительства второй Const, p ... может использоваться«как void*» не имеет никакого смысла ... это не более удобным, как и все, чем это было заранее.

Но второй объект построен на тот же адрес, так что битовый образ p представляет адрес нового объекта.

конечно, но ваши комментарии показывают, вы думаете «немного картины» как-то дис tinct from значение указателя, применимого к присвоению с =, что неверно.

new (p) Const(2) Стирание старый объект, хранящийся в p, поэтому указатель не является действительным больше, кроме как указатель на хранение (void*).

«стереть» это странный термин для него ... переписывание будет более значимым. Как указано выше и объяснено выше, 3.8/7 говорит, что вы не должны «манипулировать» объектом. p указывает после нового размещения, но значение и тип указателя не зависят от нового объекта placmeent. Как вы можете позвонить f(void*) с указателем на любой тип, место размещения - new не нужно знать или заботиться о типе выражения p.

После того, как обе p->~Const() или memset (p, 0, sizeof *p) ясно, что p не указывает на действительный объект, так что p может быть использован только в качестве указателя на хранение (void* или char*), например, чтобы реконструировать другой объект. В этот момент p->get0() не допускается.

Большинства это верно, если под «p может быть использован только» вы имеете в виду значения p в то время, а не сам указателе (который может быть, конечно, также может быть назначено). И вы пытаетесь быть слишком умны с предметом - p остается Const*, даже если он используется только при размещении нового, которое не заботится о типе указателя.

«Я хочу восстановить стоимость p в качестве Const*».

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

+0

Битовая диаграмма указателя никогда не является недопустимой. Таким образом, бит-шаблон не совпадает с значением. Вы можете скопировать шаблон бита недействительного указателя. – curiousguy

+1

@curiousguy: 'p' был правильно инициализирован, поэтому он имеет значение, которое можно безопасно получить и скопировать с помощью' = '. Если 'p' были неинициализированы или намеренно повреждены, то различие становится актуальным, и возможно, что' memcpy' будет безопасным, а '=' не будет. И BTW - вы можете скопировать значения недействительных указателей ... Стандарт считает, что указатель, который не соответствует действующему в настоящий момент объекту, был недействительным, отличается от битового шаблона, который даже не ссылается на законный адрес. –

+1

Бит-шаблон (* представление объекта *) содержит значение (* значение представления *) (§3.9/4) для тривиально-скопируемых типов, которым является указатель. –

3

Это только в качестве дополнения к ответу @Tony двойки, о

new (p) Const(2) сотри старый объект, хранящийся в p

Я думаю, вы должны различать между объектом и концептуальным идея «экземпляра».

[...] Объект является областью хранения. [...]

[N4431 пункте 1.8/1]

Так указатель p указывает на область хранилище, которое содержит битовый шаблон некоторого «экземпляра» перед размещением нового и некоторого другого битового шаблона другого, но хорошо построенного «экземпляра» правильного (того же) типа.

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

+0

Итак, это должно работать, потому что я не изменил тип? – curiousguy

+0

Я бы предположил, да. Если бы вы строили объект другого типа, тогда UB мог бы получить доступ к нему с использованием неправильно введенного указателя. –

+0

Все в порядке с тем, что 'const int' принимает разные значения? – curiousguy

Смежные вопросы