Thursday, February 14, 2008

More Pointers and references

I talked a little last time (in Pointers and References Basics) about, well, the basics of pointers and references. I'll go into a bit of detail now about some practical uses of them.

Probably the most useful thing about references/pointers is when you pass them to functions. If you declare a function like so:

void someFunction(SomeClass a, SomeClass b);

If you call that function like so:

SomeClass a;
SomeClass b;

someFunction(a, b);


both a and b are passed by value to the function. This means that the copy constructor is called, and copies of a and b are made which are used inside the function. This is desired behaviour and all, but there is some overhead involved in calling the copy constructors every time, especially in a performance intensive program (also, remember that the destructors will have to be called each time also...).

So, as a solution to this you can do one of two things. You can pass a pointer to the objects into the function, or you can pass a reference to the objects to the function. This will mean changing the way the function is defined. In the case of passing a pointer it means defining:

void someFnByPointer(SomeClass *pA, SomeClass *pB);

And for references it means defining it as:

void someFnByRef(SomeClass &a, SomeClass &b);

However, when calling the functions, there is a difference. To pass by pointer, you either have to create a pointer to the 2 objects being passed in, and pass that in, or pass in the address of the objects. To pass by reference, you call it as normal.

//By pointer
someFnByPointer(&a, &b);
//Or by reference
someFnByRef(a, b);

In my opinion the second way looks more natural, and you don't have to worry about making sure the pointer address is correct etc. Also in the function itself you can still use the '.' notation rather than the '->' notation. So I tend to prefer passing by reference.

Actually, there is another benefit to passing by reference/pointer (from now on, I'm just going to refer to 'passing by reference', even though it is equally applicable to pointers). When passing by reference, the passed object will not get 'sliced' if the reference is to a derived object. For example, if you specify that a base class object must be passed in by value, if you pass in a derived object, the additional functionality in the derived object gets 'sliced' off (as the copy constructor will only take the values from the base class). However, if you pass by reference the copy constructor is never called and you still have the full derived object. This is especially important if there are virtual functions floating about and you want the derived ones to be called.
So if you have a class, SomeDerivedClass that inherits from SomeClass, you can do the following and still have all of the derived class functionality (provided a derived class object is passed in):

void someFunction(SomeClass &a)
{
//if a is SomeDerivedClass object, then whole object
//remains, and the derived functionality is maintained
//so the following is fine:
SomeDerivedClass c = (SomeDerivedClass)a;

}


Finally, one last plug that should be filled regards what you can and can't do with the objects that are passed to a function. The benefit of passing by value is that when you pass an object you can forget about it, safe in the knowledge that it cannot be changed by the function. The same cannot be said if you pass by reference.

SomeClass someObjectA, someObjectB;
someFnByRef(someObjectA, someObjectB);
//The function can do anything it wants to someObjectA
//and someObjectB.

This is clearly undesirable in many cases. someFnByRef() needs to guarantee that the objects passed to it won't change. To do that, just add our old friend const when declaring the function.

void someFnByRef(const SomeClass &a, const SomeClass &b);

So basically you get all of the benefits of passing by reference (i.e. faster, no slicing etc.) with none of the drawbacks (it is still safe, cannot change the passed object). It's just like a late night infomercial!!

(Note: For an excellent discussion on this, and many other useful topics, check out Scott Meyers' 'Effective C++')

--
Lurning Man
--

No comments: