C++ управление памятью ссылочных типов


Я все еще довольно начинающий программист, и у меня есть вопрос об управлении памятью c++ с типами refence.

Прежде всего, мое понимание ссылочных типов:

Указатель помещается в стек,и фактические данные, на которые указывает указатель, создаются и помещаются в кучу. Стандартные массивы и классов, определенных пользователем типов перезабора. Это правильно? Во-вторых, мой главный вопрос - всегда ли работают механизмы управления памятью c и c++(malloc, free и new, delete) это правильно и освобождает память, на которую указывает класс или массив? Все ли еще работает, если эти указатели каким-то образом переназначаются другим объектам того же размера/типа в куче? Что делать, если класс имеет член указателя, который указывает на другой объект? Я предполагаю, что удаление / освобождение объекта класса не освобождает то, на что указывает указатель его члена, верно?

Спасибо всем!

- R

6 7

6 ответов:

Похоже, что вы приближаетесь к C++ из управляемого языка, такого как C#. В C++все немного по-другому.

То, что вы описываете как ссылочные типы, не существует в C++. Типы В C++ не являются ни ссылочными типами, ни типами значений. Они просто "типы". Обрабатываются ли они через ссылки (или указатели) или по значению, полностью зависит от кода, который использует тип (а не определение типа). Напротив, в таких языках, как C#, тип Декларатор решает, должен ли тип обрабатываться как ссылка или как значение. В C++ есть нечто, называемое ссылкой , но это не имеет ничего общего с тем, что вы описываете. В конце я упомяну ссылки на C++. Теперь давайте посмотрим, сможем ли мы обработать несколько частей вашего вопроса:

Указатель помещается на стек и фактические данные, на которые указывает указатель создается и помещается в кучу.

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

class MyClass { /* ... */ };
...

MyClass* pObj1 = new MyClass();
MyClass* pObj2 = (MyClass*)malloc( sizeof(MyClass) );

Но не в том случае, если вы создадите объект следующим образом:

MyClass obj3;

В последнем случае объект выделяется в стеке, и в нем нет указателя или ссылки. Вы манипулируете им как "типом ценности".

MyClass *pObj3 = &obj3;

Теперь pObj3 - указатель (в стеке) на obj3, который также находится в стеке . Видишь? Нет связи между определением класса и местом хранения объектов этого класса. Это зависит от того, как вы используете тип. Довольно часто используется комбинация объектов на основе стека и кучи одного и того же типа.

Стандартный массивов и классов, определенных пользователем типов перезабора. Это правильно?

Нет; массивы - это просто набор объектов одного типа / размера, помещенных в последовательные ячейки памяти. Массив может быть выделен в стеке или в куче, как и в случае с отдельными объектами.

C и C++ не помещают никакой отдельной семантики на массивы (за одним исключением Я упомяну через секунду). Как только они выделены, они представляют собой просто набор объектов, которые случайно оказываются последовательными. Это зависит от вашей программы, чтобы использовать операции массива или операции прямого указателя для доступа к отдельным членам. Это означает:

  • arrayOfClass[i] является точно тождественным высказыванию ((Class*)*(array + i)). В C вы можете даже сказать i[arrayOfClass], и это означает то же самое, что и arrayOfClass[i] (C++, однако, будет жаловаться, потому что у него более строгие правила типов).
  • можно использовать операторы массива в указателях на объекты, не являющиеся частью массива (и, вероятно, аварийно завершающие работу)
  • можно использовать обычные операции с указателями на элементы массива.
  • Вы можете выделить "большой" кусок памяти и "создать свой собственный массив", интерпретируя меньшие последовательные куски этой памяти, как если бы они были членами массива меньших объектов (это именно то, что вы получаете, когда используете malloc()).

Массивы не являются типами сами по себе; они просто удобный способ выделить несколько объекты, и способ делать указатели, который более удобен в некоторых обстоятельствах.

Единственное исключение, которое приходит мне на ум из этого правила "массивы не являются особыми", - это массивы case, выделенные в C++ через new. Когда вы выделяете массив через new, он оставляет информацию (обычно в куче рядом с массивом, но это не обязательно) о том, сколько элементов содержал массив, когда он был выделен. Затем необходимо использовать специальный оператор delete [] для удаления массива. delete [] находит и использует этот дополнительный бит информации для правильного удаления всех элементов массива.
Во-вторых, мой главный вопрос заключается в том, всегда ли механизмы управления памятью c и c++(malloc, free и new, delete) обрабатывают это должным образом и освобождают память, на которую указывает класс или массив?

Как долго, как вы делаете вещи правильно, да.

Все ли еще работает, если эти указатели каким-то образом переназначаются на другие объекты того же типа размер / тип в куче?

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

delete другое дело, если вы используете указатель на тип, отличный от того, что хранится в . буфер в то время, когда вы вызываете delete, поведение "неопределенно" (т. е. вы, вероятно, аварийно завершите работу). Это потому, что delete делает больше, чем то, что делает free(); он также вызывает метод деструктора объекта, и компилятор полагается на тип указателя, чтобы вызвать правильный метод. Если вы используете неправильный тип указателя, будет вызван неправильный метод, и кто знает, что произойдет. Вы можете потенциально поместить что-то" другое " в буфер после того, как вы new это сделаете, но это может потребовать нетривиального объем работы и снова является продвинутой темой.

Также обратите внимание, что вы никогда не должны выделять с malloc() и свободными с delete, а также не должны выделять с new и свободными с free(). Убедитесь, что ваши методы правильно сопряжены.

Что делать, если класс имеет член-указатель, который указывает на другой объект? Я предполагаю, что удаление / освобождение объекта класса не освобождает то, на что указывает указатель его члена, верно?

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

, что все предполагает, что объект владеет содержимым, на которое указывает указатель элемента. В управляемых языках, таких как C#, все объекты "принадлежат" среде выполнения и удаляются под контролем сборщика мусора, поэтому вам не нужно беспокоиться о том, что оно. На Языке Си++. кто "владеет" объектами, на которые указывают точки-члены, определяется семантикой вашей программы, а не языком,и вы должны обратить внимание, чтобы решить, когда нужно удалить внедренные объекты. Если вы пропустите нужное время для удаления объекта, вы утечете память; если вы удалите его слишком рано, вы получите неопределенное поведение и сбои (когда какой-то код пытается использовать объект, который уже был удален).

Итак, ссылка на C++ - это в основном просто указатель с кусочек сахарной глазури, предназначенный для облегчения использования указателей определенного типа. В In priciple нет почти ничего, что вы можете сделать со ссылками, что вы не можете сделать в C++, просто используя указатели; несколько исключений-это продвинутые темы, которые я пропущу (я должен был бы посмотреть его, чтобы дать теме справедливость, и у меня нет моих ресурсов под рукой).

С вашей точки зрения ссылка на C++ - это просто указатель, который выглядит как объект стека.

CMyClass pObj1 = new CMyClass();
CMyClass& myObject = pObj1;      // Create a reference to the object pointed by pObj1

pObj1->Method1();                // Use the pointer to call Method1
pObj1.Method1();                 // Use the reference to call Method1

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

Я предполагаю, что удаление / освобождение объект класса не освобождает то, что он есть указатель члена указывает на то, что правильно?

Это верно по умолчанию, поэтому классы C++ имеют деструкторы. Например:

class C {
    int* x;

public:
    C () : x (new int[10]) {}  // constructor
};

C* cptr = new C;  // a pointer to a C object

Если я удалю cptr, произойдет утечка памяти, так как x не освобождается. Поэтому я добавлю деструктор в C:

class C {
    ...
    ~C () {delete [] x;}  // destructor
};

(есть масса проблем с остальной частью вашего вопроса. Вы, кажется, перепутали Java и C++, вот почему большинство в вашем вопросе нет никакого смысла. Я ответил только на этот фрагмент, чтобы дать вам пример того, как автоматически освобождать ресурсы. Я предлагаю вам прочитать книгу на C++, чтобы лучше понять язык.)

Я не знаю, с чего начать.

У нас есть примитивные типы (например, int).

У нас есть наш старый друг структура С.

У нас есть классы.

Все это может быть автоматическим классом хранения; просто сидя на стеке. Все можно передать по значению.

Тогда у нас есть указатель на x. (x *). Они хранят адрес элемента в стеке или распределены где-то еще, как с new. Как только вы получите указатель, это до вас, чтобы убедиться, что вы не делаете что-то, что делает его недействительным. При возврате из функции указатели на автоматические элементы в ее динамическом контексте становятся недопустимыми. Если вы используете delete, указатель, который вы удаляете, больше не действителен, и, конечно, никакие его копии не являются таковыми.

Тогда, наконец, у нас есть ссылки. x& - это просто синтаксический сахар. Передача ссылки на что-то-это просто передача указателя на это. Использование ссылочного типа позволяет избежать необходимости вводить некоторые символы *, и он утверждает, что указатель под таблицей никогда не будет иметь значение null.

Указатель помещается на стек и фактические данные, на которые указывает указатель создается и помещается в кучу.

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

Пример:

class Foo{
    public:
    // For Test1 This is in the Stack
    // For Test2 this is in the free store
    int x; 

    // For Test1 the pointer is in the Stack
    // AND -> It points, to where we set it (could be stack or heap)
    // For Test2 the pointer-variable-location is in the Free Store
    // AND -> Similar
    int *y;
}

int main()
{
    // Lies in the Local Scope
    Foo Test1;
    // Test2 Lies in the Local Scope, and contains the address of the 
    // Object, which is now on the Free Store
    Foo *Test2 = new Foo();
}

Мой главный вопрос-do c and c++ ' s механизмы управления памятью (malloc, свободный и новый, удалить) всегда справляться это правильно и освободит память, которая на что указывает класс или массив?

Прежде всего, старайтесь избегать смешивания free и delete. Во-вторых, free() получает указатель и не проверяет, указывает ли указанный указатель на допустимое место в памяти. Это означает, что вы можете попытаться освободить не выделенную память, которая может вызвать ошибки Seg. Стандартный пример этого -

int main()
{
     int * x; // Points to random
     free(x); // Undefined behaviour
}

Другое неправильное использование указателя может быть:

int main()
{
     int * x = malloc(sizeof(int) * 10); //Allocate an array of 10
     ++x; // Now points to x[1];
     free(x); // Give me trouble
}

Все ли еще работает, если те указатели каким-то образом переназначаются на другие объекты того же размера / типа на кучи?

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

Что делать, если класс имеет член-указатель это указывает на другой объект? Я предполагая, что удаление / освобождение класса объект не освобождает то, что он является членом указатель указывает на, это правильно?

free()ing класс не вызывает Деструктор, и вы можете использовать деструктор, чтобы избежать утечки памяти. Используя delete, Вы можете настроить деструктор на удаление этого другого объекта, иначе он вызовет утечку памяти.

Ваш пост говорит мне, что вы путаете некоторые вещи, вы начинаете говорить о ссылках в C++, но в конечном итоге говорите об указателях и free() и delete, и вообще создаете впечатление, что вы путаете. Я думаю, тебе стоит купить хорошую книгу.

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

class dog {
    int age;
    char *name;
};

int main() {
    int x;
    dog barry;
    ...
}

Тогда внутри main Барри и x будут рядом друг с другом на стеке. Таким образом, стек будет содержать два целых числа и указатель на символ.

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

class dog {
    ~dog() {
        delete name;
    }
}

~Собака будет вызвана, когда вы удалите собаку или когда собака выпадает из области видимости и снимается со стека.

Ссылка-это в основном постоянный указатель, который использует синтаксис автоматической переменной. Вы не можете изменить то, на что он указывает, и вы не можете использовать его для удаления объекта (напрямую; вы можете использовать оператор адреса для ссылки, чтобы получить указатель на указанный объект, а затем удалить его).

Функции malloc и free просто выделяют и освобождают блоки памяти. Обычно вам не нужно использовать их непосредственно в C++. Операторы new и delete выделяют и освобождают память, но они также вызывают конструктор и деструктор объекта. В конструкторе класса вызываются все конструкторы автоматических членов, и то же самое с деструкторами. Если у вашего класса есть член, который является указателем, его память не выделяется автоматически или освобождается. Вы должны явно сделать это в конструкторе и деструкторе или использовать интеллектуальный указатель, такой как std::auto_ptr.

Когда оператор delete используется для указателя, вызывается деструктор для типа указателя, так что если вы принудительно наведите указатель на неправильный тип с явным приведением, тогда при удалении будет вызван неправильный деструктор.