Прямое объявление между файлами

Все:

У меня есть два файла:

main.cpp

#include <iostream>

using namespace std;

class A;

int main(){
    A a;
    a.disp();

    return 0;
}

и A.cpp

#include <iostream>
using namespace std;

class A{

    public:
    A(){}
    void disp(){ cout<<"this is A disp()"<<endl;}
};

Интересно, почему, когда я компилирую эти два файла, он сказал мне, что:

main.cpp: В функции ‘int main ()’: main.cpp: 8: 4: error: aggregate ‘A a’ имеет неполный тип и не может быть определен

Я думаю, это потому, что я не понимаю, как использовать форвардное объявление, так может ли кто-нибудь сказать мне, как это сделать?

Кстати, я знаю способ сделать это с помощью файла заголовка, но я просто хочу разобраться в этом способе прямого объявления.

Лучший,


person Kuan    schedule 17.04.2013    source источник
comment
Здесь вы не можете использовать предварительную декларацию.   -  person juanchopanza    schedule 17.04.2013
comment
@juanchopanza, можешь подробнее рассказать?   -  person Kuan    schedule 17.04.2013
comment
Вы мало что можете сделать, если не написать то, что вы бы написали в заголовке исходного файла. Компилятор просто недостаточно знает о классе, чтобы делать с ним что-либо, кроме выделения на него указателя.   -  person Will    schedule 17.04.2013
comment
@ Спасибо за ответ.!   -  person Kuan    schedule 17.04.2013


Ответы (3)


Поскольку вы объявляете его только как класс (т.е. вы не сообщаете компилятору, что содержит класс), он не может создать для него память (он не знает, насколько большим он должен быть).

Что вы можете сделать, так это определить указатели на класс A:

int main(){
    A *a;
    // a->disp() is still invalid

    return 0;
}

но ты ничего не сможешь с этим поделать.

Именно для этого и нужны заголовки!

A.h:

class A{
public:
    A(){}
    void disp();
};

A.cpp:

#include "A.h"
void A::disp(){
    cout<<"this is A disp()"<<endl;
}

После включения a.h в файл вы сможете создавать и использовать его так, как вы ожидаете.

person Dave    schedule 17.04.2013
comment
В общем, это означает, что я могу использовать только указатель или ссылку для внешнего прямого объявленного класса или типа, верно? - person Kuan; 17.04.2013
comment
Указатели да, ссылки, в которых я не уверен, но я думаю, что вы не можете. Только представьте, что синтаксис class A; говорит, когда я говорю A, я говорю о классе, но я не знаю большего, и подумайте, что компилятор мог бы разумно определить на основе этой информации. - person Dave; 17.04.2013
comment
@Kuan и Dave, правила для указателей и ссылок в основном идентичны и по тем же причинам. - person Mark Ransom; 17.04.2013
comment
@Kuan, он также работает со ссылками, если вы не вызываете через них какие-либо методы. - person juanchopanza; 17.04.2013
comment
@All Может ли кто-нибудь дать мне пример без файла заголовка? - person Kuan; 17.04.2013
comment
@Kuan Вы не можете сделать так, чтобы определение класса было невидимым, и вы не можете делать с классом больше, чем выделять указатель или ссылку. Конец истории. - person Will; 17.04.2013
comment
@Will Спасибо за ответ. Понятно! - person Kuan; 17.04.2013

main необходимо знать размер класса A и то, что у него есть функция-член disp(). По этой причине необходимо иметь доступ к объявлению класса. Поместите его в файл A.hвключить охранников и без использования пространства имен std ) и включите это в main.cpp.

файл A.h

#ifndef A_H_
#define A_H_

#include <iostream>

class A
{
    public:
    A(){}
    void disp() const { 
      std::cout<<"this is A disp()"<<std::endl;
    }
};

#endif

main.cpp:

#include "A.h"

int main() { .... }

Обратите внимание, что вы можете решить поместить реализацию void A::disp() в файл A.cpp, а не в заголовок.

person juanchopanza    schedule 17.04.2013
comment
Спасибо, мне очень жаль, что я забыл указать, я просто хочу понять, как это сделать. - person Kuan; 17.04.2013

Когда вы используете только указатель на класс в другом объявлении класса, требуется только предварительное объявление. Это означает, что в этот момент известно только имя класса, но не определение его членов или его размер. Тип неполный.

Когда вы действительно хотите использовать переменную-член или функцию-член, или когда класс создается с помощью автоматической переменной или с помощью new (), или тип используется для объявления члена в другом классе. Обычное объявление класса должно быть сделано до этого момента, и определение класса должно быть известно в этот момент, поэтому должно быть включение определения класса в ту же единицу трансляции.

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

person Emile Vrijdags    schedule 17.04.2013
comment
Но мне интересно, почему это очень хорошо работает, если я напишу A в main.cpp? - person Kuan; 17.04.2013
comment
@Kuan Делая это, вы фактически создаете файл, который выглядит для компилятора так же, как и файл, использующий #include после запуска препроцессора. - person Will; 17.04.2013
comment
если вы имеете в виду под записью A, дайте определение позже в том же файле, что и main. Тогда определение находится в той же единице перевода, и компилятор может найти определение в этой точке. Когда определение находится не в том же файле или не включено где-либо, компилятор может найти только имя и будет знать только неполный тип, указанный в предварительном объявлении - person Emile Vrijdags; 17.04.2013
comment
@Emile Vrijdags #include ‹iostream› #include ‹list› класс A; int main () {A :: AiLst lst; возврат 0; } класс А {общественность: А (); int i; typedef std :: list ‹int› AiLst; }; A :: A (): i (0) {} Но мне интересно, почему это все еще не работает? - person Kuan; 17.04.2013
comment
Моя вина .. Я не совсем прав. Объявление должно быть перед использованием класса таким образом, а не только предварительное объявление. Определение функций-членов может происходить после использования класса, но в той же единице перевода. Пример: pastebin.com/GDjWEAqB - person Emile Vrijdags; 17.04.2013
comment
Я отредактировал свой пост, чтобы сделать его более полным. Объяснение, вероятно, все еще будет неполным, но, надеюсь, правильным. - person Emile Vrijdags; 17.04.2013