2014-09-08 1 views
2

я не могу решить следующую проблему самостоятельно:Когда мы начинаем перезаписывать память, срок службы объекта закончился?

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

struct A 
{ 
    int a; 
    A(){ } 
    ~A(){ } 
}; 

struct B : A 
{ 
    int b; 
    B(){ } 
    ~B(){ } 
}; 

A *a = (A*) malloc(sizeof(A) + sizeof(B)); 
B *b = new (a) B; //Constructor of B is calling 

Время жизни объекта обозначаемого к по a закончилась перед конструктором B начинает позвонить или он закончился, когда закончил конструктор B?

+0

@RSahu _ Срок службы объекта, который указывает, что точки не заканчиваются до тех пор, пока вы не назовете delete a; _ Это не совсем так. Поскольку 3.8 сказал, что время жизни объекта типа T заканчивается, если T - тип класса с нетривиальным деструктором (12.4), начинается вызов деструктора или память, которую объект занимает, повторно используется или освобождается. –

+0

@ St.Antario: Что вы пытаетесь сделать, выполнив это? Прежде всего, 'new A' гарантированно выделяет достаточно памяти для хранения экземпляра' A', а не экземпляра 'B'. Размещение нового (что является «новым (a) B') не выполняет распределение памяти; он просто создает 'B', где указывает' a'. У вас есть неопределенное поведение и отнюдь не гарантировано работать. –

+0

@ Insilico Я понимаю, что это UB. Но я пытаюсь узнать _details_ и найти официальное объяснение с использованием Стандарта. –

ответ

1

Вы пытаетесь использовать новый оператор размещения для инициализации b. Этот оператор не вызывает деструктор класса A (a), но инициализирует новый в память, на которую указывает a. Это проблематично (как указано в комментарии), потому что sizeof(B) больше sizeof(A), и вы указали только sizeof(A) по указателю a. Таким образом, это неопределенное поведение.

Срок службы объекта a официально не заканчивается. Вы получаете что-то вроде:

class A { int a; }; 
void* p = malloc(sizeof(A)); 
A* a1 = new (p) A(); 
A* a2 = new (p) A(); 

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

+0

_I не думаю, что стандарт ничего не говорит об этом. Хммм ... Предположим, что мы должны повторно использовать память следующим образом: struct C {C() {} ~ C() {}}; C * c = новый C; новый (c) C; '. Фрагмент кода не должен создавать UB. Время жизни оригинала c заканчивается при запуске конструктора для нового c или вызвал конструктор нового c? –

+0

Исходный 'c' нигде не удален. Поэтому, если вы выделяете какие-либо ресурсы (например,) в 'C', вы не освободите их до тех пор, пока не будет вызываться' delete'. Затем вы повторно используете эту часть памяти, и это приведет к UB тоже, потому что два объекта инициализируются в одной части памяти (см. Http://stackoverflow.com/a/1022332/4010250). – Zerschmetterling

+0

_Тогда вы повторно используете эту часть памяти, и это приведет к UB слишком. Я не согласен, потому что у Стандарта есть соответствующий раздел об этом. См. Http://stackoverflow.com/questions/25657435/does-reuse-storage-start-lifetime-of-a-new-object –

0

Стандарт из соответствующего раздела (3.8) говорит,

Время жизни объекта типа Т заканчивается, когда:

- если Т представляет собой тип класса с нетривиальным деструктора (12.4), начинается вызов деструктора, или

- место хранения, которое объект занимает, повторно используется или освобождается.

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

0

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

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

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

b.b = 3 
while (a.a > 0) { 
    b.b--; 
} 
1

Как только вы ввести конструктор из B, a не указывает на объект A больше.

Причина в том, что даже до первой инструкции конструктора объекта среда выполнения уже выполнила VMT, базовые под-объекты и инициализацию члена.

Также, если конструктор B не завершается из-за исключения, память уже использовалась в любом случае, а другой объект, в конце концов присутствующий с тем же адресом памяти, больше не существует.

Другими словами, просто не делайте этого ... в объектах C++ это не просто куча байтов, и у них есть права; например, право называть своего деструктора ;-)

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