ошибка: класс не был объявлен, несмотря на включение заголовка, и код, компилируемый нормально в другом месте - PullRequest
37 голосов
/ 02 июля 2011

Итак, у меня есть класс, включенный в другой класс, который продолжает выдавать ошибку компиляции в форме "error: 'ProblemClass" не был объявлен. Файлы настроены таким образом:

#ifndef PROBLEMCLASS_H
#define PROBLEMCLASS_H

#include <iostream>
#include <cmath>

class ProblemClass
{
  public:

    virtual void Init() = 0;
};

#endif

и класс, где происходит ошибка, выглядит следующим образом:

#ifndef ACLASS_H
#define ACLASS_H

#include "problemclass.h"

class AClass : public Base
{
  public:

    void DoSomething(ProblemClass* problem);

};

#endif

Ошибка компиляции происходит в void Dosomething ();

Я знаю, что кода здесь недостаточно для решения проблемы. Я не смог создать минимальный пример, который мог бы воспроизвести его. Так что мой вопрос гораздо более общий; какие вещи могут вызвать это? Есть ли что-то конкретное, что я должен искать, или какая-то линия расследования, которой я должен следовать, чтобы отследить это?

Этот код прекрасно компилируется в почти идентичной версии проекта.

Помощь любого рода будет принята с благодарностью, независимо от того, насколько она расплывчата. Я использую кодовые блоки 10.05 с mingw4.4.1 в win 7 64 бит.

Ответы [ 7 ]

87 голосов
/ 02 июля 2011

Вы, похоже, говорите, что код, который вы показываете, на самом деле не вызывает ошибку компилятора, с которой у вас возникла проблема.Так что мы можем только догадываться.Вот некоторые возможности:

Возможно, вы забыли включить problemclass.h из файла, где вы используете ProblemClass. Возможно, вы ошиблись в названии ProblemClass либо в его собственном заголовочном файле, либо в том месте, где вы его используете.Это может быть трудно обнаружить, если это ошибка с заглавной буквы, такая как написание Problemclass или problemClass вместо ProblemClass. Вы могли бы скопировать вставленный вами сторожевой таймер #defines из одного заголовочного файла в другой, а затем забыть изменить определенные имена.Тогда вступит в силу только первый из этих двух включенных заголовочных файлов. Вы могли бы поместить ProblemClass в пространство имен A, и в этом случае вы должны ссылаться на ProblemClass как A :: ProblemClass, если вы ссылаетесь на него снаружи пространства имен A. Возможно, вы используете шаблоны и не ожидаете двух-фазный поиск для работы . Возможно, вы ошиблись именем файла в вашем включении.Компилятор не сообщит об ошибке, если у вас также есть старая версия этого файла с ошибочным именем. Вы могли бы сделать ProblemClass макросом, который будет определен только после того, как вы включите problemclass.h, и в этом случае то, что вы видите как ProblemClass, будет заменено каким-либо другим препроцессором макроса. Вы могли бы определить ProblemClass в файле заголовка, отличном от problemclass.h, а затем problemclass.h фактически определяет что-то еще.
28 голосов
/ 19 мая 2013

У меня было то же сообщение об ошибке в результате круговой зависимости в моих заголовочных файлах / классах:

foo.hpp:

#ifndef FOO_HPP
#define FOO_HPP

#include <stdio.h>
#include "bar.hpp" // <-- here

class Foo {
public:
    int value = 0;

    void do_foo(Bar myBar) {
        printf("foo + %d\n", myBar.value);
    }
};

#endif //FOO_HPP

bar.hpp:

#ifndef BAR_HPP
#define BAR_HPP

#include <stdio.h>
#include "foo.hpp" // <-- and here

class Bar {
public: 
    int value = 1;      

    void do_bar(Foo myFoo) {
        printf("bar = %d \n", myFoo.value);
    }
};

#endif //BAR_HPP

Компиляция с: g++ -std=c++11 foo.hpp -o foo привела к следующему выводу:

In file included from foo.hpp:5:0:
bar.hpp:11:15: error: ‘Foo’ has not been declared
bar.hpp: In member function ‘void Bar::do_bar(int)’:
bar.hpp:12:32: error: request for member ‘value’ in ‘myFoo’, which is of non-class type ‘int’
2 голосов
/ 02 июля 2011

Пожалуйста, опубликуйте команду, которую вы используете для компиляции. Я видел эту проблему, если у вас есть 2 отдельных файла с одинаковым заголовком и вы делаете gcc * .cpp. Это происходит потому, что #define определяется для всего экземпляра gcc, а не только для каждого отдельного объектного файла, который компилируется.

Ex.

File1

#ifndef FILE1_HPP
#define FILE1_HPP 1
....
#endif

Затем два отдельных файла, которые ссылаются на него.

#include <file1.hpp>

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

gcc -Wall *.cpp

Ответ: либо удалите #ifndef, либо скомпилируйте каждый файл в свои собственные объектные файлы, а затем свяжите их с основным приложением.

1 голос
/ 22 июня 2017

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

EG:

Заголовок

class SomeClass
{
public:
    void SomeFunc();
private:
    typedef int SomeType_t;
};

Источник (выдает ошибку SomeType_t не определен )

void SomeFunc()
{
    SomeType_t dummy = 0;
}

Источник (исправлено)

void SomeClass::SomeFunc()
{
    SomeType_t dummy = 0;
}

Это глупая, но действительно легкая ошибка, которую можно совершить, и ее нельзя увидеть до тех пор, пока вы не нанесете себе три сотрясения от удара головой о стол.

1 голос
/ 16 ноября 2016

У меня возникла похожая проблема, и мне потребовалось некоторое время, чтобы выяснить, почему.

В вашем случае вы можете определить PROBLEMCLASS_H в некоторых других заголовочных файлах. В результате ваш файл cpp пропустит определение в заголовочном файле. Другими словами, строка #include "problemclass.h" пропускается.

В моем случае я использую MingW64 под Linux. Скажем, у меня есть заголовочный файл IO.h:

// IO.h
#ifndef _IO_H_
#define _IO_H_

class A{
...
};
#endif

В моем файле main.cpp:

// main.cpp
#include <unistd.h>
#include "IO.h"
int main(int argc, char** argv) {
 //...
}

Файл cpp выглядит невинно. Однако, когда unistd.h включен, он тайно включает /usr/i686-w64-mingw32.static/include/io.h, предоставленный MingW, и это io.h выглядит следующим образом:

// io.h
#ifndef _IO_H_
#define _IO_H_
...
#endif /* End _IO_H_ */

Теперь вы можете видеть, что включение unistd.h приведет к включению io.h от MingW, и это скроет мой собственный IO.h. Я думаю, что это такая же проблема, как у вас.

Если вы переключаете порядок включений (ставьте #include <unistd.h> после IO.h), программа компилируется. Но это не очень хорошее предложение. Я рекомендую вам не использовать _IO_H_ для защиты своего собственного IO.h.

Чтобы понять, как / почему ваш PROBLEMCLASS_H включен, я согласен с @greatwolf, вы можете использовать g++ -E, чтобы вывести выходные данные препроцессора и проверить его вручную. Проверьте, какие файлы включены до вашего PROBLEMCLASS_H и в каком порядке они включены. Я надеюсь, что это может помочь решить вашу проблему.

1 голос
/ 02 июля 2011

Единственное, о чем я мог подумать, это вызвало бы ошибку компиляции, основанную на том, что вы представили, если PROBLEMCLASS_H каким-то образом переопределено вне файла заголовка. Как например:

//main.cpp
#define PROBLEMCLASS_H
#include "aclass.h"

int main() {}

Одна из идей, которую вы можете попробовать, это не включать 'problemclass.h' в 'aclass.h', а просто сделать предварительное объявление ProblemClass. Чтобы это работало, вы должны убедиться, что определение AClass содержит только ссылки или указатели на ProblemClass - вы не хотите, чтобы компилятор пытался взять размер ProblemClass, который потребовал бы его полного определения.

//aclass.h
#ifndef ACLASS_H
#define ACLASS_H

class ProblemClass;

class AClass : public Base
{
  public:
    void DoSomething(ProblemClass* problem);
};

#endif

Другой способ, который вы можете использовать для отслеживания этой проблемы заголовка, - это просто предварительная обработка проблемного модуля компиляции .cpp. Откройте предварительно обработанный выходной файл (обычно расширение «.i») и проверьте, что на самом деле происходит. Это удобно, особенно если «включенных» много и их трудно предсказать.

0 голосов
/ 23 июля 2018

У меня была та же проблема, и я обнаружил, что я делаю неправильно: следуя вашему примеру, я включил ProblemClass в AClass, таким образом вызывая проблему.

...