为什么一个类在没有前向声明的情况下有一个指向另一个类的实例的指针?

时间:2016-07-28 01:49:58

标签: c++ typename

我知道这是不可能的

class foo{
    foo bar;
};

因为编译器不知道为foo准备多少内存。 但请考虑:

class A{
    B::C* foo;
};

class B{
    class C{
    };
};

无论C的定义如何,编译器都能确切地知道A的大小似乎是合理的。为什么这不可能呢? (标准是否明确禁止?为什么?)

我有一个非常粗略的方法绕过将foo视为uintptr_t的约束,然后reinterpret_cast将其视为C,这只是为了表明我真的没有得到(客观)理由,而且我知道如果没有正当理由的话,这是非常糟糕的做法。

最后,标准禁止这样做。

3 个答案:

答案 0 :(得分:1)

一个重大挑战是:

class other {};

namespace N {
  class foo {
    other* p;
  };

  class other {};
}

应该考虑哪个other*?编译器的工作量太大了。虽然我们可以通过::otherN::other明确表达,但在上述情况下仍会造成混淆。

答案 1 :(得分:0)

TL; DR:那是因为标准规定语法是这样的。不幸的是,它就像那样简单。

这是我遵循的路径:

  1. 您打开标准,例如c++11 draft

  2. 您在第9节中寻找关于课程的部分。您立即看到它有“9.2级成员”小节。定义了完整的语法。经过一些阅读后,您将进入member-declarator。我们背景中最重要的部分是declarator

  3. 第8节。声明者。在第4页。你有declarator的语法。在完成所支持的花哨语法的所有正式定义后,您会注意到它取决于id-expression

  4. 这在5.1.1主表达式中定义。一般([expr.prim.general])。它必须是qualified-id的{​​{1}}。

  5. 因此,您的问题可以改为“为什么名称查找不起作用?”但这是另一个问题。

    FWIW,我猜不多,但这是一个设计决定。它需要跟踪编译单元的所有失败的分辨率。在这种情况下进行懒惰检查具有非常深远的影响。

答案 2 :(得分:0)

示例后

class foo{
    other* bar;
    ...
};
class other{
    ...
}

...其中(1)使用未声明的名称,(2)缺少关键的分号,你问

  

为什么这不可能呢? (标准是否明确禁止?为什么?)

圣标要求每个名字必须在使用前声明。

请注意,在给定的C ++实现中,即使数据指针也可以具有不同的大小(void*保证能够保存任何数据指针),更不用说函数指针了。

声明在首次使用前声明的替代方法可能是采用默认值,例如在早期C天使用隐式int。例如。编译器可以假设other是类类型。但这在一般情况下并不是很有用,而且会使语言复杂化。

但是,在这种特殊情况下,可以将other的必要最小声明与其首次使用结合起来:

class foo{
    class other* bar;
    //...
};

class other{
    //...
};

或者您可以使用普通的前向声明:

class other;

class foo{
    other* bar;
    //...
};

class other{
    //...
};

正因为编译器知道other是类类型,而不是例如一个函数,或charvoid的同义词(如果基本原始数据指针的大小有任何变化,则指向这些类型的指针是最大的。)

然后断言:

  

上面发布的虚拟示例似乎没有任何实际意义。我没有前向声明的原因是,当你有一个嵌套类时,没有可以保留封装的前向声明。

那是错的。

E.g。这很好用:

class Foo
{
private:
    class Other;
    Other* bar;

    class Other{};

public:
    Foo(): bar( new Other ) {}
};

但是,在第一次使用中使用声明的快捷方式class Other* bar;在这种情况下不起作用。那是因为它有效地将Other声明为命名空间级别类而不是嵌套级别。

相关问题