题
如果我创建一个像这样的类:
// B.h
#ifndef _B_H_
#define _B_H_
class B
{
private:
int x;
int y;
};
#endif // _B_H_
并像这样使用它:
// main.cpp
#include <iostream>
#include <vector>
class B; // Forward declaration.
class A
{
public:
A() {
std::cout << v.size() << std::endl;
}
private:
std::vector<B> v;
};
int main()
{
A a;
}
编译时编译失败 main.cpp
. 。现在我知道的解决方案是 #include "B.h"
, ,但我很好奇为什么会失败。两者都不 g++
或者 cl
的错误消息在这件事上非常有启发性。
解决方案
编译器需要知道“B”有多大,然后才能生成适当的布局信息。如果相反,你说 std::vector<B*>
, ,那么编译器就不需要知道 B 有多大,因为它知道指针有多大。
其他提示
事实上,如果 A 的构造函数是在知道 B 类型的编译单元中实现的,那么您的示例就会构建。
无论 T 是什么, std::vector 实例都有固定的大小,因为正如其他人之前所说,它只包含指向 T 的指针。但向量的构造函数取决于具体类型。您的示例无法编译,因为 A() 尝试调用向量的 ctor,而在不知道 B 的情况下无法生成该向量。这是可行的:
A的声明:
// A.h
#include <vector>
class B; // Forward declaration.
class A
{
public:
A(); // only declare, don't implement here
private:
std::vector<B> v;
};
A的实现:
// A.cpp
#include "A.h"
#include "B.h"
A::A() // this implicitly calls vector<B>'s constructor
{
std::cout << v.size() << std::endl;
}
现在 A 的用户只需要知道 A,而不是 B:
// main.cpp
#include "A.h"
int main()
{
A a; // compiles OK
}
为了实例化 A::v,编译器需要知道 B 的具体类型。
如果您试图最大程度地减少 #included 包袱的数量以缩短编译时间,您可以做两件事,它们实际上是彼此的变体:
- 使用指向 B 的指针
- 使用轻量级 代理人 到B
所需的不仅仅是 B 的大小。例如,现代编译器将有一些奇特的技巧来在可能的情况下使用 memcpy 来加速向量复制。这通常是通过部分专门化元素类型的 POD 性来实现的。您无法从前向声明中判断 B 是否是 POD。
就像 fyzix 所说,您的前向声明不起作用的原因是您的内联构造函数。即使是空的构造函数也可能包含大量代码,例如非 POD 成员的构造。在您的情况下,您有一个要初始化的向量,如果不完全定义其模板类型就无法做到这一点。
析构函数也是如此。向量需要模板类型定义来告诉销毁它所拥有的实例时调用哪个析构函数。
要解决这个问题,只需不要内联构造函数和析构函数即可。在 B 完全定义之后,在某个地方单独定义它们。
了解更多信息,http://www.chromium.org/developers/coding-style/cpp-dos-and-donts
无论您使用向量还是只是尝试实例化一个 B,这都无关紧要。实例化需要对象的完整定义。
伙计,你正在实例化 std::vector
类型不完整。不要触及前向声明,只需将构造函数的定义移至 .cpp
文件。
不能使用前向声明的原因是 B 的大小未知。
在你的例子中没有理由不能将 B.h 包含在 A.h 中,那么你真正想要解决的问题是什么?
编辑: 还有另一种方法可以解决这个问题:别再用C/C++了!这真是 70 年代的事啊……;)