Chris Gregory

Home Blog LinkedIn Github

Java to C++ - const and references

9/23/2015

As we saw in the last post, Java to C++ - Destructors, giving a string to the getline function will modify it to be the current line buffered by the stream. This is really cool, but it is dangerous because the side effects, or places where variables change, can be obscured.

C++ helps to ease this pain by allowing you to declare a variable as const. This allows you to only use functions on it that don't modify the state of the object, or read public variables from the object. Passing a variable by constant reference is a really cool feature that allows a function to look at the contents but not modify them.

const is very similar to the final keyword, but much more powerful. Every type in Java is a pointer, meaning that it is really just a value that tells you where to look for the actual value. The final keyword just tells the compiler that you can't change the address that the pointer points to, but the const keyword does that too!

An ampersand after a type but before the name of a variable tells the compiler that it is a reference. Let's look at an example.

#include <assert.h>
#include <string>

int main() {
    std::string str = "Hello World!";
    const std::string  immutable_copy      = str;
    const std::string& immutable_reference = str;

    str = "Different string";

    // compile errors, can't change state of const object:
    // immutable_reference = "";
    // immutable_copy = "";

    assert(immutable_copy == "Hello World!");
    assert(immutable_reference == "Different string");

// str and immutable_copy are deleted at this closing brace.
// immutable_reference doesn't need to be deleted as it isn't a value type.
}

As you can see, the reference maintains shares the same memory as the value, str. References are just like final objects in Java. The key difference between references and pointers are that references cannot be null as they have to point to a memory location when they are declared. This gives them incredible power as parameters because null checks are redundant.

You can see that the .equals() method isn't used to compare objects because you can just overload the == operator. Don't worry, you can still compare memory addresses, but we'll get to that later. Operator overloading is a very powerful feature of C++ that isn't in Java. It allows you to treat classes as builtin types, which I'll post later about.

The main reason references are so popular is that they can make a huge improvement over value types for arguements to a method. A value type is one that is not a pointer or reference. They aren't very popular as they have to be cloned when used as an argument. Let's look at an example of const references used as function inputs. These are a popular subgenre because they ensure that there won't be a change to the object passed in while maintaining high performance.

#include <string>

std::string concat(std::string a, std::string b) {
    return a + b;
}

The problem with this code is that the strings are passed by value but then not modified, making that operation unnecessary and a waste of resources. To get a nice performance boost, all we have to change is the declaration of the function!

std::string concat(const std::string& a, const std::string& b) {
    return a + b;
}

You might think that returning a reference to the string created by the + operator would be a good idea, but that would enact undefined behavior. This is because the result of the + operator is deleted at the closing brace, but with a value type the compiler will optimize the unnecessary copy and immediate deletion and just return the value. A general rule of thumb is to never return a reference if it points to a variable declared during the scope of the function.