Problem with Composition Relation/Forward Referencing.

M

Milind

Hi,

I was trying to implement a composition relation, somthing of the
following type:

class A
{
public:
class B
{
int member_of_B;
};

B b;
// ... some other members;
};


but as the defintion of B is too large I thought of may be writing it
outside. ;). So i modified the class as:


class A
{
class B;
B b;
....
};

class B {
int member_of_B;
};

We have two issues here:

1. anybody including "the .h" (in which i have declared the classes)
would get the type B and can use it. which i didn't intend to allow ;).

2. gcc 3.3.2 gave me an error of this type:

error: field `b' has incomplete type

Am I not allowed to use forward referencing ? What exactly does the
statement

class B;

in definition of A do? Doesn't it introduce the typename in A?

Any suggestions? Am I missing something obvious ?

~ M.
 
J

John Harrison

Milind said:
Hi,

I was trying to implement a composition relation, somthing of the
following type:

class A
{
public:
class B
{
int member_of_B;
};

B b;
// ... some other members;
};


but as the defintion of B is too large I thought of may be writing it
outside. ;). So i modified the class as:


class A
{
class B;
B b;
...
};

class B {
int member_of_B;
};

We have two issues here:

1. anybody including "the .h" (in which i have declared the classes)
would get the type B and can use it. which i didn't intend to allow ;).

2. gcc 3.3.2 gave me an error of this type:

error: field `b' has incomplete type

Am I not allowed to use forward referencing ? What exactly does the
statement

class B;

in definition of A do? Doesn't it introduce the typename in A?

A couple of misunderstandings here. The correct way to make the forward
declaration is

class B;

class A
{
B b;
};

class B
{
};

Since class B is declared outside of class A, the forward declaration
must also be outside of class A.

But the second misunderstanding is that an incomplete class (which is
what B is between the forward declaration and the actual declaration) is
only good for a very few things, for instance declaring pointers. In you
code the compiler needs to know the size of B in order to generate code
for A, but it does not know the size of B from just the forward declaration.

The obvious solution to this is just to reverse the order in which you
define your classes.

The other solution would be to rewrite A like this

class B;

class A
{
B* b;
};

By using a pointer you would not need to include the definition of B in
the header file at all. This might also help with your other requirement
that B not be usable by itself.
Any suggestions? Am I missing something obvious ?

Not obvious, but I'd guess you come from a Java background and haven't
quite absorbed yet that objects in C++ are values not references. Easily
the most fundamental difference between Java and C++.

john
 
M

Milind

The correct way to make the forward
declaration is

class B;

class A
{
B b;
};

class B
{
};

Well Even that wont work ;( i copy pasted the above stuff and tried to
compile it with g++ 3.3.2 on my Debian box.

That thing apart,
Since class B is declared outside of class A, the forward declaration
must also be outside of class A.

As per my previous posting...
I dont want anybody use class B directly, i always want others to use
it as A::B ,i.e as a part of class A. declaring in the way you
suggested will allow everyone else to use it as is.

making it a member would definitely impose using object of class B
inside A's object with this syntax. however, we can't stop ppl from
using bare class B objects.

But the second misunderstanding is that an incomplete class (which is
what B is between the forward declaration and the actual declaration) is
only good for a very few things, for instance declaring pointers.

Yes; pointers work as they have same sizes and we dont allocate memory.
i tried that b4 posting here atfirst and it works fine. that is why i
got confused about forward referencing. if the type is known, why can't
the compiler do a second pass to allocate the mem ?
the compiler needs to know the size of B in order to generate code
for A, but it does not know the size of B from just the forward declaration.

that is why i asked:
i mean doesn't the compiler do a second pass to get the size and
stuff??
The obvious solution to this is just to reverse the order in which you
define your classes.

hmmmm ..... but still if i define class B outside class A i will get it
exposed to anyone including the header file.
The other solution would be to rewrite A like this

class B;

class A
{
B* b;
};

By using a pointer you would not need to include the definition of B in
the header file at all. This might also help with your other requirement
that B not be usable by itself.

I dont know how by just declaring a pointer in a class can i restrict
B's usage. could you ellaborate please ?

I did try and implement this with private constructors and friend
functions. However that necessarilly means something else semantically.


i want to implement something like-> A::B always.
Not obvious

Thnx. cause i was getting pretty worried :)

Thnx.
~M
 
M

Milind

Hey Fabio,

I think the pipl idiom is mostly concerning the avoiding of unnecessary
recompilations and opace interafaces for a class. The example that we
had a couple of days back on the group was regarding the size of a
std::string. i dont exactly believe that the idiom would be useful in
my case.

Thanks.
~M
 
K

Karl Heinz Buchegger

Milind said:
Hey Fabio,

I think the pipl idiom is mostly concerning the avoiding of unnecessary
recompilations and opace interafaces for a class. The example that we
had a couple of days back on the group was regarding the size of a
std::string. i dont exactly believe that the idiom would be useful in
my case.

It would solve *exactly* your requirement of
 
K

Kai-Uwe Bux

Milind said:
Hi,

I was trying to implement a composition relation, somthing of the
following type:

class A
{
public:
class B
{
int member_of_B;
};

B b;
// ... some other members;
};


but as the defintion of B is too large

Why "too large"? Did you exceed compiler limits?

I thought of may be writing it outside. ;). So i modified the class as:


class A
{
class B;
B b;
...
};

class B {
int member_of_B;
};

We have two issues here:

1. anybody including "the .h" (in which i have declared the classes)
would get the type B and can use it. which i didn't intend to allow ;).

Well, this is not really a difference. With your original code, everybody
could declare objects of type B. One would just type A::B instead of B.
Now, you might say that this additional typing effort on part of the user
is exactly what you want to impose (sort of as a forced reminder). Well,
that won't fly either since everybody could just do a

typedef A::B B;

at the begining of their file to get rid of that restriction.

2. gcc 3.3.2 gave me an error of this type:

error: field `b' has incomplete type

Am I not allowed to use forward referencing ? What exactly does the
statement

class B;

in definition of A do? Doesn't it introduce the typename in A?

Well, it is a forward declaration inside the scope of A. The compiler should
complain when you do not follow that up by a real declaration inside that
scope.

Also, forward declarations are incomplete. The size of class B is not
deducible from a forward declaration. Thus, the compiler cannot deduce how
much memory is needed for the member object b.


Best

Kai-Uwe Bux
 
J

John Harrison

Milind said:
Well Even that wont work ;( i copy pasted the above stuff and tried to
compile it with g++ 3.3.2 on my Debian box.

Well no, it wasn't intended to work, it only corrects the first of your
errors, not the second.
That thing apart,




As per my previous posting...



I dont want anybody use class B directly, i always want others to use
it as A::B ,i.e as a part of class A. declaring in the way you
suggested will allow everyone else to use it as is.

OK, well then the only choice you have is to declare B inside of A.
making it a member would definitely impose using object of class B
inside A's object with this syntax. however, we can't stop ppl from
using bare class B objects.





Yes; pointers work as they have same sizes and we dont allocate memory.
i tried that b4 posting here atfirst and it works fine. that is why i
got confused about forward referencing. if the type is known, why can't
the compiler do a second pass to allocate the mem ?

It just doesn't, C++ was designed that way. Makes life easier for the
compiler writers I guess.

john
 
J

Jack Klein

On Fri, 30 Sep 2005 06:48:50 GMT, John Harrison

[snip]
A couple of misunderstandings here. The correct way to make the forward
declaration is

class B;

class A
{
B b;
};

class B
{
};

ITYM either:

class A
{
B *b;
};

....or:

class A
{
B &b;
};
 
M

Milind

Why "too large"? Did you exceed compiler limits?

No; BTW, how do you decide on that ??
Well, this is not really a difference. With your original code, everybody
could declare objects of type B. One would just type A::B instead of B.

oh. i am sorry, i intended to make B private. Then i am under the
impression that doint something like that is going to make a hell lot
of difference...

First, i dont clutter the namespace in which A is declared. i only have
the defintion of B inside A.
Second, i strictly implement the composition relation, Whenever A dies,
B has to die...
Now, you might say that this additional typing effort on part of the user
is exactly what you want to impose (sort of as a forced reminder). Well,
that won't fly either since everybody could just do a

typedef A::B B;

Good trick. but i dont intend to impose any typing overheads (because
initially i will be typing more!!) ;) i just dont want ppl to use B as
you have suggested here. The rule is always make a object of A and we
can then create B inside functions of A.

~M
 
K

Kai-Uwe Bux

Milind said:
No; BTW, how do you decide on that ??

I would trust that the compiler will tell me.

oh. i am sorry, i intended to make B private. Then i am under the
impression that doint something like that is going to make a hell lot
of difference...
Yes, if B is private, it makes a *huge* difference.

So, I take it you are just concerned about the file growing a little too
large. Well, you could put the declaration of B inside A and do the
definition afterwards:


#include <iostream>

class A {

class B {

int i;

void print ( void ) const;
};


};

void A::B::print ( void ) const {
std::cout << i << '\n';
}


Of course, this way you can also put the definitions of a B-methods into a
different file.

First, i dont clutter the namespace in which A is declared. i only have
the defintion of B inside A.
True.


Second, i strictly implement the composition relation, Whenever A dies,
B has to die...

Classes don't die. Objects do. Composition just means that you have an
object of type B as a member of A. It does not mean that there cannot be
any independent B-objects. All member objects of any A-object will die
during the agony of the ambient object. Whether the class B is local or not
is immaterial in this regard.


Best

Kai-Uwe Bux
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,780
Messages
2,569,610
Members
45,255
Latest member
TopCryptoTwitterChannels

Latest Threads

Top