33

我有一个 B 类,我想调用 A 类的成员。所以:

1.

//A.h    
class B; 
class A 
{ 
private:
    B* m_p; 
}; 

//a.cpp
#include "B.h"

2.

// A.h
#include "B.h"

class A 
{ 
private: 
    B * impl_; 
}; 

当一个没有太多依赖的小项目涉及时,哪种方式更好,这两者是否相似?

4

6 回答 6

38

你这样做的第一种方式意味着在 中a.h存在class B已知的,但不是它的定义。这限制了您可以对Binside执行的操作a.h。例如,你可以有 type 的变量B *,但不能有 typeB的变量(因为对于 type 变量的声明,B编译器必须能够看到 的完整定义B)。此外,如果您有 type 的变量,B *则不能取消引用指针(因为也B必须知道 的定义)。

因此,您的第二选择——没有这些问题——是首选,这是大多数人大部分时间使用的。

只有在第一种方法可能有用的特殊情况下。例如:

  • 如果.h文件相互包含(但是您可能会遇到一些进一步的问题,也涉及包含保护;这通常很困难并且应该避免);
  • Ifb.h非常大且复杂,因此您希望尽可能避免包含它,因为它会减慢编译过程。
于 2013-04-05T07:24:02.813 回答
20

您的第一种方法是前向声明。您的第二个实际上包括 B 类。

什么时候使用一个而不是另一个?

在以下情况下使用第一个:

  • 在 A 的定义中,你只有一个指向 B 的指针,即不是 B 的成员。
  • 您永远不会从 A 的定义中调用 B 的任何函数。(即对 B 的成员函数的所有调用都发生在您实际实现 A 的成员函数的 .cpp 文件中。)
  • 您希望类 B 的接口或大小经常更改,但不会更改 A 的接口。这样,如果 B 更改,则仅重新编译 a.cpp 的内容,但 ah(以及包含 ah 的其他文件)不需要更改.

在以下情况下使用第二个:

  • 您需要知道 B 的大小。编译器使用类定义及其所有成员的大小来计算类的大小。例如,如果类 A 有一个类型为 B 的成员,那么要计算 A 的大小,编译器需要知道 B 的大小;要知道 B 的大小,你需要包括 bh
    • 你需要调用B类的函数。为了知道你调用的是不是实际存在的函数,编译器需要知道B类的接口,即你需要包含bh
于 2013-04-05T07:22:34.200 回答
13

答:1
看看http://www.umich.edu/~eecs381/handouts/handouts.html

C 头文件指南

C++ 头文件指南(由密歇根大学 EECS 系的 David Kieras 撰写)说:

准则 #10。如果类型 X 的声明不完整,请使用它而不是 #include 其标头 Xh。如果另一个结构或类类型 X 仅作为指针或引用类型出现在头文件的内容中,那么您不应该#include Xh,而只需将不完整的 X 声明(也称为“前向”声明)放在头文件的开头,如:class X;请参阅讲义 不完整的声明以获取有关此强大且有价值的技术的更多讨论。请注意,标准库包含一个不完整声明的标头,该标头通常足以满足该<iostream> 库的需要,名为<iosfwd>. #include<iosfwd>尽可能,因为<iostream>头文件非常大(巨大的模板!)。

于 2013-04-05T07:20:33.703 回答
0

只需在 A 类的标题中声明该类即可。

class B;
于 2013-04-05T07:13:05.980 回答
0

第二个更好。它使B该类成为您使用该.h文件包含的模块。考虑您B将来子类化并更新A为使用C. 在第二种情况下,您只需更换标题#includeA化妆。在第一种情况下,您必须更改前向声明。此外,在第二种情况下,您定义的不仅仅是符号B

#include "B.h"和注释一样,如果头文件与其余代码位于同一目录中,则应使用。

于 2013-04-05T07:13:40.760 回答
0

好吧,您正在做的事情称为前向减速,您想要这样做的原因是,如果您有类似使用 B 类的 A 类和使用 A 类的 B 类的东西。

在只有一个关系的情况下,您当然可以使用第二个选择。如果您需要双重使用,那么您的至少一个类 decleration 必须使用前向 decleration

于 2013-04-05T07:14:13.457 回答