views:

878

answers:

15

I've learned in College that you always have to free your unused Objects but not how you actually do it. For example structuring your code right and so on. Are there any general rules on how to handle pointers in C++?

I'm currently not allowed to use boost. I have to stick to pure c++ because the framework I'm using forbids any use of generics.

+6  A: 

Rules:

  1. Wherever possible, use a smart pointer. Boost has some good ones.
  2. If you can't use a smart pointer, null out your pointer after deleting it.
  3. Never work anywhere that won't let you use rule 1.

If someone disallows rule 1, remember that if you grab someone else's code, change the variable names and delete the copyright notices, no-one will ever notice. Unless it's a school project, where they actually check for that kind of shenanigans with quite sophisticated tools. See also, this question.

Josh
A: 
  • When you have to use manage memory manually, make sure you call delete in the same scope/function/class/module, which ever applies first, e.g.:
  • Let the caller of a function allocate the memory that is filled by it, do not return new'ed pointers.
  • Always call delete in the same exe/dll as you called new in, because otherwise you may have problems with heap corruptions (different incompatible runtime libraries).
Timbo
+10  A: 

I have worked with the embedded Symbian OS, which had an excellent system in place for this, based entirely on developer conventions.

  1. Only one object will ever own a pointer. By default this is the creator.
  2. Ownership can be passed on. To indicate passing of ownership, the object is passed as a pointer in the method signature (e.g. void Foo(Bar *zonk);).
  3. The owner will decide when to delete the object.
  4. To pass an object to a method just for use, the object is passed as a reference in the method signature (e.g. void Foo(Bat &zonk);).
  5. Non-owner classes may store references (never pointers) to objects they are given only when they can be certain that the owner will not destroy it during use.

Basically, if a class simply uses something, it uses a reference. If a class owns something, it uses a pointer.

This worked beautifully and was a pleasure to use. Memory issues were very rare.

Sander
I would use std::auto_ptr<> to show that owner ship is being transferred (rather than RAW pointer). Presumably you are already storing in a smart pointer, if sole ownership is expected std::auto_ptr<> is perfect.
Martin York
The OP said he couldn't use generics, which presumably rules out any smart pointer type.
Sam Stokes
+2  A: 

In the general case (resource management, where resource is not necessarily memory), you need to be familiar with the RAII pattern. This is one of the most important pieces of information for C++ developers.

On Freund
A: 

I have to stick to pure c++ because the framework I'm using forbids any use of generics.

There's certainly a rationale behind this decision? I'd be interested to hear it because off the top of my head I can't think of any reason to forbid templates completely.

Konrad Rudolph
A: 

I have to stick to pure c++ because the framework I'm using forbids any use of generics.

There's certainly a rationale behind this decision? I'd be interested to hear it because off the top of my head I can't think of any reason to forbid templates completely.

The official answer to this is:

They are not portable to different operating systems, especially in the way they are supported by compilers and link-editors.

Alexander Stolz
And is your product available in multiple platforms? That's ridiculous. I hope you don't work there any more.
jmucchiello
A: 

you could derive everything from some base class that implement smart pointer like functionality (using ref()/unref() methods and a counter.

All points highlighted by @Timbo are important when designing that base class.

mkrus
A: 

They are not portable to different operating systems, especially in the way they are supported by compilers and link-editors.

Wow, that's an interesting claim. And yes, the link-editing part is right but that's true for all of C++. ;-)

Konrad Rudolph
+1  A: 

G'day,

I'd suggest reading the relevant sections of "Effective C++" by Scott Meyers. Easy to read and he covers some interesting gotchas to trap the unwary.

I'm also intrigued by the lack of templates. So no STL or Boost. Wow.

BTW Getting people to agree on conventions is an excellent idea. As is getting everyone to agree on conventions for OOD. BTW The latest edition of Effective C++ doesn't have the excellent chapter about OOD conventions that the first edition had which is a pity, e.g. conventions such as public virtual inheritance always models an "isa" relationship.

Rob

Rob Wells
+1  A: 

I would add another rule here:

  • Don't new/delete an object when an automatic object will do just fine.

We have found that programmers who are new to C++, or programmers coming over from languages like Java, seem to learn about new and then obsessively use it whenever they want to create any object, regardless of the context. This is especially pernicious when an object is created locally within a function purely to do something useful. Using new in this way can be detrimental to performance and can make it all too easy to introduce silly memory leaks when the corresponding delete is forgotten. Yes, smart pointers can help with the latter but it won't solve the performance issues (assuming that new/delete or an equivalent is used behind the scenes). Interestingly (well, maybe), we have found that delete often tends to be more expensive than new when using Visual C++.

Some of this confusion also comes from the fact that functions they call might take pointers, or even smart pointers, as arguments (when references would perhaps be better/clearer). This makes them think that they need to "create" a pointer (a lot of people seem to think that this is what new does) to be able to pass a pointer to a function. Clearly, this requires some rules about how APIs are written to make calling conventions as unambiguous as possible, which are reinforced with clear comments supplied with the function prototype.

Nick
A: 

The official answer to this is:

It's wrong. I mean, C++ as a whole is not compatible across different OSes (most platforms have a clear and well-defined C ABI, not so C++, especially when you get into details like virtual inheritance representations and so on). Templates don't change anything on that front; instantiated templates are just classes with funny names.

Compiler support was a pressing concern a few years ago, but these days there's no excuse not to be using g++ 4.x or VC++ 2005/2008, both of which are more than good enough for any normal usage.

DrPizza
+1  A: 

In general, avoid allocating from the heap unless you have to. If you have to, use reference counting for objects that are long-lived and need to be shared between diverse parts of your code.

Sometimes you need to allocate objects dynamically, but they will only be used within a certain span of time. For example, in a previous project I needed to create a complex in-memory representation of a database schema -- basically a complex cyclic graph of objects. However, the graph was only needed for the duration of a database connection, after which all the nodes could be freed in one shot. In this kind of scenario, a good pattern to use is something I call the "local GC idiom." I'm not sure if it has an "official" name, as it's something I've only seen in my own code, and in Cocoa (see NSAutoreleasePool in Apple's Cocoa reference).

In a nutshell, you create a "collector" object that keeps pointers to the temporary objects that you allocate using new. It is usually tied to some scope in your program, either a static scope (e.g. -- as a stack-allocated object that implements the RAII idiom) or a dynamic one (e.g. -- tied to the lifetime of a database connection, as in my previous project). When the "collector" object is freed, its destructor frees all of the objects that it points to.

Also, like DrPizza I think the restriction to not use templates is too harsh. However, having done a lot of development on ancient versions of Solaris, AIX, and HP-UX (just recently - yes, these platforms are still alive in the Fortune 50), I can tell you that if you really care about portability, you should use templates as little as possible. Using them for containers and smart pointers ought to be ok, though (it worked for me). Without templates the technique I described is more painful to implement. It would require that all objects managed by the "collector" derive from a common base class.

Colonel Kernel
A: 


Compiler support was a pressing concern a few years ago, but these days there's no excuse not to be using g++ 4.x or VC++ 2005/2008, both of which are more than good enough for any normal usage.


I'm using a compiler which is a customized version of the VS.NET 2001 compiler, so theres my excuse..

And yes it sucks..

Alexander Stolz
A: 

Always test to make sure your pointer is not null before using it (that includes free'ing it):

Bar* pObject = new Bar();
if(pObject)  // The following code block won't run if the pointer is null
...

You don't have to do this, if the runtime system cannot allocate enough memory for your new Bar object std::bad_alloc will be thrown (http://www.parashift.com/c++-faq-lite/freestore-mgmt.html#faq-16.6).

WMR
A: 

Picking up from Nice "This makes them think that they need to "create" a pointer (a lot of people seem to think that this is what new does) to be able to pass a pointer to a function.)"

Maybe, but I think its because most universities teach Java first. In Java you have to "new" everything and it's hard to break the habit, especially when the two languages look so similar, but are in fact completely different. For real fun, try working with both at the same time!

Chris Huang-Leaver