Почему я получаю синтаксическую ошибку во время компиляции? - PullRequest
1 голос
/ 03 ноября 2019

Может ли кто-нибудь объяснить, почему я получаю синтаксическую ошибку: идентификатор 'Bar' во время сборки без включения class Bar; в заголовочный файл foo.hpp?

Я не получаю никаких ошибок в Visual Studio2019 перед сборкой, и порядок сборки выглядит как bar, затем foo, затем main, поэтому после операторов #include может показаться, что файл заголовка bar сначала включен в foo заголовок во время сборки.

Я включил приведенный ниже код с изложением основной проблемы.

//Foo header file
#pragma once

#include "bar.hpp"  
#include <iostream>

class Bar; //Commenting this line out results in no longer being able to build the project

class Foo {

public:
    Foo();

    void pickSomething(Bar& bar);
};

//Foo cpp file
#include "foo.hpp"

Foo::Foo() {
    std::cout << "Made Foo" << std::endl;
}

void Foo::pickSomething(Bar& bar) {
    bar.getSomething();
    std::cout << "Picked something!" << std::endl;
}

//Bar header file
#pragma once

#include "foo.hpp"
#include <iostream>

class Foo;

class Bar {

public:
    Bar(Foo& foo);

    void getSomething();
};

//Bar cpp file
#include "bar.hpp"

Bar::Bar(Foo& foo) {
    std::cout << "Made bar" << std::endl;
}

void Bar::getSomething() {
    std::cout << "Gave something!" << std::endl;
}

//main file
#include "foo.hpp"
#include "bar.hpp"

int main() {
    Foo foo;
    Bar bar(foo);
    foo.pickSomething(bar);

    return 0;
}

Ответы [ 2 ]

1 голос
/ 03 ноября 2019

Резюме:

  • foo.hpp включает bar.hpp и bar.hpp включает foo.hpp. Это циклическая зависимость.
  • #pragma once следит за тем, чтобы не загружать тот же самый заголовок, если он уже загружен.

  • Только компиляцияиз bar.cpp не удается (foo.cpp и main.cpp будут успешно скомпилированы)

Давайте будем следовать препроцессору при компиляции bar.cpp. Все происходит в следующем порядке.

  1. bar.cpp включает bar.hpp
  2. bar.hpp включает foo.hpp. (Обратите внимание на следующие два пункта)
    • Препроцессор запоминает, что он ввел bar.hpp и не будет вводить его снова в текущем цикле.
    • Символ Bar (объявление класса Bar) еще не загружен, так как foo.hpp включен до этого
  3. foo.hpp попробуйте включить bar.hpp
    • Препроцессор знает, что он уже введен bar.hppследовательно, это игнорируется!
    • Однако в объявлении класса Foo используется символ с именем Bar. Но это не объявлено ранее!

Неизвестный символ Bar мог бы представлять многие вещи (класс, макрос и т. Д.). Таким образом, компилятор (по праву) терпит неудачу, поскольку он не знает, как с ним обращаться.

Решением является предварительная декларация. Там вы гарантируете компилятору, что символ Bar представляет класс. Пока вы ничего не делаете, когда компилятор должен знать о макете класса (например, определить функцию-член типа Bar, получить доступ к элементам Bar и т.д ...), компиляция будет успешной.

0 голосов
/ 03 ноября 2019

foo.hpp и bar.hpp не зависят друг от друга, поэтому предварительные объявления - хороший способ избежать циклической зависимости между их заголовочными файлами. Только файлы .cpp должны включать оба заголовочных файла.

...