tags:

views:

1639

answers:

5

I'm used to doing Java programming, where you never really have to think about pointers when programming. However, at the moment I'm writing a program in C++. When making classes that have members of other classes, when should I use pointers and when should I not? For example, when would I want to do this:

class Foo {
    Bar b;
}

As opposed to this:

class Foo {
    Bar* b;
}
A: 

Both are OK under different conditions. For example, if you know how to construct b when an object of class Foo is constructed, the first is OK. But if you don't, the only choice is to use the second.

PolyThinker
+21  A: 

Start by avoiding pointers.

Use them when:

  • You want to use the Pimpl idiom, or an abstract factory.
  • The Bar instance is actually managed by some other part of your program, whereas the Foo class just needs to be able to access it.
  • You want to postpone the construction of the Bar object (i.e., you want to create it after constructing Foo).
  • In your business logic, the Bar object may not exist at all; you would use null also in Java. However, check out boost::optional as well.
  • Bar is actually a base class, and you need the instance to be polymorphic.
  • You happen to be using a toolkit that prefers to present GUI widgets as pointers. Examples could include (but are certainly not limited to) wxWidgets and GLUI.

In any of these cases (*), start by using a smart pointer, such as boost::shared_ptr. Otherwise, you are likely to forget to deallocate the memory, sooner or later. Once you know what you are doing, consider case-by-case what pointer type is best.

(*) any case – except, probably, the bullet regarding GUI widgets; in this case, your toolkit would most probably manage the resources for you as well

Pukku
...or in the case you're writing a toolkit (which is often my case). I have never had problems with pointers which smart pointers would solve, so I wouldn't say "USE THEM!" but "it is easiest to use them in most cases."
strager
@strager: True. Softened the remark a bit.
Pukku
maybe i'm alone. but i think especially beginners should (not in real world projects of course) first try to work with raw pointers. i think this is similar to the question "IDE/no IDE". they should learn to know *why* smart pointers are useful and what pitfalls they avoid
Johannes Schaub - litb
before taking and using them to magically avoid some problems others told them about.
Johannes Schaub - litb
@litb: I do agree with your point. I guess the answer is a bit different if you ask about best practices for real-world projects (which is the question I was answering), or learning to understand the fundamental stuff. OTOH, I figured he already knew how to work with raw pointers in the basic case.
Pukku
Starting point should be boost::scoped_ptr, go for shared_ptr only when you need that functionality.
Patrick
@Patrick: True. "Because it is noncopyable, it is safer than shared_ptr or std::auto_ptr for pointers which should not be copied." However, it wouldn't work in the situation described by the second bullet.
Pukku
+1  A: 

In the first example memory for the Bar object will be allocated automatically when you construct the Foo object. In the second case you need to allocate the memory yourself. So you must call:

Foo *foo = new Foo();
foo->b = new Bar();

This may be desirable if the Bar object is large and you don't want to bundle it with the Foo object. It is also desirable when the construction of the Bar object is independent of the creation of Foo object. In this case the b object is "injected" into foo:

Foo *foo = new Foo();
foo->b = b_ptr;

where b_ptr is constructed somewhere else and a pointer is passed to foo.

For smaller objects it is dangerous, as you may forget to allocate the memory.

kgiannakakis
`new` returns a pointer, not a value. Do you mean Foo foo; or Foo foo = Foo(); instead?
strager
I've corrected the code to use dynamic allocations.
kgiannakakis
+10  A: 
class Foo {
    Bar b;
}

b is contained in Foo. If the Foo object ends lifetime, b automatically ends lifetime too. This is what models composition. b above denotes the object itself, not just a pointer to it like in Java. Therefor, if b goes out of scope, the object will end lifetime.

class Foo {
    Bar * b;
}

Here, the object b points to is used by or referenced by the Foo object. If the Foo object ends lifetime, the object b points to may continue to live, depending on circumstances. This can be used to model aggregation and general relationship. The object may be shared with other Foo objects for example.

Pointers are roughly what references are in Java. They can also point to nothing. If a pointer points to nothing, it's a null pointer.

Similar to pointers are references. References in C++ must be initialized and can only point to one (valid) object, for which the reference was initialized. A reference therefor cannot hold value which could mean "nothing" like null in Java.

Johannes Schaub - litb
This may be too late, but in the first paragraph, shouldn't you say "If the `Foo` object ends lifetime, b automatically ends lifetime too." instead of `Bar` in the place of `Foo`.
@user68, thanks. corrected
Johannes Schaub - litb
+1  A: 

You need to do some assembly programming and understand the memory layout well. C is just a crossplatform assembly, unlike Java or other languages. To use it properly, one should understand low level details.

All comments made are valid, but for individuals like you, who jump from high level languages to C, having this sort of experience would be more than beneficial. With proper understanding, you would not ask questions like that anymore.

valenok
Note that the question was about C++, not C.
Pukku
But noobs don't know the difference between C++ and C. It was still a good advice.
toto