2009-08-18 7 views
1

У меня есть следующие структуры данных:Различия между распределением указателя кучи

struct Inner 
{ 
    int myValue; 
}; 

struct Outer 
{ 
    Inner* inner; 
}; 

Вариант 1

Если я сделать следующее:

Outer outer; 
outer.inner = NULL; 

outer.inner = new inner; 
inner* pInner = outer.inner; 

, а затем добавить часы для следующие 2 значения:

  • outer.inner
  • Пиннер

, то они оба не-NULL, и равны по величине.

Вариант 2

Если я сделать следующее:

Outer outer; 
outer.inner = NULL; 

inner* pInner = outer.inner; 
if (! pInner) 
{ 
    pInner = new inner; 
} 

затем Пиннере указывает на действительной памяти кучи, и outer.inner еще NULL.

Вопросы

  1. Почему варианты различны - они не должны быть оба, указывая на ту же область памяти в варианте 2, а?

  2. Предполагая, что я пошел с вариантом 1, и тогда я был , читал значения из внутреннего. Тогда я мог бы использовать pInner и outer.inner, не так ли? Почему это отличается от выделения памяти?

+0

pInner будет * всегда * быть нулевым в инструкции if, поэтому вы всегда выполняете pInner = new inner.Поэтому мне ясно, что pInner - это действительная память кучи. external.inner никогда не затрагивается после инициализации, поэтому его ясный для меня внешний .inner == NULL –

ответ

1

В C++, любой назначение a = b (или конструктор копирования <<sometypehere>> a = b) обычно копирует биты от Ь к а для простых старых типов данных (вы можете переопределить operator= и скопировать конструкторы, чтобы получить различные эффекты, но это, очевидно, не case здесь, поскольку типы являются указателями ;-). Итак, если вы измените b позже, это абсолютно не влияет на a, которое просто сохраняет те же самые точные биты, которые копирует конструктор присваивания или копии.

Re 2, если два указателя являются точно такими же битами (вы назначили либо один из другого и не изменили ни один из них после назначения), то, конечно, вы можете безразлично использовать либо для доступа (писать или читать через него). Назначение (и копирование) - это то, что совершенно и сильно отличается (факт, что указатель или копия имеет указатель на свежую выделенную память, - это красная селедка).

1
Outer outer; 
outer.inner = NULL; 

Inner* pInner = outer.inner; 
if (! pInner) 
{ 
    pInner = new Inner; 
} 

Почему вы ожидали outer.inner изменить после назначения new Inner к Пиннере? Они независимы.

Вам нужно сделать pInner ссылкой на внешний.внутренний:

Inner*& pInner = outer.inner; 

Тогда вариант 2 работает так, как вы планировали.


P.S. идентификаторы не должны различаться только в зависимости от случая, что подвержено ошибкам. пример: «новый внутренний» и «внутренний * pInner»

6

Указатель хранит адрес памяти. В первом варианте вы сохраняете расположение памяти объекта, который распределяется по new inner в outer.inner. Затем вы копируете это значение в pInner, и обе переменные содержат один и тот же адрес.

Во втором варианте сначала outer.inner содержит адрес NULL. Это значение копируется в pInner, и обе переменные содержат NULL. После этого pInner изменяется, чтобы содержать адрес недавно выделенного объекта, но этот адрес, хранящийся в outer.inner, не изменяется. Этот адрес по-прежнему равен NULL.

Указатели - это просто цифры, которые являются адресами памяти. Различные назначения работают так же, как если бы переменные были объявлены как int. То, что эти числа фактически являются адресами памяти, становится интересным только тогда, когда * или -> используется для разыменования указателя. Две разные переменные указателя могут указывают на на тот же адрес, но они все еще разные переменные.

0

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

struct Inner { 
    int myValue; 
} 

struct Outer { 
    int inner; 
} 

Outer outer; 
outer.inner = 0; 

int pInner = outer.inner; 

if(!pInner) { 
    Inner inner; 
    pInner = &inner; 
} 

В этом коде, вы не ожидали бы pInner иметь такое же значение, как outer.inner бы вы?


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

Outer outer; 
outer.inner = NULL; 

Прямо здесь, outer.inner == 0 верно. Вы назначили 0 на номер outer.inner.

inner* pInner = outer.inner; 

Теперь вы скопировали значение 0 в pInner, так pInner == 0 также. Обратите внимание, что это только адрес памяти, нет данные по этому адресу. В этом случае адрес памяти 0x00000000.

if (! pInner) 

Оценивает к истине, потому что pInner == 0.

{ 
    pInner = new Inner; 
} 

Теперь pInner присваивается новое целое, сгенерированные системой. pInner больше не имеет того же целочисленного значения, что и inner.outer.

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