⚙️ C++

السلاسل النصية والعمل مع النص في C++17: دليل مفصل

Функции в C++: параметры, перегрузка и ссылки для новичков

Функции (в C++ их часто называют функциями, а не методами) — основной инструмент организации кода. Написал один раз — вызываешь когда нужно. Именно функции превращают хаотичный код в понятную программу.

Объявление и вызов функции

#include <iostream>
#include <string>
using namespace std;

// Синтаксис: тип_возврата имяФункции(тип параметр, ...) { тело }

// Без параметров и без возврата
void printSep() {
    cout << "══════════════════════════════" << endl;
}

// С параметром
void greet(string name) {
    cout << "Привет, " << name << "! Добро пожаловать!" << endl;
}

// Возвращает значение
int add(int a, int b) {
    return a + b; // return завершает функцию и возвращает значение
}

// Несколько точек return
string classify(int n) {
    if (n > 0) return "положительное";
    if (n < 0) return "отрицательное";
    return "ноль";
}

// Проверка простого числа
bool isPrime(int n) {
    if (n < 2) return false;
    for (int i = 2; i * i <= n; i++)
        if (n % i == 0) return false;
    return true;
}

int main() {
    printSep();
    greet("Иван");
    greet("Мария");
    
    cout << "7 + 13 = " << add(7, 13) << endl;       // 20
    cout << "3 + 4 = " << add(3, 4) << endl;         // 7 (сразу использовать)
    
    cout << classify(42) << endl;   // положительное
    cout << classify(-5) << endl;   // отрицательное
    cout << classify(0)  << endl;   // ноль
    
    cout << "Простые до 20: ";
    for (int i=2; i<=20; i++) if (isPrime(i)) cout << i << " ";
    cout << endl;
    printSep();
    return 0;
}

Прототипы функций (объявления)

// В C++ функцию нужно объявить ДО использования!
// Если вы вызываете функцию раньше её определения — нужен прототип.

#include <iostream>
using namespace std;

// Прототипы (объявления без тела)
double circleArea(double r);
bool isEven(int n);
void swap(int& a, int& b); // & означает ссылку

int main() {
    cout << "Площадь: " << circleArea(5.0) << endl; // Работает благодаря прототипу
    
    int x=10, y=20;
    swap(x, y);
    cout << "x=" << x << ", y=" << y << endl; // x=20, y=10
    
    return 0;
}

// Определения (реализации)
double circleArea(double r) { return 3.14159 * r * r; }
bool isEven(int n) { return n % 2 == 0; }
void swap(int& a, int& b) { int tmp=a; a=b; b=tmp; }

Передача по значению vs по ссылке

Аналогия: Передача по значению — отправить КОПИЮ документа. Передача по ссылке — дать доступ к ОРИГИНАЛУ. В первом случае изменения копии не влияют на оригинал.

#include <iostream>
using namespace std;

// По значению: получаем КОПИЮ — оригинал не изменяется
void tryModify(int value) {
    value = 999; // Изменяем копию
    cout << "Внутри функции: " << value << endl; // 999
}

// По ссылке: получаем ССЫЛКУ — изменяем оригинал!
void modify(int& value) {
    value = 999;
    cout << "Внутри функции: " << value << endl; // 999
}

// Swap — классика, требует ссылок
void swap(int& a, int& b) {
    int temp = a; a = b; b = temp;
}

// const ref: передаём ссылку но запрещаем изменение (для эффективности)
void printString(const string& s) {
    cout << "Строка: " << s << endl;
    // s = ""; // ОШИБКА: s is const reference
}

// Возврат через ссылочный параметр (для нескольких значений)
void minMax(const int arr[], int n, int& minVal, int& maxVal) {
    minVal = maxVal = arr[0];
    for (int i=1; i<n; i++) {
        if (arr[i] < minVal) minVal = arr[i];
        if (arr[i] > maxVal) maxVal = arr[i];
    }
}

int main() {
    int x = 42;
    tryModify(x);
    cout << "После tryModify: " << x << endl; // 42 — не изменился!
    
    modify(x);
    cout << "После modify: " << x << endl;    // 999 — изменился!
    
    int a=10, b=20;
    swap(a, b);
    cout << "a=" << a << ", b=" << b << endl; // a=20, b=10
    
    printString("Большая строка — не копируется!");
    
    int arr[] = {3, 1, 7, 2, 9, 4};
    int minV, maxV;
    minMax(arr, 6, minV, maxV);
    cout << "Min=" << minV << ", Max=" << maxV << endl; // Min=1, Max=9
    
    return 0;
}

Перегрузка функций (Overloading)

#include <iostream>
#include <cmath>
using namespace std;

// Несколько функций с одним именем — разные параметры
double area(double r)              { return M_PI * r * r; }           // Круг
double area(double w, double h)    { return w * h; }                  // Прямоугольник
double area(double a, double b, double c) {                           // Треугольник (Герон)
    double s = (a+b+c)/2;
    return sqrt(s*(s-a)*(s-b)*(s-c));
}

// print для разных типов
void print(int n)    { cout << "int: " << n << endl; }
void print(double d) { cout << "double: " << d << endl; }
void print(string s) { cout << "string: " << s << endl; }
void print(bool b)   { cout << boolalpha << "bool: " << b << endl; }

// abs для разных типов (уже встроено, но как пример)
int    myAbs(int n)    { return n<0 ? -n : n; }
double myAbs(double n) { return n<0 ? -n : n; }

int main() {
    cout << area(5.0) << endl;         // Круг r=5
    cout << area(4.0, 6.0) << endl;   // Прямоугольник 4×6 = 24
    cout << area(3.0, 4.0, 5.0) << endl; // Треугольник = 6
    
    print(42);            // int: 42
    print(3.14);          // double: 3.14
    print(string("Hi")); // string: Hi
    print(true);          // bool: true
    
    cout << myAbs(-7) << endl;     // 7
    cout << myAbs(-3.14) << endl; // 3.14
    
    return 0;
}

Параметры по умолчанию

#include <iostream>
using namespace std;

// Параметры по умолчанию — ВСЕГДА справа!
void drawLine(int length=10, char symbol='-') {
    for (int i=0; i<length; i++) cout << symbol;
    cout << endl;
}

double power(double base, int exp=2) { // По умолчанию возводим в квадрат
    double result = 1;
    for (int i=0; i<exp; i++) result *= base;
    return result;
}

void greet(string name, string greeting="Привет", string ending="!") {
    cout << greeting << ", " << name << ending << endl;
}

int main() {
    drawLine();          // Использует дефолты: 10 символов '-'
    drawLine(20);        // 20 символов '-'
    drawLine(5, '*');    // 5 символов '*'
    drawLine(3, '#');
    
    cout << power(5) << endl;       // 25 (5²)
    cout << power(2, 10) << endl;   // 1024 (2¹⁰)
    cout << power(3, 3) << endl;    // 27 (3³)
    
    greet("Иван");                   // Привет, Иван!
    greet("Мария", "Добро пожаловать", ".");
    
    return 0;
}

Лямбда-функции (C++11)

#include <iostream>
#include <vector>
#include <algorithm>  // sort, find_if, for_each
using namespace std;

int main() {
    // Лямбда: [захват](параметры) { тело }
    
    // Простая лямбда
    auto square = [](int n) { return n * n; };
    cout << square(5) << endl;  // 25
    
    // Лямбда с несколькими строками
    auto factorial = [](int n) {
        long result = 1;
        for (int i=2; i<=n; i++) result *= i;
        return result;
    };
    cout << "5! = " << factorial(5) << endl; // 120
    
    // Захват переменных из внешнего контекста
    int threshold = 5;
    auto isLarge = [threshold](int n) { return n > threshold; };
    cout << isLarge(10) << endl; // 1 (true)
    cout << isLarge(3)  << endl; // 0 (false)
    
    // Захват по ссылке [&]
    int count = 0;
    auto increment = [&]() { count++; }; // захватывает count по ссылке
    increment(); increment(); increment();
    cout << "count = " << count << endl; // 3
    
    // Лямбды в алгоритмах
    vector<int> nums = {5, 2, 8, 1, 9, 3, 7, 4, 6};
    
    // Сортировка по убыванию
    sort(nums.begin(), nums.end(), [](int a, int b) { return a > b; });
    cout << "Убывание: ";
    for (int n : nums) cout << n << " "; // 9 8 7 6 5 4 3 2 1
    cout << endl;
    
    // Найти первое число больше 6
    auto it = find_if(nums.begin(), nums.end(), [](int n) { return n > 6; });
    if (it != nums.end()) cout << "Первое > 6: " << *it << endl; // 9
    
    // Вывести все чётные
    cout << "Чётные: ";
    for_each(nums.begin(), nums.end(), [](int n) {
        if (n%2==0) cout << n << " ";
    });
    cout << endl;
    
    return 0;
}

Рекурсивные функции

#include <iostream>
using namespace std;

// Факториал
long factorial(int n) {
    if (n <= 1) return 1;       // Базовый случай
    return n * factorial(n-1);  // Рекурсивный вызов
}

// Фибоначчи
int fib(int n) {
    if (n <= 0) return 0;
    if (n == 1) return 1;
    return fib(n-1) + fib(n-2);
}

// НОД (наибольший общий делитель, алгоритм Евклида)
int gcd(int a, int b) {
    return b == 0 ? a : gcd(b, a % b);
}

int main() {
    for (int i=0; i<=7; i++) cout << i << "! = " << factorial(i) << endl;
    
    cout << "Фибоначчи: ";
    for (int i=0; i<10; i++) cout << fib(i) << " ";
    cout << endl;
    
    cout << "НОД(48,18) = " << gcd(48, 18) << endl; // 6
    cout << "НОД(100,75) = " << gcd(100,75) << endl; // 25
    
    return 0;
}

Итого: что мы узнали

  • ✅ Функция — блок кода с именем для многократного использования
  • ✅ Прототип — объявление без тела (для использования до определения)
  • ✅ Передача по значению — копия; оригинал не меняется
  • ✅ Передача по ссылке (&) — доступ к оригиналу; изменения сохраняются
  • ✅ const& — эффективная передача без копирования и без изменений
  • ✅ Перегрузка: одно имя + разные параметры — компилятор выбирает нужное
  • ✅ Параметры по умолчанию — не нужно передавать все аргументы
  • ✅ Лямбды — анонимные функции: [захват](параметры) { тело }