VLA - Variable-length array, массив переменной длинны (определяет свою длинну в момент исполнения программы)
Переменная внутри скобочек при создании массива, int a[b];
- это всегда VLA, даже если b
- const
переменная, в си. Подробнее про это в си
(const
по переменной всё что делает - запрещает её изменять и требует иниц. при создании, => такие переменные могут быть инициализированны в момент запуска программы, например - другими переменными, которые зависят от ввода)
В си++ нет, не было и не ожидается VLA вообще (в стандарте, в некоторых компиляторах включенно в качестве расширения компилятора)
Следовательно const
переменные которые могут быть инициализированны в момент компиляции - подходят для создания обычных массивов. Если мы попытаемся в си++ сделать массив длинны которая определяется в момент исполнения прграммы - по стандарту оно не должно скомпилироваться, однако некоторые компиляторы поддерживают VLA и в си++!
Ещё в си++ есть специальное слово - constexpr
, оно говорит, что эта переменная обязана быть иниц. в момент компиляции.
#include
не подключает никуда никакие файлы!
a.cpp
main.cpp
Чтобы функция g() была доступна в main.cpp нужно написать ей прототип в main.cpp!. Прототип позволяет вызвать функцию реализованную в другом файле, не инклуды. #include<...>
- это простая текстовая вставка всего содержимого файла внутри <>, чтобы не писать прототипы ручками в каждом новом .сpp файле! Инклуды не подключают никаких библиотек, функций и прочего, это делают прототипы!
Динамические
dll
библиотеки требуются в момент запуска, а статические (вшиваются в программу) в момент компиляции
В си++ есть три типа переменных:
g_
Так же принято по разному выделять public
и private
поля класса, так же константы времени компиляции (обычно заглавными буквами, как #define
)На си в функции пустые скобки - g()
означает, что у функции неизвестное количество, и типы аргументов(её можно вызвать с любым количеством любых аргументов) Отсутвие аргументов можно указать void
в скобках - g(void)
На c++ отсутвие аргументов в скобках эквивалент void
, что у функции нет аргументов!
В си так по историческим причинам, в первых версиях языка вообще не было прототипов, и когда мы пишем функцию без прототипа - компилятор считает, эта функция существует, принимает те параметры, которые мы ей передали и возвращает int
, тогда были только int
, прототип это способ чтобы компилятор проверил что бы всё совпало (вызов с реализцией). Язык C наследник языка B, в которых вообще не было типов.
У static
есть 2 значения:
static
по глобальному обьекту - ограничивает видимость этого обьекта этим файлом, время жизни останется тем же (по умолчанию видимость глобального обекта - вся программа) (глобальная переменная, функция, структура) (так можно писать функции в .h
файлов, но она будет в копии у каждого .cpp
который подключил .h
)
static
по переменной в функции - означет, что эта переменная одна на все функции
Пременная a
живет всю программу, но область видимости (имени a
) у неё только в функции f(), переменная b
живет всю программу и области видимости у неё вся программа. (глобальные переменные можно называть как статические в функции, но тогда их не будет видно в этой функции)
В СИ глобально живущие обьекты (так же static
переменные) могут быть инициализированны только ПРОСТЫМИ выражением вычисляемым в момент компиляции, НЕ зависящими от других глобальных обьектов:
В C++ разрешили иниц. глобальные переменные сложными выражениями, например:
Вывод после запуска:
Глобальные переменные иниц ДО main()
!
Настоящее начало программы происходит в: недрах OC -> С runtime (даже в C++) -> main
С runtime:
В C++ также разрешили иниц. глобальные переменные глобальными обьектыми: (разделим код на файлы)
a.cpp
main.cpp
Ещё один случай:
a.cpp
main.cpp
порядок можно опредлелить, но оч сложно
Давайте вернемся к первому примеру:
Распаралеллим этот код, тогда у кучи тредов будет неиниц глобальная (по времени жизни) переменная, которую оин должны иниц каким-то своим значением, они одновременно начнут писать туда свои переменные и может получиться мусор, однако - по стандарту b
должно быть иниц только один раз, что означает, что это место образует критическую секцию, которая сольет все треды в один и выстроит их в порядок очереди, этот код очень трудоемкий и содержит в себе кучи вызовов системных функций, ожиданиями, вызовами ядра и т.д.
Если бы тут было просто static b = 2;
этого всего бы не было, то, что нам разрешили писать static b = a + 2;
выглядит как почти ничего не изменилось, а на самом деле всё радикально изменилось! (Это задача консенсуса - договориться какая переменная, из какого треда должна иниц переменную b
)
В этом вся идея C++, что тут более тяжелые вещи, но эти вещи могут быть значительно дольше исполняться.
Чтобы не писать постоянно свои namespace
, в стандартной библиотеки си++ все функции убрали в namespace std (в том числе сишные, вместо math.h
стало cmath
и также с остальными).
Теперь каждый вызов стандартной функции надо начинать с std::
, что заметно бесит, так что разработчики добавили возможность не писать постоянно название namespace - using namespace std; (их можно написать несколько, главное чтобы не пресеклись функции (имя + те же аргументы))
Анонимные namespace:
Делает магическое имя namespace и сразу же пишет для него using namespace
Таким образом пытались избавиться от static
по глобальным обьектам, и даже задиплекейтили его в стандарте 2003, но андипликейтили в стандарте 2011, поняв абсурдность, подробнее.
namespace единственное, что делает - ещё больше заворачивает имена функции в name mangling
namespace можно обернуть в другой namespace
Посмотрим на наши функции abs
:
Они выглядят как дословная копипаста тела функции, различие только в типах
Для таких случаев придумали шаблоны:
В <>
указываются аргументы шаблона, типы или целочисленные константы времени компиляции.
Шаблонные функции не создают код совсем! Компилятор создает копию функции только при обнаружении вызова с новым типом. (создает функцию подставив вместо шаблона какой-то тип)
Шаблоны - способ автокопипасты когда разница только в типе.
Вместо typename
можно писать class
- template <class T>
T
можно использовать внутри шаблона в любом месте, где можно использовать тип.
Можно сделать специальные типы шаблоннов для конкретных типов:
(Напомни мне добавить то, что я знаю про шаблонны)
Ссылки - синтаксический сахар на указатели, выглядит как обьект, но на самом деле указатель, не может быть привязанна к NULL, не может быть привязанна к константе, НЕ меняется после создания.
Нельзя создать ссылку на ссылку, будет указывать на то, что указывает первая ссылка
Это были так называемые l-value
ссылки, умеют связываться с обьектами, не с значениями!
int &&y
- r-value
ссылка, то что умеет связываться со значениями, но не с обьектами!
Если коротко:
l-value
- то, что может стоят слева от знака =
r-value
- то, что может стоять справа от знака =
const int &y
- константная ссылка может связаться с обьектом и со значением, но через неё нельзя менять обьект!
const int &&y
тоже имеет некоторый смысл, подробнее
auto (в старом значении) - это место размещения данных, по локальным переменным в стандарте 3 возможных варианта, где размещать обьект:\
auto
в новом значении - тип этого обьекта такой, который в него присваивается. (то есть можно писать перед иеменем переменной только в момент иниц.) (взять тип справа от знака =
) (придумали чтобы не писать длинный типы посл шаблонов)
auto вычисляется в момент компиляции и подставляет нужный тип
Full - C_C++, 8 лекция, 16.04.2022, Скаков