名称空间中类的前向声明

Forward declaration of a class in a namespace

本文关键字:声明 空间      更新时间:2023-10-16

如何在命名空间中向前声明类。例如,下面是一个库的头文件,用户不需要知道私有的myPtr,所以当包含在头文件下面时不需要包含boost头文件。那么,我如何向前声明boost::shared_ptr来启用用户代码编译呢?

MyClass.h

class MyClass
{
private:
  boost::shared_ptr<Mytype> myPtr;
}

TL;DR这里需要包含<boost/shared_ptr.hpp>。没有(聪明的)变通办法。但是MyType本身可以前向声明。

当然,你可以在标题的顶部写#include <boost/shared_ptr.hpp>,这样你的用户就不必自己做了。实际上,提供自治的标头(即可以首先包含而不会出现错误的标头)是一种很好的做法。


关于前向编译的规则稍微复杂一些。比起试图记住所有的案例,理解它们的原因更容易。

有两个因素:

    <
  • 语义/gh>内存属性(size &对齐)

语义:为了访问对象的方法、属性或基类,你需要了解它们。当然,除了构造函数、赋值操作符和析构函数,即使是自动生成的,也是方法之外,这似乎是显而易见的。我们很容易忘记它们。

内存属性:与大多数语言不同,c++试图尽可能高效,这意味着它会为对象直接分配内存,而不是在某处分配内存并在使用点使用指针,除非你指示它这样做(通过使用指针引用)。为了知道要分配多少,编译器需要看到对象的内部,即在底层是什么。这意味着,即使确切的细节是不可访问的(private/protected的东西),他们需要是可见的,所以它可以看到,24个字节对齐在一个8字节的边界是必需的(不相关的shared_ptr顺便说一下)。

在标准中,我们说对象的定义对于这两种需求(方法和内存布局)中的任何一种都是必需的。如果需要定义,那么它必须明显可用。


好了,现在我们知道了原因,我们可以检查各种东西。当:

时需要定义:
  • 使用对象作为sizeofalignof的参数?yes(显然,需要内存属性)
  • 使用对象作为属性?yes(需要内存属性)
  • 使用对象作为静态属性?没有 (1)
  • 使用指针或引用对象作为属性?没有 (2)
  • 在函数声明中使用对象作为参数?没有 (3)
  • 在函数声明中使用对象作为返回类型?没有 (3)
  • 传递指针或引用到一个对象周围?没有 (4)
  • 转换为基类?yes(语义检查存在&基类可访问性)
  • 转换为另一种类型? 取决于(5)

(1)声明不需要任何东西,但是静态属性的定义需要对象的定义。

(2)指针是32位或64位大(取决于你如何编译,…)独立于对象。引用具有实现定义的表示。

(3)即使按值接受/返回!但是,函数定义(如果在内部使用)或函数调用站点可能需要它。

(4)当然,如果你尝试使用它(p->foo()p.foo()),那就是另一回事了。

(5)如果需要使用对象的转换操作符,那么显然是必需的;否则,如果使用其他类型的构造函数,则适用与函数相同的规则(但需要使用其他类型定义)。

我希望事情现在更清楚了。