Слайд 3Содержание лекции
Многофайловые программы
Причины использования многофайловых программ
Взаимодействие между файлами
Примеры
Вопросы из теста
Слайд 4Причины использования
Разделение кодовой базы
Разделение работы над проектом на несколько разработчиков
Удобство организации архитектуры
программы
Сокращение кодовой базы
Упрощение внесения правок
Слайд 5Взаимодействие между файлами
Взаимодействие между скомпилированными по отдельности, но скомпонованными вместе исходными файлами
(.cpp)
Взаимодействие с заголовочными файлами (.h)
Слайд 6Взаимодействие исходных файлов
Три основных элемента исходных файлов:
Переменные
Функции
Классы
Для каждого типа элементов есть свои
правила межфайлового взаимодействия.
Слайд 7Межфайловые переменные
Объявление переменной – декларация её типа и имени.
Переменная определяется, когда происходит
резервация места под не в памяти.
int some_var;
extern int some_var;
Слайд 8Межфайловые переменные
// File A
int global_var;
// File B
global_var = 3; // Error!
Слайд 9Межфайловые переменные
// File A
int global_var;
// File B
extern int global_var; // Declaration in
File B
global_var = 3; // Good!
// But:
extern int global_var = 27; // extern will be ignored!
Слайд 10Статические переменные
// File A
static int global_var; // Only visible in A
// File
B
static int global_var; // Only visible in B
В этом случае статическая переменная имеет внутреннее связывание. Нестатические глобальные переменные имеют внешнее связывание.
Для сужения области видимости можно использовать также пространства имен.
Полезно: Внутренняя и внешняя линковка в C++
Слайд 11Совет
Не использовать глобальных переменных
Если очень хочется, и по логике программы переменная используется
только в текущем файле, то лучше сделать переменную статической.
В контексте глобальных переменных слово static просто сужает область видимости до одного файла.
Слайд 12Константы
Переменная, определенная с помощью const, в общем случае не видна за пределами
одного файла.
Но если очень нужно:
// File A
extern const int const_var = 99;
// File B
extern const int const_var;
Слайд 13Межфайловые функции
Объявление функции задает ее имя, тип возвращаемых данных и типы всех
аргументов.
Определение функции — это объявление плюс тело функции (тело функции — код, содержащийся внутри фигурных скобок).
Всё, что нужно знать при вызове – это имя, тип и типы аргументов. Все это есть в объявлении функции.
Слайд 14Пример
// File A
int add(int a, int b) {
return a + b;
}
//
File B
int add(int, int);
int main() {
cout << add(10, 10) << endl;
return 0;
}
Слайд 15Заголовочные файлы
Указанный после директивы #include файл просто вставляется в исходный (например, iostream).
Пример:
#include
#include "add.h"
Слайд 16Заголовочные файлы
// sum.h
extern int not_found = 404;
int sum(int, int);
// sum.cpp
int sum(int a,
int b) { return a + b; }
// main.cpp
#include "sum.h"
int main() {
cout << sum(10, 10) << endl;
cout << not_found << endl;
// 20
// 404
}
Слайд 17Заголовочные файлы
В заголовочном файле можно хранить объявления, но не определения переменных или
функций.
В противном случае, использование этого файла в других (если только данные не static или const) приведет к ошибке компоновщика «повторные определения».
Слайд 18Ошибка повторения включений
// example.h
#if !defined(EXAMPLE_H)
#define EXAMPLE_H
int global_var;
int sum(int a, int b) {
return a + b;
}
#endif
Слайд 19Пространства имен
Пространства имен предоставляют более гибкий подход к вопросу управления видимостью переменных
и функций.
Пространство имен — это некая именованная область файла.
Слайд 20Пример
namespace geo {
const double PI = 3.14159;
double L(double radius) {
return 2 * PI * radius;
}
}
int main() {
cout << geo::PI << endl;
cout << geo::L(10) << endl;
// 3.14159
// 62.8318
}
Слайд 21Пример: using
namespace geo {
const double PI = 3.14159;
double L(double radius)
{
return 2 * PI * radius;
}
}
L(100); // Not works!
using namespace geo;
L(100); // Works!
Слайд 22Неоднократное определение пространств имен
namespace geo {
const double PI = 3.14159;
}
// Other
code ...
namespace geo {
double L(double radius) {
return 2 * PI * radius;
}
}
Слайд 23Пример
// geo.h
namespace geo {
const double PI = 3.14159;
double L(double radius);
}
//
geo.cpp
#include "geo.h«
double geo::L(double radius) { return 2 * PI * radius; }
// main.cpp
#include "geo.h«
int main() {
cout << geo::PI << endl;
cout << geo::L(10) << endl;
}
Слайд 24Статические и динамические библиотеки
Библиотека — это фрагмент кода, который можно многократно использовать
(переиспользовать) в разных программах.
Библиотека в C++ состоит из:
Заголовочный файл, который объявляет функционал библиотеки.
Предварительно скомпилированный бинарный файл, содержащий реализацию функционала библиотеки.
Есть 2 типа библиотек: статические и динамические.
Слайд 25Статическая библиотека
Статическая библиотека состоит из подпрограмм, которые непосредственно компилируются и линкуются с
разрабатываемой программой.
При компиляции программы, которая использует статическую библиотеку, весь необходимый функционал статической библиотеки становится частью исполняемого файла.
В Windows статические библиотеки имеют расширение .lib (library), в Linux .a (archive).
Слайд 26Статическая библиотека
Преимущество:
Всего лишь один (исполняемый) файл, чтобы пользователи могли запустить и использовать
программу. Статические библиотеки становятся частью программы
Недостатки:
Копия библиотеки становится частью каждого исполняемого файла, что может привести к увеличению размера файла
Для обновления статической библиотеки придется перекомпилировать каждый исполняемый файл, который её использует
Слайд 27Динамическая библиотека
Динамическая библиотека состоит из подпрограмм, которые подгружаются в разрабатываемую программу во
время её выполнения.
При компиляции программы, которая использует динамическую библиотеку, эта библиотека не становится частью исполняемого файла.
В Windows динамические библиотеки имеют расширение .dll (dynamic link library), в Linux .so (shared object).
Слайд 28Динамическая библиотека
Преимущества:
Разные программы могут совместно использовать одну копию динамической библиотеки, что значительно
экономит используемое пространство
Можно обновить до более новой версии без необходимости перекомпиляции всех исполняемых файлов, которые её используют.
Недостаток:
Механизм взаимодействия может быть непонятен для новичков
Слайд 30Библиотеки импорта
Библиотека импорта (import library) – это библиотека, которая автоматизирует процесс подключения
и использования динамической библиотеки.
В Windows это обычно делается через небольшую статическую библиотеку (.lib) с тем же именем, что и динамическая библиотека (.dll).
В Linux общий объектный файл (с расширением .so) дублируется сразу как динамическая библиотека и библиотека импорта.
Слайд 31Пример
// math.h
#if !defined(MATH_H)
#define MATH_H
int round(double r);
#endif
// math.cpp
#include "math.h"
int round(double r) {
return
(r > 0.0) ? (r + 0.5) : (r - 0.5);
}
Слайд 32Пример
#include "math.h"
#include
using namespace std;
int main() {
double number;
cout << "Enter
the number to round: ";
cin >> number;
cout << round(number) << endl;
}
Слайд 33Пример: запускаем как обычно
g++ -c app.cpp -o app.o
g++ -c math.cpp -o math.o
g++
app.o math.o -o app.out
./app.out
// Enter the number to round: 11.11
// 11
Слайд 34Пример: статическая библиотека
Получаем файл библиотеки:
ar cr libmath.a math.o
Используем полученный файл библиотеки:
g++ app.o
libmath.a -o app.out
g++ app.o -L. -lmath -o app.out
Проверяем:
./app.out
Слайд 35Пример: динамическая библиотека
Создаем файл динамической библиотеки:
g++ -shared -o libmath.so math.o
Используем полученный файл
библиотеки:
g++ app.o libmath.so -o app.out
g++ app.o -L. -lmath -o app.out
Проверяем:
./app.out
./app.out: error while loading shared libraries: libmath.so: cannot open shared object file: No such file or directory
Слайд 36В чем проблема?
Пользователь должен подсказать операционной системе, где искать необходимые библиотеки (libmath.so).
В
случае с Linux:
Добавить путь к библиотеке к переменной окружения LD_LIBRARY_PATH
Использовать флаг -rpath, чтобы указать путь к динамической библиотеке при сборке исполняемого файла
Слайд 37Исправленный пример
export LD_LIBRARY_PATH = $LD_LIBRARY_PATH:/c/dev/OAIP/static_example/
./app.out
Или
g++ app.o -L. -lmath -o app.out -Wl,-rpath,/c/dev/OAIP/static_example/
./app.out
Слайд 38Пример вопроса на экзамене
Глобальная переменная определена в файле A. Чтобы получить доступ
к ней из файла B, необходимо:
Определить ее в файле B, используя extern;
Определить ее в файле B, используя static;
Расслабиться;
Должно быть константой;
Объявить ее в файле B, используя extern.
Слайд 39Пример задачи на экзамене
Создать библиотеку для нахождения параметров треугольников (тип треугольника, площадь,
периметр и т.п.).
Использовать заголовочные файлы и пространства имен.