25

So I was learning about classes and I stumbled upon something I found was quite awkward to me.

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

        void printout()
        {
            printout2();
        }

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

I found that in a class I can use functions before I declare them (prototype them)

You can see I used printout() , printout2() before decleration.

And I can use variables also before declaring them

You can see I did return x; before declareing x.

Why can I use functions and variables in classes before declaration but outside the class if I do that, I get an error?

Thanks

Lee Taylor
  • 7,761
  • 16
  • 33
  • 49
Mohamed Ahmed Nabil
  • 3,949
  • 8
  • 36
  • 49
  • 1
    I haven't seen a specific answer for this one, but I've guessed that it's due to the compiler doing two passes on the class. – chris Oct 26 '12 at 21:51
  • @chris what do you mean that the compiler is doing two passes on the class – Mohamed Ahmed Nabil Oct 26 '12 at 21:52
  • [This comment](http://stackoverflow.com/questions/2632601/why-are-forward-declarations-necessary#comment2646159_2632601) seems to reinforce that guess. The question deals with the concept a bit. – chris Oct 26 '12 at 21:57

5 Answers5

27

Good question; I've relied on that feature for years without thinking about it. I looked through several C++ books to find an answer, including Stroustrup's The C++ Programming Language and The Annotated C++ Reference Manual, but none acknowledge or explain the difference. But, I think I can reason through it.

The reason, I believe, that your example works is that the bodies of your test and printout aren't truly where they appear in your file. The code

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

...which appears to violate the rule of having to declare variables before you use them, is actually equivalent to:

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

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

Once we rewrite it like that, it becomes apparent that the stuff inside your MyClass definition is actually a list of declarations. And those can be in any order. You're not relying on x until after it's been declared. I know this to be true because if you were to rewrite the example like so,

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

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

...it would no longer compile! So the class definition comes first (with its complete list of members), and then your methods can use any member without regard for the order in which they're declared in the class.

The last piece of the puzzle is that C++ prohibits declaring any class member outside of the class definition, so once the compiler processes your class definition, it knows the full list of class members. This is stated on p.170 of Stroustrup's The Annotated C++ Reference Manual: "The member list defines the full set of members of the class. No member can be added elsewhere."

Thanks for making me investigate this; I learned something new today. :)

Philip
  • 1,532
  • 12
  • 23
  • Thanks for the compliment. I am sure of what I wrote, which may not be good enough if you're worried about the standard and compiler compatibility. I think aschepler's answer is more "100% sure" than mine. – Philip Oct 26 '12 at 22:45
  • 3
    @Philip: If you want a better example, use typedefs: http://ideone.com/Es28Zq. C++ does all the declarations _in order_ and then the method bodies. – Mooing Duck Oct 26 '12 at 23:20
  • What about inline methods? – Mateusz Wojtczak Aug 30 '16 at 08:30
12

Just to make it clear, this is required by the C++ Standard, not just the way several compilers handle class definitions.

N3242 3.3.7:

The potential scope of a name declared in a class consists not only of the declarative region following the name's point of declaration, but also of all function bodies, brace-or-equal-initializers of non-static data members, and default arguments in that class (including such things in nested classes).

aschepler
  • 70,891
  • 9
  • 107
  • 161
2

Besides Philip's good response, Stroustrup gives a nice explanation of Name Lookup Rules in The Design and Evolution of C++. This is described in "6.3 Clarifications". In 6.3.1.1, "The ARM Name Lookup Rules", he mentions 2 rules defined in the ARM:

[1]The type redefinition rule:A type name may not be redefined in a class after it has been used there.

[2] The rewrite rule: Member functions defined inline are analyzed as if they were defined immediately after the end of their class declarations.

So in your case it would apply the rewrite rule (as Philip deduced), that's why you can forward reference those class members.

This book may be mainly of historical interest (it's written in '94), but I think those rules are applied the same way today.

German Garcia
  • 1,209
  • 14
  • 14
1

The reason you are able to do this is because by the time you call test, printout or printout2, they will have already been created. If you call the function outside the arbitrary function before it's implementation, then you'll get an error.

Think of class member-functions as being asynchronous with the flow of evaluation of the rest of the class. This won't work with stand alone functions, but you can access data members that haven't been instantiated yet. I'm not completely sure why we are able to do this, but I think it has to do with instantitation of the class object.

David G
  • 94,763
  • 41
  • 167
  • 253
1

Why can I use functions and variables in classes before declaration

This is because the body of a member function is a complete-class context of a class, as mentioned in the quoted statements below:

From class.mem.general#6:

6. A complete-class context of a class is a:

  • function body ([dcl.fct.def.general]),

  • default argument,

  • noexcept-specifier ([except.spec]), or

  • default member initializer

within the member-specification of the class.

This means that the usage of printout inside member function test and the usage of printout2 inside member function printout is allowed here even though those members appear later when writing the class definition.

Jason
  • 36,170
  • 5
  • 26
  • 60