When I have to store primitive data, I prefer
array. When I need to use an object of a class, I prefer to create the
object on the stack and pass it by reference; no
Most C++ developers would agree with you on all these points. Proper containers are safer than primitive arrays, and if you can avoid allocating something on the heap then you should.
There are some instances, however, when you do need pointers. If you can avoid them in your code, then that's great, but they become more difficult to avoid in larger projects.
This is a fancy name for a fairly simple concept. The scope of a function is the body of that function together with all the bodies of functions that it calls, the bodies of functions that they call, and so on. If I declare a local variable in a function, then I know that it will stay alive until that function exits. So I can freely pass it (by reference) to any other functions within that function, and know that I'm doing something safe.
But what if I want to create an object inside my function, store it somewhere and have it persist after the function has exited? If we were writing Java, we would not think twice about writing something like this (for example, in a factory class).
public BigObject createBigObject()
BigObject bigObject = new BigObject(param1, param2);
createBigObject() has run, we want the variable
bigObject still to be alive. Since Java is a garbage-collected language, this is fine.
In C++, if we tried to write
BigObject& createBigObject() const
BigObject bigObject(param1, param2);
then the behaviour of the program is undefined. At best, your compiler will warn you that you are returning a reference to a local variable. If you try to run the code, you might find that it works some times, and crashes at others, since the lifetime of
bigObject ends when
createBigObject returns. You need to use dynamic allocation, either with
new or with a smart pointer. E.g.:
BigObject* createBigObject() const
BigObject* const bigObject = new BigObject(param1, param2);
If you wanted to, you could
return *bigObject instead and return a
References in C++ are immutable: they have to be initialized to refer to some other object when they are created and cannot then be pointed towards some other object. In that respect, a
T& is a bit like a
T* const (and a
T const& is a bit like a
T const* const). Now suppose you are setting up a class and you want to hold a reference to some other object as a member variable in that class. If the other object is all set up when you construct the class then this is fine - you can initialized the reference straight away - but if you have to set things up later in an initialization step, then you'll need to use a pointer so that you can point it at the object only once it's been created.
Polymorphism (sort of)
If you've programmed in Java, you've probably made a lot of use of
interfaces. In C++, if you want to do the same thing, you normally need to use pointers (sort of - sometimes you want to use templates instead). In Java, we can write
List<String> list = new ArrayList<String>();
to indicate that we only care that
list is some sort of list - the fact that it is an
ArrayList is an implementation detail. In C++, writing
List<std::string> list = ArrayList<std::string>();
ArrayList existed as types in your code) would try to construct a
List and then set it equal to the
ArrayList, which would be a compile error if
List had pure virtual members, since then it would be impossible to create
list would have to be either a
List& reference or a
Now sometimes you can use references to do polymorphism (as you could in this example), but in such cases you're usually better off doing compile-time polymorphism using templates. In C++ we usually use runtime polymorphism when we want to store the polymorphic objects in some container (e.g., a
std::vector). And this comes back to the previous point: it's impossible to just store a reference on the fly, since references must be initialized when they are created. So we have to store them as pointers.