Language: EN

cpp-referencias

References in C++

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

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

Any operation performed through the reference is done directly to 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 time of its declaration.

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

Definition of a reference

The basic syntax to declare a reference is:

type &referenceName = variable;
  • type is the data type being pointed 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

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 various scenarios, from performance optimization to implementing advanced features.

Pass 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;
}

Return 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;
}

This usage must be handled carefully, as returning references to local variables can cause undefined behavior.

Constant references

When we do not want a reference to allow modifying the original variable, we can declare it as constant.

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

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

Usage 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 Show() const {
        std::cout << "Value: " << value << std::endl;
    }
};

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

    return 0;
}

Comparison of 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:

FeatureReferencePointer
Syntaxint &ref = variable;int *ptr = &variable;
Main useAccess to existing variablesMemory manipulation.
ReassignmentCannot be reassignedCan be reassigned
Must be initializedYesNot necessarily
NullabilityCannot 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;
}

In this example,

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

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