Still celebrating National IT Professionals Day with 3 months of free Premium Membership. Use Code ITDAY17

x
?
Solved

Forward references and static class member instances !

Posted on 2003-11-16
9
Medium Priority
?
633 Views
Last Modified: 2010-04-01
I am playing with forward references and am having trouble figuring out why the following code compiles/links and other code will not compile.

Firstly, the code that compiles:

/////////////////////////////////////////

#include <iostream>

using namespace std;

class B
{
public:
      B() { cout << "B constructor" << endl;}
};

class A
{
public:
      A() { cout << "A constructor" << endl;}
private:
      static B classBInstance;
      B a;
};


//B A::classBInstance;

int main()
{
      A a;
      return 0;
}


/////////////////////////////////////////

Right then, two classes stripped to the bare bones.  I have declared A to hold a static member of a class B instance, yet have commented out the part that actually initialises it.  

Output:

B constructor
A constructor

Why does the code still compile and run ?  I should be getting a linker error at the lack of A::classBInstance should I not ??  And why is the output showing that the B member (the none static one) is being constructed before the A object itself ?

Secondly,

/////////////////////////////////////////

#include <iostream>

using namespace std;

class B;

class A
{
public:
      A() { cout << "A constructor" << endl;}
private:
      B a; <-----------------
};

class B
{
public:
      B() { cout << "B constructor" << endl;}
};

int main()
{
      A a;
      return 0;
}

/////////////////////////////////////////

I have set up a forward reference to class B, as can be seen, yet the code as-is still fails to compile, moaning about line <-- with:

'a' uses undefined class 'B'

Yet if I change the <-- line to be

B a();

i.e. add parentheses, or move the whole of class B to be above class A, there is no complaint.  Why are the parentheses needed ?  And what is the correct syntax for a forward reference of this type ?
0
Comment
Question by:mrwad99
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 3
  • 2
  • 2
  • +1
9 Comments
 
LVL 3

Expert Comment

by:monkesdb
ID: 9758995
as for your question i'm not at all sure.

B a;       ' This should define a as being of type B

B a();     ' Whereas, this should define a as being of type B and construct it.

so if one of them was not to work I would have thought the second wouldn't

But
it would probably be better to use pointers. With pointers there's no problem what so ever since it doesn't need to know the size of the class to create a pointer to it. and it works when A creates a B and B creates an A (circular reference).

class A
{
public:
     A() : { cout << "A constructor" << endl;}
private:
     B* b;
};

class B
{
public:
     B() : { cout << "B constructor" << endl;}
private:
     A* a;
};

int main()
{
     A a;
     return 0;
}
0
 
LVL 19

Author Comment

by:mrwad99
ID: 9759018
Yeah thanks, but I am only interested as to why it does not work without pointers.

Also the example does not work without forward reference to either of A or B, depending on the order they are coded in (I used forward ref to B to make it work).  Also no need for the : in the constructors.  Using MSVC++ 6.0SP3

Ta anyway :)
0
 
LVL 11

Expert Comment

by:bcladd
ID: 9759315
It doesn't work in the second case because if you declare a member of type B the compiler must add the size of the member to the size of the class but ALL it knows about B at that point is that you have promised that it is a classs somewhere defined. Thus putting int a B* will fix the problem.

B a; // requires size of B to instanitate (full definition required)

B a(): // defines a FUNCTION called a that takes no parameters and returns a B

B * a; // B is a type so this is safe; to USE a inside any member of the class you'll need the full definition

Your first question is related to this answer: The object member (non-static) is an object so the constructor is still called. The static member is NOT constructed and no memory is set asiide for it BUT it is only an undefined symbol if someone references it (excuse the anthropomorphism). So only one B is built, the one built when the A is instantiated in main.

Hope this helps, -bcl
0
Independent Software Vendors: We Want Your Opinion

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 

Expert Comment

by:marcon33
ID: 9759460
//B A::classBInstance;

This creates another instance of A, it is not necessary. Putting is outside of main is sort of like declaring a function. The compiler says, OK. If it is not there it is OK too.


Objects are constructed by the compiler without you having to initialize with a line like the above. They are also "destructed" in the background if you do not provide a destructor.
It seems that I read somewhere that the order of object creation and destruction has to do with keeping things simple. B is created , Then A is created, B is destroyed then A is destroyed. If it were the opposite and you were concerned about compiler complexity and speed, create A , create B, destroy A but first check if other objects were created first, then do that again, and again.......

You can watch what is happening with the code below :

#include <iostream>

using namespace std;

class B
{
public:
     B() { cout << "B constructor" << endl;}
       ~B();
};

B::~B()
{
      cout << " B destroyed" << endl;
}


class A
{
public:
     A() { cout << "A constructor" << endl;}
       ~A();


private:
     static B classBInstance;
     B a;
};

 A::~A()
{
      cout << " A destroyed" << endl;
}  

include <iostream>
#include  "TestExp.h"

using namespace std;

B A::classBInstance;

int main()
{
     A a;
     return 0;
}


Hope that helps1
0
 
LVL 19

Author Comment

by:mrwad99
ID: 9759555
Right,

Sorry about the delay, I was scrutinizing what was said and then applying it in my code.

marcon33 -

>//B A::classBInstance;
>This creates another instance of A, it is not necessary.

I am lost.  Whenever I have had a class containing a static member it has always been initialised in the body of the class, i.e. the .cpp file.  I am not sure at all as to what you are saying here.  Can you elaborate ?

bcladd -

1) I am still not clear as to why defining an object to have a pointer to a type that has not yet been fully defined is allowed, yet having an actual object is not allowed.  Can anyone clarify this further ?  (sorry).

2)  Regarding the static variable, I have changed my code to:

//////////////////////////////////////

#include <iostream>

using namespace std;

class B
{
public:
    B() { cout << "B constructor" << endl;}
    void F() { cout << "B::F()" << endl; }
};


class A
{
public:
    A() { cout << "A constructor" << endl;}
    static B b;
};

B A::b;

int main()
{
    A a;
    B b;
    a.b.F();
    return 0;
}

//////////////////////////////////////

Now, without the B A::b definiton, the code will not link, stating that it cannot find a.b.F().  This is because I am trying to call the method of an object, but the object of does not exist,  correct ?

2)  Code change again:

//////////////////////////////////////

#include <iostream>

using namespace std;

class B
{
public:
      B(int) { cout << "B(int) constructor" << endl; }
        B() { cout << "B constructor" << endl;}
      void F() { cout << "B::F()" << endl; }
private:    
};


class A
{
public:
          A() { cout << "A constructor" << endl;}
          static B b;
};


B A::b(1000);

int main()
{
      cout << "Creating A" << endl;
        A a;
      cout << "Creating B" << endl;
      B b;
      cout << "Calling method..." << endl;
      a.b.F();
        return 0;
}


OUTPUT:

B(int) constructor (i)
Creating A
A constructor (ii)
Creating B
B constructor (iii)
Calling method...
B::F()

//////////////////////////////////////

Now, with (i) it appears that the compiler is constructing the static object before I have even created a B object !  What ?!?!?
(ii) is obviously down to the creation of a, the A object, but (iii) - why is the default B constructor being called *here* (it looks like it has already constructed the static object, hence (i) ), and why is the default one anyway ?!?! I have defined it to be a B object taking 1000 as a parameter after all !

Cheers again all !
0
 
LVL 11

Accepted Solution

by:
bcladd earned 160 total points
ID: 9759665
(1) A reference's or a pointer's size can be determined without knowing the size of the object refered/pointed to. Why? Two reasons: the standard says so (sorry, my standard is not at home so I can't give the reference) and it is practical to do so (most computers have a single size for addresses which is what a reference/pointer stores).

So, without knowing what class Q looks like, it is possible to tell the compiler that there is such a class (the forward reference) and to declare a pointer at one:

    class Q;
    class R {
        Q * myQ;
    };

The compiler knows that it should set aside enough room to hold a pointer at a Q. It is not possible to in-line the body of any function that calls any members using myQ, however, because the compiler knows only an incomplete type until the full definition of class Q is provided.

(2) marcon33: You say the following:

    //B A::classBInstance;

    This creates another instance of A, it is not necessary. Putting is outside of main is sort of like declaring a function. The compiler says, OK. If it is not there it is OK too.

and it is incorrect. Uncommenting the line will NOT create an A. All it does is have the compiler set aside the space for an object of type B which has the name A::classBInstance (it is an almost standard global variable declaration; only the name looks weird). When a data member is declared static in a class no space is actually set aside to store the one value for the class. It requires a single external delcaration such as the one that is commented out here.

(3) Static object are created BEFORE execution of your main program begins (assuming that they are properly declared outside the class itself). Thus in the first example, with the commented-out line restored, the output is
    B constructor - Static object constructed BEFORE main runs
    B constructor - Member of A that is being constructed. Default constructor for class type data member called
    A constructor - Constructor body for class A runs

(4) Look at your final code to answer your questions:

    B A::b(1000); // (i)

    int main()
    {
         cout << "Creating A" << endl;
         A a; // (ii)
         cout << "Creating B" << endl;
         B b; // (iii)
         cout << "Calling method..." << endl;
         a.b.F();
        return 0;
    }

    OUTPUT:

    B(int) constructor (i)
    Creating A
    A constructor (ii)
    Creating B
    B constructor (iii)
    Calling method...
    B::F()

Hope this helps, -bcl
0
 

Expert Comment

by:marcon33
ID: 9759938
ooops in over my head on this one. bcladd and mrwadd99, I appreciate your indulgence.
0
 
LVL 3

Expert Comment

by:monkesdb
ID: 9760609
(sorry about the colons they shouldn't be there i started initialising stuff and decided it was a bad idea so removed it but forgot the colons)
0
 
LVL 19

Author Comment

by:mrwad99
ID: 9766354
Once again, an outstanding answer from bcladd.  The standard of the experts here is jaw droppingly good, and really is an inspiration !

Many thanks to all !
0

Featured Post

VIDEO: THE CONCERTO CLOUD FOR HEALTHCARE

Modern healthcare requires a modern cloud. View this brief video to understand how the Concerto Cloud for Healthcare can help your organization.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Errors will happen. It is a fact of life for the programmer. How and when errors are detected have a great impact on quality and cost of a product. It is better to detect errors at compile time, when possible and practical. Errors that make their wa…
Templates For Beginners Or How To Encourage The Compiler To Work For You Introduction This tutorial is targeted at the reader who is, perhaps, familiar with the basics of C++ but would prefer a little slower introduction to the more ad…
The goal of the tutorial is to teach the user how to use functions in C++. The video will cover how to define functions, how to call functions and how to create functions prototypes. Microsoft Visual C++ 2010 Express will be used as a text editor an…
The viewer will learn how to pass data into a function in C++. This is one step further in using functions. Instead of only printing text onto the console, the function will be able to perform calculations with argumentents given by the user.

688 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question