There's no such thing as a "simple object". An object is an object is an object.
What matters is the object lifetime: automatic (scoped), dynamic (manual) or static (permanent).
As much as you can, and unless you have a really, really good reason to do otherwise, use objects with automatic lifetime.
If you think about how one object may depend on another, you'll see that if you only ever use automatic objects, then you won't have a problem with dangling references (or "references to local variable"), because the dependent object will have more deeply nested scope than the referenced object.
You should have a very convincing argument why a particular object must have manual lifetime. These situations happen, of course, but they should be abstracted and factored away. For example, most standard library containers do of course require manual management of their elements, but all that is handled by the container, so that the user can just use an container object of automatic lifetime and all is well. Finally, if you decided that you really must manage an object's lifetime manually, then use a single-responsibility resource managing class like shared_ptr
or unique_ptr
to pass a handle to that object around -- the handler classes are now automatic again, and once they go out of scope, they release the managed object for you. You get the best of both worlds: the benefits of manual object allocation and scoped responsibilities.
If you follow these ideas methodically, you should find that you will only very rarely create dynamic objects yourself, and if you do, then there's a single line of code that's relevant (this gives you "locality"); for example, auto p = std::make_shared<T>(a,b,c);
, or std::unique_ptr<T> p(new T(a,b,c));
. A simple grep
for new
on all your source code can easily audit whether all your dynamic allocations are handled responsibly.