Ответ
В C++ есть несколько основных способов передачи параметров, выбор зависит от того, нужно ли изменять оригинал, насколько объект велик и какие гарантии безопасности требуются.
1. Передача по значению (copy): Создается локальная копия. Изменения внутри функции не затрагивают оригинал. Подходит для примитивных типов и небольших, дешевых для копирования объектов.
void modifyCopy(int x) {
x = 42; // Меняется только локальная копия
}
2. Передача по указателю (pointer):
Передается адрес объекта. Позволяет изменять оригинал и передавать nullptr. Требует явного разыменования (*) и проверки на nullptr.
void modifyViaPointer(int* ptr) {
if (ptr) { // Всегда проверяем!
*ptr = 42; // Меняем значение по адресу
}
}
3. Передача по ссылке (reference):
Альтернатива указателю без синтаксиса разыменования. Ссылка не может быть nullptr (если только это не ссылка в состоянии "dangling"). Изменяет оригинал.
void modifyViaReference(int& ref) {
ref = 42; // Меняем оригинальную переменную
}
4. Передача по константной ссылке (const T&):
Оптимальный способ для передачи больших объектов (строк, векторов, пользовательских классов), которые не нужно изменять. Избегает дорогого копирования и гарантирует, что функция не изменит объект.
void printLargeObject(const std::vector<std::string>& data) {
for (const auto& item : data) {
std::cout << item << 'n';
}
// data.push_back("new"); // Ошибка компиляции: data константная
}
5. Передача по rvalue-ссылке (T&&):
Для поддержки семантики перемещения, когда объект больше не нужен вызывающему коду (например, временный объект). Позволяет эффективно "забрать" ресурсы.
void takeOwnership(std::unique_ptr<int>&& ptr) {
// Теперь ptr принадлежит этой функции
}
// Вызов: takeOwnership(std::make_unique<int>(5));
Практическое правило:
- Входные параметры, которые не меняются и дороги для копирования —
const T&. - Выходные или входно-выходные параметры —
T&(если объект гарантированно существует) или *`T** (если нужна возможность передатьnullptr`). - Примитивные типы (int, bool) — по значению.
- Чтобы принять владение ресурсом —
T&&(для move-семантики).
Ответ 18+ 🔞
Давай разберём эту тему, а то некоторые до сих пор путаются, как будто на дворе 2002-й год. Ситуация, в общем, пиздопроебибна, но мы её разжуём.
Смотри, есть несколько способов запихнуть данные в функцию. Выбор зависит от того, хочешь ли ты накосячить с оригиналом, насколько объект здоровенный и какие гарантии тебе нужны. Волнение ебать, но всё просто.
1. Передача по значению (копия):
Тут создаётся локальная копия. Всё, что ты внутри натворишь — останется там. Оригинал будет спать спокойно. Идеально для простых типов вроде int или для мелких объектов, которые не жалко скопировать.
void modifyCopy(int x) {
x = 42; // Поменял тут, а снаружи всем похуй
}
Если передать сюда здоровенный вектор, будет овердохуища копий и всё загнётся. Не делай так с большими штуками.
2. Передача по указателю:
Передаёшь адрес. Это как дать кому-то ключи от своей квартиры — теперь он может там всё перевернуть. Но может и не прийти, то есть быть nullptr. Поэтому всегда проверяй, блядь!
void modifyViaPointer(int* ptr) {
if (ptr) { // Э, сабака сука, а вдруг тут пусто?
*ptr = 42; // Теперь меняем жизнь снаружи
}
}
Забыл проверить — получил краш и сам от себя охуел.
3. Передача по ссылке: Тот же указатель, но замаскированный. Не может быть пустым (в теории), и не надо звёздочку ставить. Меняешь — и оригинал тут же меняется.
void modifyViaReference(int& ref) {
ref = 42; // Всё, приехали. Снаружи тоже 42.
}
Удобно, но если передашь что-то неживое (висячая ссылка), будет тебе хиросима.
4. Передача по константной ссылке (const T&):
Вот это, блядь, король для передачи больших объектов, которые трогать нельзя. Никакого копирования, просто дал посмотреть. Функция поклялась не менять твои данные.
void printLargeObject(const std::vector<std::string>& data) {
for (const auto& item : data) {
std::cout << item << 'n';
}
// data.push_back("new"); // Компилятор тебе: "Шёл бы ты на хуй, это const!"
}
Для строк, векторов, своих классов — это твой лучший друг. Доверия ебать ноль, но компилятор за этим следит.
5. Передача по rvalue-ссылке (T&&):
Это для продвинутых, когда объект — временный и ему уже всё равно. Можно эффективно "перетащить" ресурсы к себе, как стянуть одеяло.
void takeOwnership(std::unique_ptr<int>&& ptr) {
// Всё, чувак, теперь это моё. У исходного вызова — пусто.
}
// Вызываем так: takeOwnership(std::make_unique<int>(5));
Короче, практическая мудрость:
- Передаёшь что-то большое и только для чтения —
const T&. Не изобретай велосипед. - Нужно, чтобы функция что-то поменяла снаружи —
T&(если объект точно есть) или *`T`** (если нужна возможность сказать "ничего не передаю"). - Простые числа, булевы флаги — смело по значению, не парься.
- Хочешь забрать ресурсы у временного объекта —
T&&(семантика перемещения).
Выбери не тот способ — получишь или тормоза, или баги, или и то, и другое. Э бошка думай, когда пишешь сигнатуру.