Почему я получаю неполный тип при использовании предварительного объявления вместо #include?

Здесь у меня есть state_machine.h:

#ifndef STATE_MACHINE_H
#define STATE_MACHINE_H

// state machine classes

//#include "state_t.h"
class state_t;

class state_machine
{

public:
    state_machine();
    void change_state(state_t *newState);
    void process_state(int data);

private:
    state_t *_state;
};

#endif // STATE_MACHINE_H

А вот и state_t.h:

#ifndef STATE_T_H
#define STATE_T_H

#include <QByteArray>
#include <QDebug>

//#include "state_machine.h"
class state_machine;

class state_t
{
public:
    state_t(QByteArray stateName);
    virtual ~state_t();
    virtual void processState(state_machine *sm, int input) = 0;

    void defaultUnknownEventHandler(int event);

    QByteArray name;
};

#endif // STATE_T_H

Затем некоторые классы состояния, которые более или менее одинаковы, я просто перечислю один:

teststate1.h:

#ifndef TESTSTATE1_H
#define TESTSTATE1_H

#include "state_t.h"

class testState1 : public state_t
{
public:
    testState1();
    void processState(state_machine *sm, int event);
};

#endif // TESTSTATE1_H

teststate.cpp:

#include "teststate1.h"
#include "teststate2.h"

testState1::testState1() :
    state_t("state1")
{
}

void testState1::processState(state_machine *sm, int event)
{
    qDebug() << name << ": event" << event;
    switch (event)
    {
        case 2:
        {
            // error: invalid use of incomplete type 'class state_machine'
            sm->change_state(new testState2()); 
            break;
        }
        default:
        {
            defaultUnknownEventHandler(event);
            break;
        }
    }
}

Проблема:

Я пытаюсь привести в порядок свой код и использовать минимальное количество включений заголовков (особенно в файлах заголовков), используя предварительные объявления. Вы можете видеть в заголовке класса state_machine, что я закомментировал #include "state_t.h" и заменил его предварительным объявлением class state_t;. Это сработало, и мой код скомпилировался и запустился.

Затем в state.h я заменил #include "state_machine.h" предварительным объявлением class state_machine; (вы можете видеть, где я его закомментировал).

Но теперь я получаю сообщение об ошибке error: invalid use of incomplete type 'class state_machine', которое я прокомментировал в коде testState1.cpp. Но я не уверен, почему. Почему state_machine неполный тип?


person code_fodder    schedule 31.10.2013    source источник
comment
Поместите включение туда, где оно необходимо: в teststate.cpp.   -  person juanchopanza    schedule 31.10.2013
comment
Для этого вопроса должно быть множество дубликатов, я просто не могу найти правильный.   -  person rubenvb    schedule 31.10.2013
comment
@juanchopanza Спасибо, пожалуйста, посмотрите мой комментарий Майку ниже ... Я также не уверен, зачем это нужно : (   -  person code_fodder    schedule 31.10.2013
comment
@rubenvb да, я просмотрел некоторые из них, но я не могу полностью сопоставить их со своим кодом. В результате того, что я читал, я пытаюсь использовать форвардные деки...   -  person code_fodder    schedule 31.10.2013


Ответы (1)


teststate.cpp нуждается в определении state_machine; поэтому включите туда state_machine.h.

«Неполный» означает, что компилятор видел только объявление class state_machine;, а не полное определение. С неполным типом можно выполнять разные действия, например объявлять указатели или ссылки. Но вы не можете вызывать функции-члены (как вы делаете здесь) или вообще делать что-либо, что требует знания членов класса, без полного определения.

person Mike Seymour    schedule 31.10.2013
comment
Я не совсем уверен, что понимаю, почему это так... он включает teststate1.h, который включает state_t.h, который имеет предварительное объявление state_machine, так почему это не сработало? и почему это неполный тип? ... это меня смущает! - person code_fodder; 31.10.2013
comment
@code_fodder: он неполный, потому что он был объявлен, но не определен. Вам нужно полное определение (из заголовка) для вызова функции-члена. - person Mike Seymour; 31.10.2013
comment
ааа, даже когда это указатель? Это фактический вызов члена, который требует его неявного определения? Хорошо, я думаю, я понимаю. Спасибо! - person code_fodder; 31.10.2013
comment
@code_fodder: вызов члена требует знания членов, которые доступны только из полного определения. - person Mike Seymour; 31.10.2013
comment
большое спасибо!, я просто не полностью понял эту часть :) - person code_fodder; 31.10.2013