hse-oimp.github.io

Лекция №3 Ключевое слово auto. Константность. Функции. Ссылки. Указатели.

auto

auto - ключевое слово, которое показывает компилятору, что тип переменной восстанавливается по значению переменной. ЭТО НЕ ДИНАМИЧЕСКАЯ ТИПИЗАЦИЯ!

auto делает код менее понятным, так как не видно, какой тип используется.

auto i1 = 4; // int
auto i2 = 15ull; // unsigned long long
...

Константность

Ключевое слово const создает переменную, которую нельзя изменять. Пример неправильного кода:

int main() {
    const int i = 10;
    i = 20; // Error
    return 0;
}

Ссылки

Хотим сделать функцию, которая печатает строку:

void PrintString(std::string str) {
    std::cout << str << std::endl;
}

int main() {
    std::string str = "abracadra";
    PrintString(str);
    return 0;
}

Проблема этого кода в том, что он копирует строку, а это неэффективно.

Пытаемся исправить это и передаем в функцию ссылку на переменную (новое имя для переменной, хранит в себе адрес в памяти). Изменение ссылки повлияет и на исходный объект, в отличие от копии.

void PrintString(std::string& str) {
    std::cout << str << std::endl;
}

int main() {
    std::string str = "abracadra";
    PrintString(str);
    return 0;
}

Ссылка на переменную является типом, но из-за особенностей синтаксиса & “приклеивается” только к переменной. Одна из причин объявлять по одной в строке.

Новая проблема:

void PrintString(std::string& str) {
    std::cout << str << std::endl;
}

int main() {
    const std::string str = "abracadra";
    PrintString(str);
    return 0;
}

Такой код не запускается, так как пытаемся передать ссылку на константный объект (его нельзя изменять) в функцию, которая может его изменить (так как в шапке функции не указано const).

Итоговый код:

void PrintString(const std::string& str) {
    std::cout << str << std::endl;
}

int main() {
    const std::string str = "abracadra";
    PrintString(str);
    return 0;
}

Ссылку на неконстантный объект можно передавать туда, где ожидается константная ссылка.


int main() {
    std::string str = "aaa";
    const std::string& str2 = str + "!!!";
    return 0;
}

Этот код рабочий, хотя ссылке присваиваем временный объект. Так происходит, потому что const std::string& показывает, что объект не будет изменяться, а будет использоваться по значению, поэтому достаточно сохранить куда-то это значение и дальше с ним работать.


std::string& CreateString() {
    std::string str = "aaa";
    return str;
}

Такой код не запустится, так как возвращается ссылка на объект, который удаляется после выполнения функции. Но компилятор может не заметить подобные ошибки, поэтому надо следить за областью жизни переменных при работе со ссылками.


Обычно область жизни переменной - между парой фигурных скобок, но есть исключения: 1) Глобальные переменные создаются до входа в main, удаляются после выхода. 2) Ключевое имя static: будет инициализировано во время первого момента работы программы, а дальше будет вести себя аналогично глобальной переменной. Удаляется в момент завершения программы, но доступна только в своем блоке. —

Ссылку нельзя не инициализировать.

Указатели

int main {
    int i = 10;
    int *ptr = nullptr; // указатель в никуда
    int *iPtr = &i;

    *iPtr = 15;

    return 0;
}

Теперь & обозначает взятие адреса переменной. У одинаковых символов могут быть разные значения в зависимости от контекста.

Изменения значения указателя меняет и исходный объект. Поведение указателя похоже на поведение ссылки.


Константность указателей

Есть константность трех типов:

1) Указатель на константный объект : c++ int main() { int i = 10; const int* iPtr = &i; iPtr = nullptr; // OK *iPtr = 10; // Ошибка return 0; }

2) Константный указатель на объект (похоже на ссылку): c++ int main() { int i = 10; int* const iPtr = &i; iPtr = nullptr; // Ошибка *iPtr = 10; // ОК return 0; }

3) Константный указатель на константный объект: c++ int main() { int i = 10; const int* const iPtr = &i; iPtr = nullptr; // Ошибка *iPtr = 10; // Ошибка return 0; }

Такие выражения удобно читать справа налево. Аналогичным образом устроена ссылка на константный объект.

Арифметика на указателях

int main() {
    std::string str = "Hello";
    const char* cStr = str.c_str();

    const char* p = cStr + 3 * sizeof(*cStr);
    std::cout << *cStr; // 'H'
    std::cout << *(cStr + 3); // 'l'
    // *(cStr + 3) == *p
    std::cout << (p - cStr); // '3'
    std::cout << cStr; // 'Hello'. Оператор вывода переопределен для const char* 
    return 0;
}