Форум программистов, компьютерный форум, киберфорум
С++ для начинающих
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск  
 
 
Рейтинг 4.84/68: Рейтинг темы: голосов - 68, средняя оценка - 4.84
74 / 37 / 3
Регистрация: 23.09.2012
Сообщений: 408

Как правильно из одного cpp подключить другой

06.10.2012, 00:02. Показов 15975. Ответов 34
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Есть 2 .cpp файла: a.cpp и b.cpp. Когда пишу:

C++
1
2
/* a.cpp */
#include "b.cpp"
Выдает ошибку. Как правильно из одного cpp подключить другой?
0
cpp_developer
Эксперт
20123 / 5690 / 1417
Регистрация: 09.04.2010
Сообщений: 22,546
Блог
06.10.2012, 00:02
Ответы с готовыми решениями:

Перенос данных из одного cpp в другой
Здравствуйте. Допустим, есть два cpp файла, в первом прописано событие "щелчок по кнопке" void Window::on_pushButton_clicked() //...

Передать значения переменных из одного cpp в другой
Здравствуйте. У меня возникла необходимость в чужом проекте передать значения переменных из одного cpp в другой. Я подключил h-файл и...

Как правильно подключить два винчестера(один sata, другой ide)?
Здравствуйте. Подскажите, как правильно подключить два винчестера(один sata, другой ide)? На sata хочу установить windows xp, на ide -...

34
Эксперт С++
5058 / 3118 / 271
Регистрация: 11.11.2009
Сообщений: 7,044
07.10.2012, 21:50
Лучший ответ Сообщение было отмечено как решение

Решение

Студворк — интернет-сервис помощи студентам
func.h
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Следующая конструкция ifndef-define-endif имеет название "страж включения".
// Она нужна для предотвращения множественного включения одного и того же файла
// в другой файл. Суть работы следующая: при первом включении файла с помощью
// define символ FUNC_H (или любой другой, главное, чтобы он был по возможности
// уникальным и совпадал в ifndef и define) ещё не определён; препроцессор
// определяет этот символ и включает весь текст, который находится между ifndef
// и endif. При повторном включении файла символ FUNC_H будет ужё определён, и
// препроцессор уже не будет включать текст, находяцийся между ifndef и endif
#ifndef FUNC_H
#define FUNC_H
 
// Прототип функции. Говорит компилятору, что эту функцию можно вызвать с двумя
// аргументами типов double и char соответственно (или совместимых с ними), а
// также, возможно, присвоить её результат переменной типа int (или совместимого
// с ним)
int func(double d, char c);
 
#endif // FUNC_H
func.cpp
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// В данном случае подключение этого заголовочного файла не обязательно,
// поскольку ничего нового компилятору он не скажет. В общем случае, когда в
// заголовочном файле определяется больше информации (например, какие-то
// пользовательские типы и т.д.), которая используется в данном файле
// реализации, подключить этот файл необходимо. Я обычно всегда подключаю
// заголовочный файл в файле реалиации, это позволяет связать эти два файла в
// единый модуль (позволяет связать программисту, а не компилятору)
#include "func.h"
 
// Здесь всё стандартно, подключаем заголовочные файлы, функции из которых будем
// использовать в файле реализации
#include <iostream>
 
// Реализация функции, объявленной в заголовочном файле
int func(double d, char c)
{
    // Выводим на экран значения переданных в функцию аргументов
    std::cout << "double d = " << d << "; char c = " << c << std::endl;
    
    // Возвращаем из функции ASCII-код второго аргумента; приведение типа
    // здесь необязательно, но явное лучше, чем неявное
    return static_cast<int>(c);
}
main.cpp
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
// Файл, в котором используется функция, объявленная в func.h и реализованная в
// func.cpp
#include <iostream>
 
// Смысл включения здесь этого файла в следующем: когда компилятор встрети вызов
// func, ему не обязательно знать, как эта функция реализована; нужно только
// знать, аргументы каких типов можно в неё передавать и значение какого типа
// она возвращает. В этом ему поможет прототип функции.
// По факту препроцессор, который вызывается ещё ДО компиляции, просто подставит
// вместо этого оператора всё, что в файле func.h располагается между ifndef
// и endif. Т.е. вместо
// 
// #include "func.h"
// 
// будет текстово подставлено
//
// #define FUNC_H
//
// int func(double d, char c);
// 
#include "func.h"
 
int main()
{
    double d = 3.14;
    char c = 'X';
    
    // Здесь происходит вызов функции func. При этом тело этой функции
    // компилятору видеть ненужно, ведь на стадии компиляции ещё нет никакого
    // вызова функции, есть только код, который этот вызов осуществляет. Здесь
    // компилятор проверит (на основании прототипа, который уже был текстово
    // включён в данный файл препроцессором), подходят ли типы аргументов,
    // передаваемых в функцию (если необходимо преобразование и компиилятор
    // может выполнить его автоматически, перед вызовом будет подставлен код,
    // выполняющий преобразование), а также можно ли результат функции присвоить
    // переменной того типа, которой он присваивается (опять же, если необходимо
    // преобразование - оно будет по возможности выполнено перед сохранение
    // результата в соответствующую переменную)
    int result = func(d, c);
    
    std::cout << result << std::endl;
    
    return 0;
}
Компиляция:
Bash
1
2
3
g++ -ansi -Wall -c -o func.o func.cpp
g++ -ansi -Wall -c -o main.o main.cpp
g++ -o main func.o main.o
Ключ -ansi говорит компилятору не делать никаких поблажек и строжайшим образом отыскивать все несоответствия стандарту. Ключ -Wall запрещает подавлять какие-либо, даже несущественные, предупреждения, и сообщать обо всём, что он посчитал хоть сколько нибудь неверным. Можно было бы обойтись и без них, но я так привык.
Клюс -c говорит, что необходимо остановится сразу после генерации объектного кода, а не вызывать линкёр и генерировать бинарный файл.
В первой строке отдельно компилируется файл func.cpp, на выходе будет объектный файл func.o. Здесь компилятору неважно, что эта функция где-то вызывается. Ему вообще не важно, вызывается она или нет. Его просят откомпилировать исходный файл в объектный, и он это делает.
Во второй строке отдельно компилируется файл main.cpp, на выходе объектный файл main.o. Компилятор при встрече вызова функции, определения которой не видит, вместо кода вызова этой функции (в котором фигурирует адрес, по которому она располагается в исполняемом файле) поставит заглушку, содержащую всю необходимую информацию, по которой линкёр сможет заглушку эту изменить на реальный адрес вызываемой функции (важно, чтобы он не ошибся и не подставил туда какой-то другой адрес, поэтому и нужна информативная заглушка). Всё, больше компилятору ничего не нужно, он не хочет видеть здесь тело функции, а возлагает поиск этого тела на линкёр.
В третьей строке, хотя и вызывается компилятор g++, на самом деле он сразу же вызовет линкёр (утилита ld), который сделает следующее (в неформальном виде, только для общего понимания процесса): отыщет все заглушки, поставленные компилятором, расшифрует информацию, которая в них хранится, найдёт в других объектных файлах реализации функций, соответствующих заглушкам, и заменит заглушки на код вызовов этих функций. В случае, если реализации какой-либо функции найдено не будет - линкёр выдаст ошибку о неразрешённой внешней ссылке. В случае множественного определения также будет выдана ошибка. Именно из-за всего, написанного выше, линкёру и нужно передавать ВСЕ объектные модули, которые ранее компилировались по-отдельности, ведь линкёр должен располагать всеми адресами всех используемых функций.
4
74 / 37 / 3
Регистрация: 23.09.2012
Сообщений: 408
07.10.2012, 21:53  [ТС]
alsav22, хм, вы правы. Попробовал - почти все прояснилось. Единственное:

Я правильно понимаю, что если есть некий заголовочный файл, в нем прототипы функций, то компилятор полезет в точно так же названный cpp файл за этими функциями?
0
Эксперт С++
5058 / 3118 / 271
Регистрация: 11.11.2009
Сообщений: 7,044
07.10.2012, 22:03
Цитата Сообщение от Kgfq Посмотреть сообщение
что если есть некий заголовочный файл, в нем прототипы функций, то компилятор полезет в точно так же названный cpp файл за этими функциями?
Нет, так он делать не будет. Прочтите моё сообщение, там есть очень важный вывод (в явном виде, правда, не оформленный): во-первых, вызов компилятора заключается в неявном вызове кучи различных вспомогательных утилит (препроцессор, лексический анализатор, синтаксический анализатор, редактор связей и много чего ещё). Во-вторых, важно понимать, что все эти утилиты распределяют обязанности между собой. Компилятор не занимается линковкой, а линкёр - компиляцией. У каждого свои задачи, требующие своей входной информации.
0
74 / 37 / 3
Регистрация: 23.09.2012
Сообщений: 408
07.10.2012, 22:03  [ТС]
silent_1991, хм. Не совсем понимаю смысл создавать отдельный cpp, если потом в него включается .h. Ведь есть в хедере изменить хоть 1 несущественный символ, компилировать придется заново. Разве это не сводит всю прелесть на нет?

Так же, не понимаю, разве двойное включение <iostream> не сделает хуже?
ведь оба файла компилируются отдельно. И получается в обоих .o будет код из iostream
0
Эксперт С++
5058 / 3118 / 271
Регистрация: 11.11.2009
Сообщений: 7,044
07.10.2012, 22:06
Цитата Сообщение от Kgfq Посмотреть сообщение
Ведь есть в хедере изменить хоть 1 несущественный символ, компилировать придется заново
Теоретически да. Практически же реализацию редактировать придётся куда чаще, чем объявление. Хедер будет редактироваться только в связи с какими-либо значительными изменениями (введение нового пользовательского типа, новой функции, изменение тела класса и т.д.), который в любом случае повлекут за собой перекомпиляцию реализации, в которой также необходимо отслеживать эти изменения и поддерживать их в актуальном состоянии.
0
74 / 37 / 3
Регистрация: 23.09.2012
Сообщений: 408
07.10.2012, 22:09  [ТС]
silent_1991, а как линкер поймет, из какого файла брать реализацию?
0
Эксперт С++
5058 / 3118 / 271
Регистрация: 11.11.2009
Сообщений: 7,044
07.10.2012, 22:11
Kgfq, ну ведь на момент вызова у него есть вся необходимая информация, все файлы с реализацией ему предоставлены. Здесь в ход вступает такая вещь, как таблица символов, которая является, по сути, индексом всего содержимого объектного файла. Если линкёр находит в таблице символов символ, соответствующий разрешаемой им в данный момент заглушке, он берёт из этой таблицы смещение и подставляет его вместо заглушки.
Возможно, я несколько не точен в деталях реализации, но концепция такая.
0
5500 / 4895 / 831
Регистрация: 04.06.2011
Сообщений: 13,587
07.10.2012, 22:13
Цитата Сообщение от Kgfq Посмотреть сообщение
Ведь есть в хедере изменить хоть 1 несущественный символ, компилировать придется заново
Какая разница, в данном случае, что куда подключено? Или вы хотите вносить изменения в программу, и чтобы потом ничего не перекомпилировалось? И как вы себе это представляете?
0
74 / 37 / 3
Регистрация: 23.09.2012
Сообщений: 408
07.10.2012, 22:22  [ТС]
alsav22, как я себе представлял это - реализация должна абстрагироваться от остального кода.
Какая разница, какие классы есть в коде, когда этой функции нужно подать определенное значение и она должна его как-то изменить.
0
Эксперт С++
5058 / 3118 / 271
Регистрация: 11.11.2009
Сообщений: 7,044
07.10.2012, 22:32
Kgfq, так она и абстрагирована. Возьмите мой пример. В main.cpp абсолютно ничего неизвестно о реализации func. Всё это дело склеивается в единый монолит на самой последней стадии - стадии линковки.
0
5500 / 4895 / 831
Регистрация: 04.06.2011
Сообщений: 13,587
07.10.2012, 22:50
Цитата Сообщение от Kgfq Посмотреть сообщение
alsav22, как я себе представлял это - реализация должна абстрагироваться от остального кода.
И хотите .cpp с реализацией подключать к другим файлам. Где логика?
0
74 / 37 / 3
Регистрация: 23.09.2012
Сообщений: 408
07.10.2012, 23:05  [ТС]
silent_1991,
alsav22,

Спасибо за подробные объяснения, я разобрался. Единственное, еще вопрос, когда стоит начинать задумываться о разделении прототипа и реализации?
0
5500 / 4895 / 831
Регистрация: 04.06.2011
Сообщений: 13,587
07.10.2012, 23:14
Цитата Сообщение от Kgfq Посмотреть сообщение
когда стоит начинать задумываться о разделении прототипа и реализации?
К правильному, желательно, сразу привыкать. Неверное, есть смысл помещать прототипы отдельно, в заголовочном файле, когда много функций и они используются во многих файлах.

Добавлено через 4 минуты
Стоит сказать ещё о шаблонах. Прототип шаблона и реализация должны быть в одном файле.
1
Эксперт С++
5058 / 3118 / 271
Регистрация: 11.11.2009
Сообщений: 7,044
08.10.2012, 01:12
Kgfq, задумываться стоит сразу. Ещё почитайте о системах сборки, например, make. Именно с ними все прелести раздельной компиляции показываются наружу.
На счёт шаблонов: иными словами, шаблон - единственный случай, когда подключается не объявление, а полностью реализация. Такова особенность шаблонов.
0
 Аватар для warchief
36 / 36 / 0
Регистрация: 09.08.2012
Сообщений: 132
08.10.2012, 04:50
Цитата Сообщение от Kgfq Посмотреть сообщение
silent_1991, хм, за теорию спасибо, стало яснее. Не могли бы вы сделать проект с maim.cpp, func.h и func.cpp, что бы я наглядно увидел?
func.h
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#pragma once
 
// в header пишутся только объявления
 
extern int GLobalInt;
 
void Foo(int i);
 
class FooClass
{
public:
    FooClass();
 
    void Init(int i);
 
private:
    int m_i;
};
func.cpp
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include "func.h"
 
int GLobalInt = 0;
 
void Foo(int i)
{
   i += 10;
   printf("%d", i);
}
 
FooClass::FooClass()
{
   m_i = 0;
}
 
void FooClass::Init(int i)
{
   m_i = i;
   printf("%d", m_i);
}
main.cpp
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include "func.h"
#include <stdio.h>
#include <stdlib.h>
 
int main()
{
   GLobalInt = 10;
   printf("%d", GLobalInt);
 
   Foo(GLobalInt);
 
   FooClass fc;
   fc.Init(GLobalInt);
 
   system("PAUSE");
   return 0;
}
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
raxper
Эксперт
30234 / 6612 / 1498
Регистрация: 28.12.2010
Сообщений: 21,154
Блог
08.10.2012, 04:50

Как правильно перенести сайт с одного хостинга на другой?
На старом хостинге экспортирую БД. потом с помощью ftp скачиваю все файлы сайта. потом на новом хостинге импортирую БД. с фтп закачиваю все...

Как правильно передать метод делегата из одного класса в другой?
Добрый день! Я написал класс который рисует спираль Архимеда в pictureBox. За основу для для прорисовки был использован делегат для...

Как ПРАВИЛЬНО перенести windows 7 с одного компьютера на другой через образ?
Здравствуйте, форумчане! Можете подсказать мне что я делаю не так? Когда всякие авторы сборок образов windows в формате Acronis в...

Как подключить cpp-файл к h-файлу?
как связать файл Form1.h с главным файлом проекта 1.cpp ? что то вроде того не получается #include &quot;1.cpp&quot;

Как подключить элементы из cpp файла?
Доброе утро. У меня вопрос. Есть отдельно header(где прототип функции) и cpp файл, где содержится тело функции. При подключении к примеру в...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
35
Ответ Создать тему
Новые блоги и статьи
Запустил конкурс "тем и промптов для текстовых квестов созданных почти чисто ИИ"
Adler 06.10.2026
Всем привет! За последние три-четыре дня я создал более 16 текстовых квестовых игр используя преимущественно по одному запросу к ИИ на игру. Мне так понравилось смотреть все ветки/ сцены во всех. . .
ИИ не может найти нужный язык в списке
Supersumestria 05.10.2026
Я ему даю вот такое изображение и прошу найти и подчеркнуть немецкий язык. Возвращает он вот это: https:/ / i. **********/ vqBWLe2. png Нужную строчку в 3й колонке просто выдумал. . Это. . .
Новая последняя моя музыка в SUNO
zorxor 05.10.2026
Здравствуйте, дорогие мои друзья! С большой радостью я хотел бы представить вам свою новую последнею музыку, которую сгенерировала мне по моей просьбе нейросеть SUNO. С уважением, zorxor. Это. . .
Программный домашний кинотеатр
russiannick 27.09.2026
Сподобился на программный домашний кинотеатр. В качестве ЯВУ по традиции выбрал js. В помощники взял Яндекс-Алису. Было создано три зала на разные интересы. исторические и ретро сериал Хичкок. . .
Беседа с ИИ о программистах, недопускающих к созданию и правке кода генеративные ИИ и причины этого
zorxor 21.09.2026
Раньше я радовался или получал некоторые эмоции, пусть небольшие, но всё же, от самого процесса написания кода, рекомпиляции и запуска, видя постепенное развитие программы и прочее. А теперь лень. . .
Мобильное приложение ColorStep
pavlinmavlin 17.09.2026
Реализовал приложение Красный, Зеленый, Синий в Unity3d + c#. Название изменил на ColorStep. Приложение прошло модерацию и теперь доступно для скачивания. Делал его сам, шаг за шагом — и вот,. . .
Запрет дублирования строк в табличной части
Maks 13.09.2026
Реализация из решения ниже выполнена на нетиповом справочнике "Нормы ТО" с табличной часть "Виды ТО", разработанного в КА2, со следующими реквизитами: - ВидТО (СправочникСсылка. ВидыТО); - ВидГСМ. . .
Скрипты Tampermonkey для CyberForum, ChatGPT, Claude и пр.
Jin X 06.09.2026
Скрипты Tampermonkey для CyberForum, ChatGPT, Claude и пр. Работая с форумом и нейросетями в браузере часто хочется что-то подкорректировать или добавить какого-то функционала. Ниже прикреплён. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru