如何在命名空间中转发声明类。例如,下面是一个库的头文件,用户不需要知道私有的myPtr,所以当包含在头文件下面时,不需要包含boost头文件。那么我如何向前声明boost::shared_ptr以启用用户代码编译呢?
MyClass.h
class MyClass
{
private:
boost::shared_ptr<Mytype> myPtr;
}
这里需要包含
。没有(聪明的)办法绕过它。但是mytype
本身可以向前声明。
当然,您可以在头的顶部编写#include
,这样您的用户就不必自己完成了。提供自治头(即可以在没有错误的情况下首先包含的头)实际上是一个很好的实践。
关于正向编译的规则略显复杂。理解他们的原因比尝试和记住所有的案例要容易得多。
有两个因素:
语义:为了访问对象的方法、属性或基类,您需要了解它们。当然,除了构造函数、赋值运算符和析构函数(即使是自动生成的)都是方法之外,这似乎是显而易见的。很容易忘记他们。
内存属性:与大多数语言不同,C++试图尽可能地高效,这意味着它将为对象分配内存,而不是将其分配到某个地方,并且只在使用点使用指针,当然除非您指示它这样做(通过使用指针或引用)。为了知道要分配多少,编译器需要查看对象的内脏(即引擎盖下面是什么)。这意味着,即使不能访问确切的细节(private
/protected
东西),它们也需要是可见的,这样就可以看到需要在8字节边界上对齐的24个字节(顺便说一下,与shared_ptr
无关)。
在标准中,我们说对象的定义对于这两种需求(方法和内存布局)中的任何一种都是必需的。如果定义是必需的,那么它显然必须是可用的。
好了,现在我们知道了原因,我们可以检查各种东西了。在下列情况下是否需要定义:
sizeof
或alignof
的参数?是(显然,需要内存属性)(1)声明不需要任何东西,但是静态属性的定义需要对象的定义。
(2)指针的大小不是32位就是64位(取决于你编译的方式,...)与对象无关。引用具有实现定义的表示形式。
(3)即使按值取/还!函数定义(如果在内部使用)或函数调用站点可能需要它。
(4)当然,如果您尝试使用它(p->foo()
或p.foo()
),那就另当别论了。
(5)如果需要使用对象的转换运算符,那么它显然是必需的;否则,如果使用另一个类型的构造函数,则应用与函数相同的规则(但另一个类型定义是必需的)。
我希望事情现在更清楚了。