Pointers vs References in C++

  • C++ supports both pointers and references. Internally, they are the same thing (a number that specifies a position in memory), but the language has completely different rules about how you use them. Example instance:
    MyClass i;
    
    Example pointer:
    MyClass* p = &i;
    
    Example reference:
    MyClass& r = i;
    
    A pointer may point to a valid instance, or it may point to nullptr. A reference must always refer to an instance. There is no concept of nullptr for references. A pointer may be initialized or left uninitialized. The compiler will force you to initialize your references.

    (Notice that the "*" or "&" is really part of the variable type, not part of the variable name, so it makes more sense to put the space after the "*" or "&", rather than before it. Since C++ is whitespace tolerant, either will compile.)


  • How to get a member value from an instance:
    int n = i.someMember;
    
    How to get a member value from a pointer to an instance:
    int n = p->someMember;
    
    How to get a member value from a reference to an instance:
    int n = r.someMember;
    
    Note that references are used just like instances. This similarity causes many students to think that a reference is the same as an instance. They are not the same!!! Instances can be very big. References are always small.


  • How to make a function that receives a parameter passed by value:
    void myfunc(MyClass v) {
    
    How to make a function that receives a parameter passed by pointer:
    void myfunc(MyClass* p) {
    
    How to make a function that receives a parameter passed by reference:
    void myfunc(MyClass& r) {
    
    Because pointers use different syntax for accessing members, the compiler will alert you if you forget a "*". However, because references and instances use the same syntax to access members, it is very easy to accidentally forget an "&". The compiler will not tell you about it, and this can have a HUGE impact on the performance of your code. So, be careful not to forget the "&" in your parameter declarations! There are very few cases when you really want to pass a large object by value, so you nearly always want an &. Only small objects (like int, char, double, etc.) are commonly passed by value.


  • You can convert a reference to pointer:
    MyClass* p = &r;
    
    You can convert a pointer to reference:
    MyClass& r = *p;
    


  • Now, here's the doozy. Pay attention! This is the issue that burns many unsuspecting C++ users: Once a reference is initialized, IT CANNOT BE CHANGED ...EVER! The first time you use an "=" to initialize your reference, it sets the reference. The next time (and forever thereafter, when) you use an "=" with the same reference, it changes the contents of that object. It does not change the reference again! You can never change the reference again. It will always reference the same instance until it is destroyed. Example:
    MyClass inst1;
    MyClass inst2;
    
    MyClass& ref1 = inst1; // Make a reference that refers to inst1.
                           //    (This is an inexpensive operation.)
    
    ref1 = inst2; // Copy the contents of inst2 by value into inst1.
                  //   (Probably not what you meant!!!)
    
    Let me repeat: Once an instance is initialized, it cannot be changed to refer to another object!

    (...okay, if you are hacker-type, you may know that it is technically possible to change them by finding the memory location of the reference and manipulating its contents, but that is an abuse of the language. You are not supposed to do that.)


  • How to initialize a pointer member variable:
    class MyClass
    {
    protected:
    	SomeObject* m_pSomeObject;
    
    public:
    	MyClass(SomeObject* p)
    	{
    		m_pSomeObject = p;
    	}
    };
    
    How to initialize a reference member variable:
    class MyClass
    {
    protected:
    	SomeObject& m_someObject;
    
    public:
    	MyClass(SomeObject& r)
    	: m_someObject(r)
    	{
    	}
    };
    
    The initializers are performed even before the constructor is called!


  • How to clean up memory when you use pointers:
    If you allocate an object, you must remember to delete it. Here is an example:
    MyClass* p = new MyClass();
    p->do_some_stuff();
    delete(p);
    
    But, here is a better example:
    MyClass* p = new MyClass();
    unique_ptr hp(p);
    p->do_some_stuff();
    
    Why is the second example better? If the "do_some_stuff" method throws an exception, then the first example will leak memory. (That's bad.) The second example will automatically delete p when hp goes out of scope, even if that happens when an exception is thrown.