C ++ Распределение памяти - PullRequest
       1

C ++ Распределение памяти

3 голосов
/ 15 сентября 2010

При использовании C ++, если есть класс:

class MyClass
{
    char memory1bye;
    int memory4bytes;
    int another4bytes;
};

этот класс использует всего 9 байтов в памяти ... так что если я сделаю что-то вроде:

MyClass *t1;

Это даст мне полезный адрес для Класса, но выделит ли он 9 байтов? И будет ли он вызывать конструктор по умолчанию? Или мне нужно распределить эти 9 байтов в классе? Если тогда я назвал что-то вроде:

t1 = (MyClass *)new MyClass;

это будет считаться утечкой памяти? Другими словами, что происходит со старым адресом?

Ответы [ 9 ]

14 голосов
/ 15 сентября 2010
  1. Не делайте предположений о размере типов данных, они зависят от реализации.
  2. MyClass *t1 определяет неинициализированный указатель. Разыменование вызывает неопределенное поведение .
  3. t1 = (MyClass *)new MyClass; выделяет память в куче и создает новый объект. Если эта память не будет освобождена с помощью delete, произойдет утечка памяти. Кроме того, вам не нужен там актерский состав, достаточно t1 = new MyClass();.

Редактировать: О распределении.

MyClass *t1 = NULL; 

объявляет указатель на MyClass -объект, но не создает объект. Этот указатель инициализируется, чтобы указывать на 0. Теперь, когда вы делаете

t1 = new MyClass();

оператор new создает новый экземпляр MyClass и назначает адрес этого объекта t1. Теперь вы можете работать с этим объектом через t1:

t1->doStuff();

Вы можете даже создать больше указателей, которые указывают на один и тот же объект:

MyClass *t2 = t1;

Теперь t2 и t1 указывают на один и тот же объект. Когда вы закончите с объектом, просто выполните:

delete t1;

(delete t2 будет иметь тот же эффект). Теперь объект уничтожен, но указатели по-прежнему указывают на тот же адрес (который больше не является безопасным). Делая

t2->doStuff();

после delete вызывает неопределенное поведение. Если мы вернемся до удаления, рассмотрим это:

t1 = NULL;
t2 = NULL;

Теперь у нас больше нет адреса объекта, который мы создали, поэтому мы не можем вызвать delete для него. Это создает утечку памяти. Надеюсь, это даст вам некоторое представление о том, что происходит. Теперь забудьте все это и прочитайте о RAII .

4 голосов
/ 15 сентября 2010

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

О, и я также обязан сказать вам, что в C ++ вам почти всегда следует искать другие способы, кроме ручного управления памятью, этоконтейнерные классы (std :: vector, std :: deque и т. п.) и интеллектуальные указатели, которые делают управление памятью менее болезненным.

2 голосов
/ 15 сентября 2010
  1. Как уже говорили многие, размер MyClass зависит от реализации.В этом случае, поскольку у класса нет методов, вы в основном получили структуру, поэтому можно сделать некоторые разумные предположения относительно размера.На обычной современной 32-битной машине без каких-либо необычных флагов компилятора размер структуры будет 12 байтов;это вытекает из того факта, что поля в современных архитектурах по умолчанию выровнены по 4-байтовым границам.

    На 64-битной машине это может быть даже больше, но я был бы немного удивлен, если бы он был большечем 24 байта (т.е. 8-байтовое выравнивание для каждого поля).Я не думаю, что что-либо использует что-либо большее, чем 8-байтовое выравнивание для полей, если это явно не указано, и нет особого смысла в использовании больших значений выравнивания для полей, поскольку сами функции выделения памяти обычно имеют 8-байтовое выравнивание. (NB: не рассчитывайте, что это правда для вашей машины!)

    только *1011* способ узнать размер чего-либо - использовать sizeof(MyClass),Вам вряд ли когда-нибудь понадобится использовать это в C ++, так как оператор new знает об этом для вас и выделяет необходимый объем пространства.И, как уже отмечалось, помните, что размеры что угодно (кроме char) не являются переносимыми, даже если они на самом деле не изменяются безвозмездно.

  2. ВыполнениеMyClass *t1; ничего не выделяет.Это просто дает вам место для хранения адреса объекта (в частности, MyClass экземпляр).По умолчанию это пространство указывает на произвольную область, если переменная находится в любой локальной области видимости или в определении класса или структуры.Если вы не собираетесь помещать адрес в переменную, вероятно, стоит явно инициализировать его как NULL, чтобы он по крайней мере указывал на определенный объект, не являющийся объектом.

  3. Ваш t1 = (MyClass *)new MyClass; содержит ненужное приведение, поскольку new в любом случае возвращает указатель на объект этого типа.Достаточно сделать t1 = new MyClass;.

  4. Если t1 ранее указывал на объект и был единственной переменной, указывающей на него, у вас будет утечка памяти (при условии, что выне используется библиотека сборщика мусора; большинство программ на C ++ пишутся без их использования).Если что-то указывает на объект, то лучше взять на себя ответственность за его очистку.Если адрес не указывает ни на что конкретно, или он указывал на NULL, то ничего не просочилось.

    Помните, адреса не просачиваются;утечка объектов.

    Вы можете уменьшить утечки памяти, создавая объекты в стеке (с прямыми MyClass t1;) и передавая их по ссылке, а не по адресу;объект будет автоматически удален, когда он выходит из области видимости.Основным недостатком этого является то, что у вас есть объект, время жизни которого не может быть красиво связано с конкретной областью действия.Именно тогда вы используете указатели (или умные указатели, которые скрывают большинство деталей при стоимости некоторых ограничений).Действительно сложный код лучше справляется со сборкой мусора, хотя он имеет свои собственные компромиссы (особенно в том, что он значительно увеличивает потребление памяти; это основная причина того, что Java известна как более требовательная к памяти, чем C ++).

2 голосов
/ 15 сентября 2010

В дополнение к некоторым другим ответам:

  1. На «размер» класса влияют многие вещи:

    • Определенный реализацией размер переменных-членов
    • vtable
    • Требования к выравниванию памяти переменных-членов
    • Перетяжка

  2. Хотя приведение не является необходимым, это также плохой стиль C ++. Предпочитают не использовать приведение в стиле C, а использовать одно из других, более безопасных (или, по крайней мере, более явных) приведений из C ++:

    • dynamic_cast<type>()
    • static_cast<type>()
    • reinterpret_cast<type>()

Для получения дополнительной информации см. Справочное руководство по C ++ - Новые операторы C ++ Cast

2 голосов
/ 15 сентября 2010
MyClass* t1;

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

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

t1 = (MyClass*)new MyClass;

Это правильное мышление для создания экземпляра MyClass.Тем не менее, обычно лучше всего делать это в стеке:

MyClass t;  // who needs a pointer?

Тогда вам даже не нужно думать о памяти.Недостатком является то, что объект существует только до тех пор, пока вы не покинете область, в которой он был создан, как указано вложением {и}.

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

t1 = new MyClass;

Вам не нужно - или вы не хотите - явно приводить возвращенный указатель к MyClass * ... это просто избыточно и потенциальный источник ошибок, если выизмените имя класса в одном месте, но не в другом.

Через некоторое время вы тоже захотите удалить t1.

Фактический размер MyClass не может быть 9 байтов .. это зависитна компиляторе и может быть функцией флагов командной строки компилятора, версии компилятора, целевой модели памяти, ОС и т. д.

1 голос
/ 15 сентября 2010

- Нет гарантии, что этот объект класса будет занимать 9 байтов. Это полностью специфичное для реализации поведение

- MyClass *ptr просто объявляет указатель на тип 'MyClass'. Он еще не указывает ни на один объект типа «MyClass». Если он глобальный, он будет инициализирован нулем, иначе, если такой указатель является локальным (например, область действия функции), он будет неинициализирован.

- Вам нужно инициализировать этот указатель, чтобы он указывал на объект «MyClass»

например. предполагая, что «m» является объектом типа «MyClass»

MyClass m; 
ptr = &m;       // this does not create any new object(no constructor runs)

OR

MyClass *ptr;
ptr = new MyClass();  // This new expression, allocates memory large 
                      // enough to hold a 'MyClass' object, initializes
                      // the object by running it's constructor

delete ptr;           // delete the MyClass object by running it's 
                      // destructor, return the allocated memory back
                      // to the implementation

-Если вы не удалите указатель 'ptr' после создания нового, это точно утечка памяти

1 голос
/ 15 сентября 2010

Если вы делаете что-то вроде

MyClass *t1;

, вы просто объявляете указатель на класс MyClass.Вы на самом деле не выделяете память.Чтобы создать экземпляр этого класса, вы можете использовать любой из них:

MyClass t2;                  // this calls a default constructor implicitly
MyClass t3 = MyClass();      // this also calls a default constructor explicitly
MyClass *t4 = new MyClass;   // calls default constructor implictly

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

MyClass t5(arg1, arg2, arg3);
MyClass t6 = MyClass(arg1, arg2, arg3);
MyClass *t7 = new MyClass(arg1, arg2, arg3);
1 голос
/ 15 сентября 2010

Не думайте, что MyClass использует 9 байтов, это зависит от машины и от компилятора!

MyClass * t1;

Это даст вам полезный указатель , но место для хранения класса не выделено. Таким образом, ответ на первые два вопроса - НЕТ.

И да, вы должны выделить место для класса для себя, если вы хотите использовать указатель. Конечно, вы можете избавиться от выделения памяти, создав MyClass в куче *:

MyClass t1 ();

И эта память будет автоматически освобождена, когда t1 выйдет из области видимости.

  • Я имел в виду: стек.
0 голосов
/ 15 сентября 2010

Класс не может быть 9 байтов.Могло бы быть и больше, если бы компилятор округлился, добавив структуру, чтобы она лучше соответствовала архитектуре компьютера.

MyClass * t1;не дает вам полезный адрес.Это неинициализированный указатель, который указывает на случайное место в памяти, и неясно, что может быть по этому адресу.Он не выделяет вам места для хранения экземпляра MyClass и не вызывает конструктор.Я рекомендую вам инициализировать указатели при их определении:

MyClass *t1 = 0;   // use 0, NULL, or null_ptr

Вам необходимо зарезервировать место для класса и вызвать конструктор.'new' делает оба эти действия за вас.

MyClass *t1 = new MyClass();

Не забывайте, что вам нужно 'delete', чтобы соответствовать каждому новому, иначе вы будете писать утечку памяти.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...