Похоже, вы приближаетесь к 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 ++.