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

  • Автор темы Guest
  • Дата начала
Статус
Закрыто для дальнейших ответов.
G

Guest

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

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
вот этот то файл и компилится но он временный, и после компиляции удаляется
но ещо я точно знаю что можна заставить компилятор этот файл не удалять
Но я не знаю как это сделать чтобы этот файл остался
 
G

Guest

#2
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. Вот... Так что препроцессор тут ни при чем :)

Доходчиво объяснил?
 
G

Guest

#3
Поправка к терминам. Есть определение (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
 
Статус
Закрыто для дальнейших ответов.