views:

727

answers:

8

why in C++, for objects A,B

//interface, case #1
class A {

B bb;
}

A::A()
{ //constructor
bb = B();
}


//interface, case #2
class A {

B *bb;
}

A::A()
{ //constructor
bb = new B();
}

Why case #2 work but not #1??

Edit: I got it now. But for case #1, if an instance of A is freed, will its bb also be automatically freed? Case #2 you have to explicitly call bb = NULL right?

+2  A: 

In C++, if you declare an object, you don't need to initialize it. The constructor is called upon declaration.

SurDin
+2  A: 

Because at compile-time, the amount of memory needed for creating a B object is not known. Therefore, it is necessary to use a pointer to the object so that the memory can be allocated dynamically (when you call new).

Steven Oxley
+1  A: 

The code shown is way too incomplete. Unless you show the definition of B, I am afraid nobody can answer. My wild guess would be class B is non-copyiable (e.g. private operator =). With a copyable B class the example you have given should work.

Suma
A: 

The question as for me was how to make

A::A()
{ //constructor
bb = new B();
}

but without new. And I suppose the code in the question is not a real code. Just why 'new' works but simple assignment doesn't.

And my answer is following. If I understood the question in a wrong way or the answer itself is wrong - please let me know.

Change

A::A()
{ //constructor
bb = B();
}

to

A::A():
    bb()
{
   // some logic
}

and to have a consistence in the case with 'new' it is better to implement like

A::A():
    bb( new B() )
{
    // some logic
}

In these both cases when 'some logic' will start its execution you can be sure that object bb either initialized or exception will be thrown.

To make your case compilable B should has implemented assign operator.

To your edit: Your guess about case 1 is correct. in case 2 you have to call delete bb in the class destructor.

Please leave a message of the '-1' reason. I am really confused.

Mykola Golubyev
what does the bb() add to case#1? it seems pointless to me, it doesn't anything that wasn't previously there.
hasen j
@hasen j: What is the question? :bb() is the constructor of BB call in the A initialization list.
Mykola Golubyev
so? it doesn't add anything to case 1. bb was already defined as a value and so its default constructor will be called anyway even if you don't explicitly put it in the initialization list.
hasen j
@hasen j: In this simple case it is. But in case of integer types or pointers or in case of not default constructor - it is the most correct way.
Mykola Golubyev
@hasen j: It is a good practice to put objects in the initialization list event they are have default constructor. The question was about correct correspondence to the "new B()" and the answer shows it.
Mykola Golubyev
Why do you believe it is good practice to have unnecessary empty initialisers? It's just noise that doesn't do anything.
Pete Kirkham
Ok. One can think it is a noise. But in the C++ world where you can be not aware of actual type it is not. Anyway the answer to the question about how to "new BB" if it is not a pointer is correct. No?
Mykola Golubyev
It is almost always better to let the complier do things automatically unless you want the behaviour to differ from the default. The default behaviour if you don't have the initialiser is the same as an empty initialiser. Where possible, prefer less code.
Pete Kirkham
@Pete: Are you sure that for 'int' variable default is the same as an empty?
Mykola Golubyev
Specifying an empty member initializer for member variables that have a default constructor makes it easier to compare the list of member initializers to the class declaration, in order to ensure that all of the member variables are initialized by the constructor.
bk1e
Also, for integral/pointer types, specifying an empty member initializer will zero-initialize the member variable, but not specifying a member initializer will leave the member uninitialized. Section 12.6.2.4 of the C++ spec describes this behavior.
bk1e
If you want to zero initialise an int or pointer, then "you want the behaviour to differ from the default" so require the initialiser, like I said above. It's very rare to see no-argument initialisers for ints or pointers in real code - if you do something, be explicit.
Pete Kirkham
@Pete: It is a matter of taste. It doesn't mean that the answer is wrong, does it?
Mykola Golubyev
+1  A: 

It looks like your "B" class hides operator=. Otherwise, what you wrote should compile.

However, it isn't necessary. This should work:

A::A() : bb()
{ //constructor
}

However, since you're calling the default constructor, there's no need to explicitly state it. This could just as easily be:

A::A() // bb is automatically constructed, since it's a member variable and not a pointer.
{ 
}

Reed Copsey
A: 

I think that the case A must work, because the compiler provides a copy constructor if no one was defined.

xgoan
+4  A: 

Both have syntax errors so 'does not work' isn't specific to one or the other. You also haven't declared the A::A constructor, so you will get a semantic error when you attempt to define it.

If the full declaration of B is available before the declaration of class A, then it will compile:

class B {};

//interface
class A {
    A();

    B bb;
};

A::A()
{ //constructor
    bb = B();
}

In the bb=B() line, bb has already been constructed, as it is a member of A. You are then constructing another B, then copying that B's values to bb. That's a bit of a waste. If you want to pass arguments to bb's constructor, use an initialiser.

If, on the other hand, you don't want to put the definition of B before the definition of A, and only have a forward reference to it, the compiler can't work out how big the B object it is. It needs to know how big a B is to determine how much space to allow for bb in A. So it won't be able to compile A without B.

In your second case, you instead use a pointer to a B. Pointers are the same size whatever they point at. That way the complier can work out how big A is, and only needs the full declaration of B before it actually uses one, for example by calling B's constructor in the new B() expression.

Pete Kirkham
+1  A: 

If the definition (not the declaration) of class B appears before the declaration of class A, then your case#1 should compile fine.

As Oxley pointed out, the reason for that is that C++ needs to know how much memory is needed for the B object, and it cannot do that without the definition of B.

However, if you use a pointer, (as in case#2), the amount of memory needed is known, because all pointers occupy the same amount of memory (32bit, or 64bit, depending on the system).

So, for case#2, all you need is the declaration of B before the definition of A. (note: declaration, not definition! of course, having the definition also won't hurt, but it's not necessarily needed).

Just to make sure I don't confuse anyone, (hey, maybe I don't have the C++ terminology down)

declaration: declares the existance of a class or a function, but doesn't say a thing about what it is.

e.g.

class A; //forward declaration

class C
{
   A * a;
   // .....
};

definition: actually defines the class or function. Although for a class, it doesn't necessarily define it fully, but it defines all of its members.

class A
{
   int a;
   int b;
   void some_method(); //not defined here, only declared, but it's ok
};
hasen j