为什么在声明之前可以调用成员函数?

时间:2015-03-22 15:39:05

标签: c++

我一直认为C / C ++在1遍中被解析,因此,必须先声明符号才能使用它。

因此这不起作用:

void a()
{
   b();
}

void b() 
{
}

除非我们转发声明void a();

但是,我注意到这确实有效:

class Foo 
{
  public:
  void a() 
  {
    b();
  }

  void b()
  {
  }
};

为什么这样做?如果C ++在一次通过代码中被解析,那么我认为这不起作用,因为在调用时没有定义符号Foo :: b()。

4 个答案:

答案 0 :(得分:1)

有些东西被称为前向引用(与前向声明不同)

class C
{
public:
   void mutator(int x) { myValue = x; }
   int accessor() const { return myValue; }
private:
   int myValue;
};

此处myValue在声明之前被访问。 C ++通常不允许前向引用,但对于类成员,它允许它们。在看到myValue

之前,记住mutator和accessor的定义是编译器的工作

答案 1 :(得分:1)

班级的定义:

class Foo 
{
public:
  void a()  {  b(); }
  void b()  {  }
};

具有与以下相同的含义:

class Foo 
{
public:
  void a();
  void b();
};
void Foo::a()  {  b(); }
void Foo::b()  {  }

这就是函数体看到所有成员的原因,就好像该类已经完全定义一样。这是通过C ++标准中规定的方式:

  

3.3.7 / 1 在一个类中声明的名称的潜在范围包括不仅是名称的声明点后面的声明性区域,而且还包括职能机构,   非静态数据成员的brace-or-equal-initializers,默认值   该类中的参数(包括嵌套类中的这些东西)。

编译器仍然在一次传递中解析文件。但是解析语法结构只是更大的编译过程的一部分,其中将上下文应用于解析的语法生成也起到了作用(另见SO question)。

答案 2 :(得分:0)

在第一遍中读取所有类接口,因此当编译function a的代码时,它知道函数b存在于类Foo

答案 3 :(得分:0)

C ++标准的3.3.7节包括其他规则

的陈述
  

在类中声明的名称的潜在范围不仅包括名称声明点后面的声明性区域,还包括所有函数体,默认参数,异常规范和括号或等于初始化函数。该类中的非静态数据成员(包括嵌套类中的这些内容)。

基本上,这要求对所提到的上下文进行分析,如果它们在类定义中内联出现,则推迟到整个类定义被处理之后,就像使用了外联定义一样。

相关规则见9.2节:

  

类说明符的结束},类被视为完全定义的对象类型(3.9)(或完整类型)。在类 member-specification 中,该类在函数体内被视为完整,   默认参数, using-declarations 为非静态引入继承构造函数(12.9), exception-specifications brace-or-equal-initializers 数据成员(包括嵌套类中的这类内容)。除此以外   它在自己的类 member-specification 中被认为是不完整的。