39

我有一个类应该有同一个类的私有成员,例如:

class A {
    private:
        A member;
}

但它告诉我 member 是不完整的类型。为什么?如果我使用指针,它不会告诉我不完整的类型,但我宁愿不使用指针。任何帮助表示赞赏

4

8 回答 8

44

在你声明你的成员时,你仍在定义A类,所以类型A仍然是未定义的。

但是,当您编写 时A*,编译器已经知道它代表类名,因此定义A了“指向 A 的指针”类型。这就是为什么您可以嵌入一个指向您正在定义的类型的指针。

同样的逻辑也适用于其他类型,所以如果你只写:

class Foo;

您声明了类 Foo,但您从未定义它。你可以写:

Foo* foo;

但不是:

Foo foo;

另一方面,A如果编译器允许递归定义,你希望你的类型有什么样的内存结构?

但是,有时在逻辑上,有一个类型以某种方式引用同一类型的另一个实例。人们通常为此使用指针,甚至更好:智能指针(如boost::shared_ptr)以避免不得不处理手动删除。

就像是:

class A
{
  private:
    boost::shared_ptr<A> member;
};
于 2011-06-14T20:41:23.847 回答
28

这是您要实现的目标的工作示例:

class A {
public:
    A() : a(new A()) {}
    ~A() { delete a; a = nullptr; }
private:
    A* a;
};

A a;

堆栈溢出快乐!

于 2011-06-14T21:14:00.973 回答
5

A在其定义结束之前是“不完整的”(尽管这不包括成员函数的主体)。

造成这种情况的原因之一是,在定义结束之前,无法知道有多大A(这取决于成员大小的总和,以及其他一些因素)。您的代码就是一个很好的例子:您的类型A由 type 的大小定义A

显然,类型的对象A可能不包含也是类型的成员对象A

您必须存储指针或引用;想要存储任何一个都可能是可疑的。

于 2011-06-14T20:47:30.467 回答
4

理解类不完整背后的原因的一种简单方法A是尝试从编译器的角度来看待它。

除其他外,编译器必须能够计算A对象的大小。知道大小是一个非常基本的要求,在许多情况下都会出现,例如在自动内存中分配空间、调用 operatornew和评估sizeof(A)。但是,计算 的大小A需要知道 的大小A,因为a是 的成员A。这导致无限递归。

编译器处理这个问题的方法是在A完全知道它的定义之前考虑不完整。您可以声明指向不完整类的指针和引用,但不允许声明值。

于 2016-08-31T14:00:12.623 回答
2

您不能在 A 中包含 A。如果您能够做到这一点,并且您声明了,例如,A a;您将需要a.member.member.member...无限引用。您没有那么多可用的 RAM。

于 2011-06-14T20:42:51.740 回答
1

一个实例如何class A也包含另一个实例class A

如果需要,它可以保存指向 A 的指针。

于 2011-06-14T20:44:05.667 回答
1

当您尝试使用尚未完全定义的类时,会发生此类错误。

尝试A* member改用。

于 2011-06-14T21:11:34.113 回答
0

当编译器在代码中遇到 A 的对象时,就会出现问题。编译器将揉搓它的手并着手创建一个 A 的对象。在这样做的同时,它会看到 A 有一个再次属于 A 类型的成员。因此,为了完成 A 的实例化,它现在必须实例化另一个 A ,并且在这样做它必须实例化另一个 A 等等。你可以看到它会以一个没有限制的递归结束。因此这是不允许的。编译器在开始实例化一个类的对象之前确保它知道所有成员的所有类型和内存需求。

于 2015-02-10T10:28:17.687 回答