Память и указатели
В чем разница между указателем и ссылкой?
Основные различия между указателем и ссылкой в C++:
-
Синтаксис и использование:
-
Указатель: Переменная, которая хранит адрес другой переменной.
int a = 10; int* ptr = &a; -
Ссылка: Альтернативное имя для уже существующей переменной.
int a = 10; int& ref = a;
-
-
Инициализация:
-
Указатель: Может быть инициализирован позже или изменён для указания на другой адрес.
int b = 20; ptr = &b; // Теперь ptr указывает на b -
Ссылка: Должна быть инициализирована сразу при создании и не может быть изменена для ссылок на другой объект.
int c = 20; ref = c; // Изменяет значение a на 20, но ref по-прежнему ссылается на a
-
-
Нулевое значение:
-
Указатель: Может быть нулевым (
nullptr), что означает, что он не указывает ни на какой объект.int* ptr = nullptr; -
Ссылку нужно связать с подходящим объектом или функцией. Сам язык не отслеживает время жизни цели: после её уничтожения ссылка может стать висячей.
-
-
Операции разыменования:
-
Указатель: Требует операцию разыменования (
*) для доступа к значению, на которое указывает.int value = *ptr; -
Ссылка: Не требует специальной операции для доступа к значению.
int value = ref;
-
Указатель можно переназначить и проверить на nullptr. Ссылка удобна как обязательный параметр и не переназначается, но не гарантирует время жизни объекта. В обоих случаях нужно избегать висячих обращений.
Для чего нужен указатель на функцию? Как его объявить?
Указатель на функцию используется для хранения адреса функции и позволяет вызывать функции через этот указатель. Это полезно для реализации обратных вызовов (callbacks), таблиц виртуальных функций и гибких механизмов обработки событий.
Объявление указателя на функцию:
-
Объявление:
-
Для функции с прототипом
void foo(int):void (*funcPtr)(int);
-
-
Инициализация:
-
Присвоение адреса функции указателю:
funcPtr = foo;
-
-
Использование:
-
Вызов функции через указатель:
funcPtr(5); // Вызывает foo(5)
-
Пример:
#include <iostream>// Обычная функция
void foo(int x) {
std::cout << "Value: " << x << std::endl;
}
int main() {
// Объявление указателя на функцию
void (*funcPtr)(int);
// Инициализация указателя адресом функции foo
funcPtr = foo;
// Вызов функции через указатель
funcPtr(5);
return 0;
}
Что будет, если забыть вызвать delete? Когда освободится та память?
Если забыть вызвать delete для динамически выделенной памяти, произойдёт утечка памяти (memory leak). В таком случае:
-
Память, выделенная с помощью
new, не будет освобождена. -
Память будет занята до завершения программы.
-
Постепенно такие утечки могут привести к исчерпанию доступной памяти, что может вызвать замедление или крах программы.
Память, не освобождённая через delete, будет освобождена операционной системой при завершении программы, но в процессе выполнения программы эта память останется недоступной для повторного использования.
Что такое умный указатель? Какие умные указатели есть в стандартной библиотеке?
Умный указатель хранит указатель и реализует правила владения. unique_ptr освобождает ресурс при уничтожении или reset, shared_ptr при исчезновении последнего владельца. weak_ptr не владеет объектом и сам его не освобождает.
-
std::unique_ptr: Эксклюзивное владение ресурсом. Не может быть скопирован, только перемещён. -
std::shared_ptr: Совместное владение ресурсом. Поддерживает подсчёт ссылок, освобождает память, когда последнийshared_ptrвыходит из области видимости. -
std::weak_ptr: Неблокирующая ссылка на ресурс, управляемыйstd::shared_ptr. Не увеличивает подсчёт ссылок.
Пример:
#include <memory>
std::unique_ptr<int> ptr1(new int(10));
std::shared_ptr<int> ptr2 = std::make_shared<int>(20);
std::weak_ptr<int> ptr3 = ptr2;
Как работает const для переменных, ссылок и указателей и от чего зависит размер указателя?
Константность переменной, ссылки, указателя:-
Константная переменная: Значение не может быть изменено после инициализации.
const int a = 10; -
Константная ссылка: Ссылка на константу, через которую нельзя изменить значение.
const int& ref = a; -
Константный указатель: Указатель, адрес которого нельзя изменить после инициализации.
int value = 10; int* const ptr = &value; -
Указатель на константу: Указатель на значение, которое нельзя изменить через этот указатель.
const int* ptr = &a;
-
Константный указатель (
int* const): Указатель не может указывать на другой адрес, но данные по этому адресу могут изменяться.int value = 10; int* const ptr = &value; -
Указатель на константу (
const int*): Указатель может менять адрес, но данные по адресу менять нельзя.const int* ptr = &a;
- Размер указателя определяется типом указателя и реализацией; проверяйте sizeof. Следующие размеры типичны для распространённых ABI, но не гарантированы стандартом для всех платформ и указателей на члены.
-
32-битная система: Указатель занимает 4 байта.
-
64-битная система: Указатель занимает 8 байт.
-
Что случится, если вернуть ссылку на временный объект?
Если вернуть ссылку на временный объект, то после завершения функции объект будет уничтожен, и ссылка станет “висячей” (dangling reference). Использование такой ссылки приведёт к неопределённому поведению, что может вызвать ошибки или сбои программы.