ОБСУЖДЕНИЕ

Тонкости работы препроцессра С++

2 ответов 5,5 тыс.
AI-выжимка обсуждения скоро

Краткие тезисы обсуждения со ссылками на ключевые ответы появятся здесь.

Статус
Закрыто для дальнейших ответов.
Автор темы
Ситуация сложилас таким образом
По правилам хорошего стиля програмирования люди делают
три файла

file1.h - интерфейсная часть
file1.cpp - реализация
file.cpp - тестовая часть (в которой содержится main())

причем file1.cpp инклудит в себя file1.h
и file.cpp инклудит в себя тоже file1.h

Наскоко мне известно смысл директивы #include
это просто тупо вставить кусок кода в том месте где
встречается эта директива
Если все проинклудить явно (по всем правилам) три выше описаных файла
то возникает вопрос :
как в файле file.cpp видно file1.cpp (ведь он же нигде не инклудится)
значит что-то вызывается не явно! Без моего ведома...
Потомуш-то даная фишка прокатывает только для класов.

А сделай ты

file1.h - интерфейсная часть (содержит определения типов, констант, переменных...)
file1.cpp - реализация (содержит только функции которые используют все константы, переменные ... объявленные в file1.h)
file.cpp - тестовая часть (в которой содержится main() использует функции file1.cpp)
Ничо компилится не будет
Подскажите как это правильно сделать.

Насколько мне известно перед компиляцией выполняется препроцесор
который проходится по всему проекту и собирает все инклуды и дефайны и
дампит их всех в один временный файл с расширением *.I
вот этот то файл и компилится но он временный, и после компиляции удаляется
но ещо я точно знаю что можна заставить компилятор этот файл не удалять
Но я не знаю как это сделать чтобы этот файл остался
 
iLok
Ситуация сложилас таким образом
По правилам хорошего стиля програмирования люди делают
три файла

file1.h - интерфейсная часть
file1.cpp - реализация
file.cpp - тестовая часть (в которой содержится main())
Несколько странные правила, ну да ладно - кто как хочет, так и делает :(
как в файле file.cpp видно file1.cpp (ведь он же нигде не инклудится)
значит что-то вызывается не явно! Без моего ведома...
Потомуш-то даная фишка прокатывает только для класов.
Неверное высказывание.
Тут надо уточнить некоторые вещи...
1. Сборка программы на С++ идет в три больших этапа: препроцессор (обработка макросов и директив препроцессора), компиляция (получение объектных файлов для каждой из единиц трансляции (c/cpp/cxx файлы)), линковка (компоновка всех объектных файлов в один исполняемый модуль).
2. В языке можно делать forward-определения - декларации классов / методов / переменных, когда определяемый объект можно использовать не зная его реализации (реализация подключится при линковке).
Вот пример:
==== test.h ====
struct MyStruct
{
int i;
void About() const;
};
==== test.h ====

==== test.cpp ====
#include "test.h"
#include <iostream>
void MyStruct::About() const
{
std::cout << "MyStruct::About()" << std::endl;
}
==== test.cpp ====

==== main.cpp ====
#include "test.h"
void main()
{
MyStruct myS;
myS.i = 5;
myS.About();
}
==== main.cpp ====
В файле test.h находится определение структуры MyStruct. Из файла main.cpp эта структура видна и может быть использована (на основе ее декларации по сигнатурам типов/классов/методов). Файл test.cpp содержит реализацию метода void MyStruct::About() const. На этапе компиляции всем вызовам и реализациям проставляются сигнатуры (сигнатура вызываемой функции / сигнатура реализуемой функции), а потом при линковке уже проставляется соответствие вызовов реализациям.

Если бы мы забыли сделать реализацию, то линковщик (именно он, а не компилятор!) выдал бы ошибку типа unresolved external, а если бы мы сделали несколько реализаций одной и той же функции в разных файлах, то получили бы от линковщика ambigous resolve. Вот... Так что препроцессор тут ни при чем :)

Доходчиво объяснил?
 
Поправка к терминам. Есть определение (definition), объявление (declaration) и объявление неполного типа (forward declaration).
Код:
// объявление переменной (не члена класса)
extern int i;
extern std::string s;
// определение без инициализации, точнее - с дефолтным конструктором
int i;
std::string s;
// определение с инициализацией
int i = 1;
std::string s = "hello";
std::string s ("hello");

/////////////////////////////////////////////////////////

// объявление функции (не члена класса)
/* extern необязательно, особенно для локальных (static) функций */
void foo(int);

// определение
void foo(int bar) { ..... }

/////////////////////////////////////////////////////////

// форвард класса/структуры/союза
class Test;

// и его использование там, где это разрешено
Test* ptr; // в объявлениях указателей, например.

// объявление класса
class Test
{
public:
// в классе можно делать объявления данных и типов, как обычно,
// а также объявления+определения методов

// объявление статического члена-данного (в том числе - константы любого типа)
int g_count;

// объявление целочисленной константы вместе с ее определением - через перечисление
enum { THE_CONSTANT = 100 };

// объявление метода (статического или метода экземпляра)
void hello();

// объявление + инлайн-определение
void goodbye() { ..... }
};

// можно использовать экземпляры объявленного класса
void test()
{
int n = Test::THE_CONSTANT;
Test var;
Test* ptr = new Test();
var.hello();
}

// определение членов класса
int Test::g_count = 123; // с инициализацией или без нее

void Test::hello() { ... };
С уважением, Кодт @ RSDN.ru
 
Статус
Закрыто для дальнейших ответов.

Ещё по теме