cpp-referencias

References in C++

  • 5 min

In C++, a reference is an alias that acts as an alternative name for an existing variable.

Once a reference is established, it is permanently bound to the original variable (meaning it cannot point to any other variable).

Any operation performed through the reference is performed directly on the original variable.

Characteristics of references:

  • Constant alias: A reference cannot be made to point to another variable after its initialization.
  • Does not occupy additional memory: Internally, the reference adds no memory overhead; it is simply another way to access the same address.
  • Requires initialization: Unlike pointers, a reference must be initialized at the moment of its declaration.

Do not confuse a reference with the address-of operator &. Both use the & symbol, but they are distinct concepts (though somewhat related).

Defining a Reference

The basic syntax for declaring a reference is:

type &referenceName = variable;
Copied!
  • type is the data type it points to.
  • referenceName is the name of the reference.

Let’s see it with an example,

int a = 10;
int &ref = a; // 'ref' is a reference to 'a'

ref += 5; // Modifies 'a' through 'ref'
std::cout << "Value of a: " << a << std::endl; // Output: Value of a: 15
Copied!

In this example, any change to ref directly affects a, because ref is simply another name for a.

Common Uses of References

References are useful in several scenarios, from performance optimization to implementing advanced features.

Passing Arguments by Reference

In C++, references are frequently used to pass arguments to functions without copying their content.

#include <iostream>
void Increment(int &number) {
    number++;
}

int main() {
    int value = 5;
    Increment(value);
    std::cout << "Incremented value: " << value << std::endl; // Output: 6

    return 0;
}
Copied!

Returning References from Functions

A function can return a reference to allow direct modification of the returned value.

#include <iostream>
int& GetElement(int arr[], int index) {
    return arr[index];
}

int main() {
    int numbers[] = {1, 2, 3, 4, 5};
    GetElement(numbers, 2) = 10; // Modifies the third element

    std::cout << "Modified element: " << numbers[2] << std::endl; // Output: 10

    return 0;
}
Copied!

This usage must be handled with care, as returning references to local variables can lead to undefined behavior.

Constant References

When we don’t want a reference to allow modification of the original variable, we can declare it as constant.

#include <iostream>
void Display(const int &value) {
    std::cout << "Value: " << value << std::endl;
}

int main() {
    int x = 42;
    Display(x); // Output: Value: 42
    return 0;
}
Copied!

Use with Classes and Operator Overloading

In classes, references are useful for implementing methods that return the object itself (method chaining) or for overloading operators.

#include <iostream>
class Counter {
private:
    int value;
public:
    Counter() : value(0) {}

    Counter& operator++() { // Overloading the prefix ++ operator
        value++;
        return *this;
    }

    void Display() const {
        std::cout << "Value: " << value << std::endl;
    }
};

int main() {
    Counter c;
    ++c; // Increments using a reference
    c.Display(); // Output: Value: 1

    return 0;
}
Copied!

Comparison: References and Pointers

References were introduced as an alternative to pointers; references are safer and more intuitive in many cases.

Although both references and pointers allow us to manipulate objects indirectly, they have important differences:

CharacteristicReferencePointer
Syntaxint &ref = variable;int *ptr = &variable;
Main UseAccess to existing variablesMemory manipulation.
ReassignmentCannot be reassignedCan be reassigned
Must be initializedYesNot necessarily
Null valuesCannot be null.Can be null.
NotationSimpler and more direct.More complex

Let’s see it in an example

#include <iostream>
void ModifyByReference(int &ref) {
    ref = 20;
}

void ModifyByPointer(int *ptr) {
    *ptr = 30;
}

int main() {
    int a = 10;

    ModifyByReference(a);
    std::cout << "By reference: " << a << std::endl; // Output: 20

    ModifyByPointer(&a);
    std::cout << "By pointer: " << a << std::endl; // Output: 30

    return 0;
}
Copied!

In this example,

  • Both references and pointers can modify the original value.
  • The syntax with references is cleaner and less error-prone.

As a general rule, we should prefer to use references whenever possible. Only in cases where it’s not possible should we use pointers (preferably smart pointers).