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

Тема в разделе "Общие вопросы по С и С++", создана пользователем Guest, 12 дек 2003.

Статус темы:
Закрыта.
  1. Guest

    Guest Гость

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

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

    iLok
    Несколько странные правила, ну да ладно - кто как хочет, так и делает :(
    Неверное высказывание.
    Тут надо уточнить некоторые вещи...
    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. Вот... Так что препроцессор тут ни при чем :)

    Доходчиво объяснил?
     
  3. Гость

    Поправка к терминам. Есть определение (definition), объявление (declaration) и объявление неполного типа (forward declaration).
    Код (Text):
    // объявление переменной (не члена класса)
    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
     
Загрузка...
Статус темы:
Закрыта.

Поделиться этой страницей