2012-10-26 100 views
10

所以我正在学习课程,我偶然发现了一些我觉得很尴尬的东西。在使用之前,类函数/变量是否必须声明?

class Nebla 
{ 
    public: 
     int test() 
     { 
      printout(); 
      return x; 
     } 

     void printout() 
     { 
      printout2(); 
     } 

    private: 
     int x,y; 
     void printout2() 
     { 
      cout<<"Testing my class"; 
     } 
}; 

我发现,在I类可以使用的功能之前,我宣布他们(原型他们)

你可以看到我decleration之前使用printout()printout2()

而且我还可以使用变量声明它们

之前,你可以看到我做return x;在声明x之前。

为什么我可以在声明之前在类中使用函数和变量,但如果我这样做,我会得到一个错误?

感谢

+0

我还没有看到这个问题的具体答案,但我猜想这是由于编译器在课堂上做了两遍。 – chris

+0

@chris你是什么意思,编译器在类上做两次传递 –

+0

[This comment](http://stackoverflow.com/questions/2632601/why-are-forward-declarations-necessary#comment2646159_2632601)似乎加强了那个猜测。这个问题稍微处理了这个概念。 – chris

回答

13

好问题;多年来我都没有考虑过这个功能。我翻阅了几本C++书籍以找到答案,包括Stroustrup的The C++ Programming Language and The Annotated C++ Reference Manual,但没有人承认或解释这种差异。但是,我想我可以通过它来推理。

我相信你的榜样的原因是你的testprintout的身体并不真正出现在你的文件中。该代码

class MyClass { 
    void someFun() { 
    x = 5; 
    } 
    int x; 
}; 

...这似乎违反了其在使用之前声明变量的规则,实际上相当于:

class MyClass { 
    void someFun(); 
    int x; 
}; 

void MyClass::someFun() { 
    x = 5; 
} 

一旦我们重写它这样,很明显MyClass定义中的内容实际上是声明的列表。这些可以以任何顺序。在宣布声明之前,您不依赖x。我知道这是真的,因为如果你想重写这样的例子,

void MyClass::someFun() { 
    x = 5; 
} 

class MyClass { 
    void someFun(); 
    int x; 
}; 

......它将不再编译!因此,类定义首先(带有完整的成员列表),然后您的方法可以使用任何成员,而不考虑它们在类中声明的顺序。

最后一块难题是C++禁止在类定义之外声明任何类成员,因此一旦编译器处理了你的类定义,它就知道类成员的完整列表。这是在Stroustrup's 附注170页注释的注释C++参考手册:“成员列表定义了该类的全部成员,没有成员可以添加到别处。

谢谢你让我调查一下;我今天学了些新东西。 :)

+0

令人惊叹的答案,你100%肯定这个答案是正确的? –

+0

感谢您的赞扬。我确信我写的是什么,如果你担心标准和编译器的兼容性,这可能不够好。我认为aschepler的答案比我的“100%肯定”。 – Philip

+0

是的,这两个很好的答案,但我更喜欢你的:D,你解释它。 –

0

你能够做到这一点的原因是因为你打电话testprintoutprintout2的时候,他们将已创建。如果在执行任意函数之前调用以外的函数,那么您将得到一个错误。

认为类成员函数与其他类的评估流程是异步的。这不适用于独立功能,但您可以访问尚未实例化的数据成员。我不完全确定为什么我们能够做到这一点,但我认为它与瞬时对象类型有关。

+0

你能提供更多/更清晰的解释吗? –

+0

让我看看我正确理解这一点, 是因为所有的函数/变量都是在声明对象后声明的吗? –

4

为了说清楚,这是C++标准所要求的,而不仅仅是几个编译器处理类定义的方式。

N3242 3.3.7:

在一个类中声明的名称的潜在范围不仅包括以下声明的名称的点声明区,而且所有功能机构,大括号或 - 非静态数据成员的相等初始化程序,以及该类中的默认参数(包括嵌套类中的这些内容)。

1

除了菲利普的反应良好,斯特劳斯给名称查找规则的C++设计与演变一个很好的解释。这在“6.3说明”中有所描述。在6.3.1.1,“ARM的名称查找规则”,他提到在ARM定义2个规则:

[1]类型重新定义规则:A型名称可能不会在一类后,重新定义已经在那里使用了。

[2]重写规则:对内联定义的成员函数进行分析,就好像它们是在类声明结束后立即定义的一样。

所以在你的情况下,它会应用重写规则(菲利普推论),这就是为什么你可以转发引用这些类成员。

这本书可能主要是历史利益(它写在'94),但我认为这些规则今天应用相同的方式。