Лекция 3

Многофайловая компоновка

Говорят, что переменная описана (определена), если она объявлена и под неё выделена память. Рассмотрим следующий пример, пусть в проекте есть два файла:

/* a.cpp */
extern int n;    // "Где-то дальше определено"
void f(int);     // Предварительное объявление

void main() {
    n = 5; f(n);
}


/* b.cpp */
#include <iostream>
int i;           // Описание переменной

// Определение функции
void f(int i) {
    std::cout << i;
}

В C++ имеет место независимая компиляция: все файлы проекта компилируются независимо один от другого. Компиляция состоит из этапа собственно компиляции и этапа линковки.

Описание процесса сборки программы

Заголовочные файлы

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

/* myheader.h */
extern int n;
void f(int);

/* myheader.cpp */
#include <iostream>
int n;

void f(int i) {
    std::cout << i;
}


/* a.cpp */
#include "myheader.h"

void main() {
    n = 5; f(n);
}

/* b.cpp */
#include "myheader.h"

void makeZero() {
    n = 0;
}

Имена пользовательских заголовочных файлов в директиве include заключаются в двойные кавычки, а имена стандартных заголовочных файлов — в угловые скобки. Стандартные заголовочные файлы расположены в /INCLUDE. Поиск пользовательских файлов производится в текущем каталоге.

Замечание: inline-функции не сохраняются в исходном коде, так как больше не используются (а сразу встраиваются на место вызова). Чтобы воспользоваться такими функциями в другой единице компиляции, их нужно поместить в заголовочный файл.

Содержимое заголовочных файлов

Что может содержать заголовочный файл:

Пример
Определения типов struct point { int x, y; };
Шаблоны типов template<class T> class V { /* ... */ }
Описания функций extern int strlen(const char*);
Определения встраиваемых функций inline char get() { return *p++; }
Описания данных extern int a;
Определения констант const float pi = 3.141593;
Перечисления enum bool { false, true };
Описания имен class Matrix;
Команды включения файлов #include <signal.h>
Макроопределения #define Case break;case
Комментарии /* проверка на конец файла */

В заголовочном файле никогда не должно быть:

Пример
Определений обычных функций char get() { return *p++; }
Определений данных int a;
Определений составных констант const tb[i] = { /* ... */ };

Глобальные описания в C++ и необходимость пространств имен

При простом определении глобальных сущностей они объединяются в глобальном пространстве имен. Для создания локального пространства имен используется ключевое слово namespace.

namespace MyNamespace{
    ... // содержимое пространства имён: типы, функции, что-угодно…
}

Пространства имен и заголовочные файлы

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

/* header.h */
namespace MyNamespace{
    extern int n;
    void f();
}

Основные этапы сборки проекта

  1. Препроцессирование.
  2. Компиляция каждого *.cpp-файла в объектный код (файлы *.obj или *.o).
  3. Линковка — сборка всех объектных файлов в один исполняемый (*.exe или ELF).

Ошибки во время линковки

Особенности линковки

  1. На главную