C ++ управление памятью ссылочных типов - PullRequest
7 голосов
/ 18 января 2011

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

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

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

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

-R

Ответы [ 6 ]

16 голосов
/ 18 января 2011

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

То, что вы описываете как ссылочные типы, не существует в C ++.Типы в C ++ не являются «ссылочными типами» и не являются «типами значений».Они просто «типы».Обрабатываются ли они ссылками (или указателями) или по значению, полностью зависит от кода, который использует тип (а не определение типа).Напротив, в таких языках, как C #, объявитель типа решает, должен ли тип обрабатываться как ссылка или как значение.В C ++ есть нечто, называемое reference , но оно не имеет ничего общего с тем, что вы описываете.В конце я упомяну ссылки на 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 , поведение будет «неопределенным» (a.k.a. вы, вероятно, аварийно завершите работу). Это потому, что delete делает больше, чем free(); он также вызывает метод деструктора объекта, и компилятор полагается на тип указателя для вызова правильного метода. Если вы используете неправильный тип указателя, будет вызван неправильный метод, и кто знает, что произойдет. Вы можете потенциально поместить что-то еще в буфер после того, как вы new сделаете это, но это может потребовать нетривиального объема работы и снова является сложной темой.

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

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

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

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

Теперь ссылка C ++ - это просто указатель с небольшим количеством сахарного покрытия, предназначенный для облегчения использования определенных типов указателей. В принципе, вы почти ничего не можете сделать со ссылками, которые вы не можете сделать в 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 ++.

3 голосов
/ 18 января 2011

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

Это верно по умолчанию, поэтому классы 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 ++, чтобы лучше понять язык.)

2 голосов
/ 18 января 2011

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

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

Пример:

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();
}

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

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

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() класс не вызывает Destructor, и вы можете использовать деструктор, чтобы избежать утечек памяти. Используя delete, вы можете настроить деструктор на удаление этого другого объекта, иначе это приведет к утечкам памяти.

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

2 голосов
/ 18 января 2011

Я не уверен, с чего начать.

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

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

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

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

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

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

1 голос
/ 18 января 2011

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

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

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

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

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

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

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

0 голосов
/ 18 января 2011

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

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

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

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