如果我像这样创建一个类:
1 2 3 4 5 6 7 8 9 10 11 12
| // B.h
#ifndef _B_H_
#define _B_H_
class B
{
private:
int x;
int y;
};
#endif // _B_H_ |
并像这样使用它:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| // main.cpp
#include <iostream>
#include <vector>
class B; // Forward declaration.
class A
{
public:
A() {
std::cout << v.size() << std::endl;
}
private:
std::vector v;
};
int main()
{
A a;
} |
编译main.cpp时,编译器失败。 现在,我知道的解决方案是#include"B.h",但是我很好奇它为什么失败。 g++或cl的错误消息都没有启发性。
编译器在生成适当的布局信息之前需要知道" B"有多大。相反,如果您说std::vector,则编译器将不需要知道B的大小,因为它知道指针的大小。
实际上,如果在知道B类型的编译单元中实现A的构造函数,则您的示例将构建。
一个std :: vector实例,无论T是什么,都具有固定的大小,因为正如其他人之前所说,它仅包含一个指向T的指针。但是vector的构造函数取决于具体类型。您的示例无法编译,因为A()试图调用向量的ctor,在不知道B的情况下无法生成该向量。这是可行的:
A的声明:
1 2 3 4 5 6 7 8 9 10 11 12 13
| // A.h
#include <vector>
class B; // Forward declaration.
class A
{
public:
A(); // only declare, don't implement here
private:
std::vector v;
}; |
A的实现:
1 2 3 4 5 6 7 8
| // A.cpp
#include"A.h"
#include"B.h"
A::A() // this implicitly calls vector's constructor
{
std::cout << v.size() << std::endl;
} |
现在,A的用户只需要知道A,而不必知道B:
1 2 3 4 5 6 7
| // 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 ++!到了1970年代...;)