文件之间的前置声明

4
大家好:
我有两个文件:
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: 错误:聚合‘A a’的类型不完全且无法定义
我认为这是因为我不了解如何使用前向声明,所以有没有人可以告诉我应该如何做呢?
顺便说一下,我知道头文件的方式可以实现这个,但我想弄清楚前向声明的方法。
最好的祝福,

3
这里不能使用前置声明。 - juanchopanza
@juanchopanza,你能给我提供更多的细节吗? - Kuan
除了在源文件的头部编写你要编写的内容外,你几乎无法做任何事情。编译器对于这个类的了解不够,只能分配一个指向它的指针,而不能进行其他操作。 - Will
3个回答

6

因为你仅仅将它声明为一个类(即你没有告诉编译器这个类包含什么),所以它无法为它创建内存(它不知道需要多大的内存)。

你可以定义一个指向 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后,您可以像预期的那样创建并使用它。


那么一般来说,这意味着我只能使用指针或引用来引用一个外部的前向声明类或类型,对吗? - Kuan
指针是可以的,至于引用,我不太确定,但我认为你不能。想象一下class A;语法,就好像在说“当我说A时,我是在谈论一个类,但我不知道更多的信息”,然后考虑编译器能够从这些信息中合理地确定什么。 - Dave
@Kuan 和 Dave,指针和引用的规则大多数情况下是相同的,原因也是一样的。 - Mark Ransom
@Kuan 只要你不通过引用调用任何方法,它也适用于引用。 - juanchopanza
@所有人,有没有人可以给我一个不需要头文件的例子? - Kuan
@Kuan,如果类定义是不可见的,那么除了分配指针或引用之外,您不能对该类进行任何更多的操作。故事结束。 - Will

3

非常感谢,非常抱歉我忘了指出,我只是想弄清楚如何以这种方式做。 - Kuan

1
当您仅在另一个类声明中使用类的指针时,只需要前向声明。这意味着此时只知道类的名称,而不知道其成员定义或大小。类型是不完整的。
当您实际想要使用成员变量或成员函数,或者当类被自动变量或使用new()实例化时,或者类型用于在另一个类中声明成员时,必须在此之前进行普通的类声明,并且在此时必须知道类定义,因此必须在同一翻译单元中包含类定义。
其中有一个有用的例子:前向声明通常用于规避循环类依赖/包含。其中一个类具有第二个类的成员,该类也具有第一个类的成员。当一个包含另一个,而另一个包含一个时,会出现问题。当您仅使用前向声明并将实际包含推迟到稍后的时间点时,就不再存在循环包含了。

但是我想知道,如果我在main.cpp中写A,为什么它能够很好地工作? - Kuan
@Kuan 通过这样做,您实际上正在创建一个文件,该文件在预处理器运行后与使用#include的文件看起来相同。 - Will
如果你的意思是“写A”,那么在同一个文件中稍后给出定义。这样,定义就在同一翻译单元中,编译器可以在那个点找到定义。当定义不在同一个文件或任何地方都没有包含时,编译器只能找到名称,并且只知道由前向声明给出的不完整类型。 - Emile Vrijdags
@Emile Vrijdags #include <iostream> #include <list>class A;int main(){ A::AiLst lst; return 0; }class A{ public: A(); int i; typedef std::list<int> AiLst; };A::A():i(0){}但是我不知道为什么这还是没有用? - Kuan
我的错..我不是完全正确的。声明必须在这种方式中使用类之前,而不仅仅是前向声明。成员函数的定义可以在使用类之后进行,但在同一翻译单元中。例如:http://pastebin.com/GDjWEAqB - Emile Vrijdags
我编辑了我的帖子,使其更完整。解释可能仍然不完整,但希望已经正确地解释了。 - Emile Vrijdags

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接