VBTerminator писал(а):В каких "сторонних юнитах"? Что это означает?
Проекты обычно состоят более чем одного файла (исходника).
Каждый файл (исходник) компилируется компилятором в объектный файл (обычно .obj в Win-мире и .o в nix-мире).
Все полученные объектные файлы затем линкером слинковываются в единый выходной файл (exe, dll, и т.п.)
К тому же объектные файлы можно собирать в своего рода архивы — библиотеки (статические библиотеки, lib-файлы в Win-мире, .a-файлы в nix-мире (a, потому что archive)).
Компилятор (как приложение, например cl.exe) вызывается для каждого исходного файла отдельно. Когда компилятор компилирует например bar.cpp, он ничего не знает о том, что раньше компилировал перед этим foo.cpp, а после будет компилировать baaz.cpp. Иными словами, при компиляции каждый файл компилируется так, как будто он единственный во вселенной, и компилятору ничего не известно о сущностях во всех остальных файлах.
Поэтому, если в одном файле
размещена какая-то сущность (переменная, функция и т.п.), а ещё 40 файлов хотят к ней обращаться, то во всех этих файлах нужно
объявить эту сущность, чтобы её можно было использовать. То есть, если есть функция, то в одном файле будет её код, а ещё в 30 файлах — вызов этой функции, то в этих 30 файлах должен быть прототип, чтобы компилятор (при обработке именно этих файлов) знал, что такая функция есть, что у неё такие-то аргументы такого-то типа и такой-то тип возвратов, и мог проверить соответствие типов и сообщить об ошибке. Для этой цели используют h-файлы, чтобы в каждом из 30 файлов не объявлять кучу прототипов сущностей из другого файла. Но можно объявить и вручную, без h-файлв. Поэтому люди, называющие h-файлы в общем случае библиотечными — глупцы, и киньте пожалуйста в них тухлое яйцо, если они будут при вас нести в массы эту дезинформацию (это обычно авторы книг «С/С++ для новичков» или всякие недо-преподаватели, которые сами учились по таким книжкам).
Так что, компилируя отдельный С/С++-исходник, вы можете объявить прототипы 1000 функций со случайными названиями и делать вызовы всех этих функций. То, что самих этих функций (их реализации) вообще не существует в природе — компилятору не помешает скомпилировать код в объектный файл. Просто в этом файле будет 1000 внешних зависимостей (external dependency).
Именно поэтому глупостью являются заявления, что функции вроде printf, scanf, fopen, memcpy являются «встроенными функциями языка С» (или языка С++). Нифига они не являются. Они просто объявлены в h-файлах, то есть там объявлены их прототипы, и компилятор обрабатывает их на общих основаниях с какой-нибудь функцией «muhaha_megalol». И только потом при линковке «внешние зависимости» с одной стороны встречаются с «предлагаемыми сущнстями» с другой стороны (со стороны сишного рантайма).
Если при линковке не указывать obj/lib-файлы (относящиеся к CRT), то линковка проекта, в исходниках которого есть обращения к таким функциям (вроде printf или strcmp), провалится даже при том, что подключены соответствующие h-файлы.
Дело здесь в другом: существование таких функций, их набор, их функциональность, их прототипы определены стандартом языка. И стандарт предписывает не только существования компилятора, но и существование реализации всех этих функций (называмых функциями стандартной библиотеки Си, если мы говорим о Си) и возможность «слинковаться» с этими функциями.
При этом вызов таких функций обрабатывается компилятором на общих основаниях (как вызов любой другой функции, даже несуществующей, прототип которой объявлен), линковка обрабатывается линкером тоже на общих основаниях, а само использование этих функций не является обязательным, равно как и необходимость при линковке к набору своих obj/lib-файлов добавлять obj/lib-файлы, соответствующие стандартной библиотеке (то есть содержащие реализацию этих функций).
Но надо сделать оговорку: есть такое понятие, как intrinsic-функции. Хотя мы говорили, что сам компилятор Си плевать хотел на функции стандартной библиотеки Си, и обрабатывает их на общих основаниях, сейчас ситуация несколько отличная: для некоторых функций, соответствующих функциям стандартной библиотеки, всё-таки сделана в компилторе специфичная обработка. Такие функции называются intrinsic-ами. Например это memcpy, встретив вызов которой, компилятор не генерирует вызов «внешней функции», а генерирует машинный код, который непосредственно занимается копированием байтов.
А вот уже при линковке все внешние зависимости должны быть разрешены, и если какой-то объектный файл зависит от некоторой сущности, которая не предоставляется ни одним из других объектный файлов, тогда такой набор просто не слинкуется, хотя есть ключ линкера, заставляющий его наплевать на это.
Соответственно, пусть мы делаем некоторый компонент (в абстрактном смысле этого слова), который у нас у самих распределён на 10 файлов-исходников. А использоваться этот компонент будет потом из 1000 остальных файлов исходников. И мы хотим сделать некоторые функции как-бы private-ными, чтобы они были доступны только внутри исходников, относящихся к компоненту. А остальные функции — публичными, чтобы они были доступны всем остальным.
Так вот, без всяких public и private, и даже не чистом голом Си (в котором нет public и private в принципе) можно такое провернуть: просто мы делаем две версии h-файла — в одной версии объявлены прототипы и «public» и «private» методов, в другой версии — только прототипы «public» функций).
В файлах-исходниках, относящихся к самому компоненту мы подключаем один h-файлах. Файлы, не относящиеся к компоненту, подключают другой h-файл.
Таким образом, обращение к «приватной» функции из исходника-инсайдера будет успешно скомпилировано и успешно склинковано. Обращение к «приватной» функции из исходника-аутсейдера не будет успешно скомпилировано, потому что компилятор при обработке кода файла-аутсайдера до того момента, как дойти до вызова, ни разу не встретил прототип функции (он его не встретил, потому что подключалась версия h-файлов «для аутсайдеров», не содержащая прототипов «приватных» функций).
Эта система даёт возможность всё-таки вызвать private-функцию из файла-аутсайдера: достаточно просто объявить в нём прототип такой функции. Тогда компилятор уже не ругнётся, а линковка будет в любом случае успешной, потому что все сущности выставляются в «предоставляемые».
VBTerminator писал(а):То есть для моих целей достаточно класса со статичными методами.
Нет. Нужно ещё сами функции объявлять с ключевым словом
static, потому что экземпляр будет всего-лишь один (и всегда один), и передача указателя на экземпляр не нужна.
И вызывать не так:
object.method();, а так
object::method()