Language: EN

cpp-referencias

References in C++

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

Once a reference is established, it is permanently bound to the original variable (that is, 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 point to another variable after its initialization.
  • Does not occupy additional memory: Internally, the reference does not add 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 distinct concepts (though with some relation)

Definition of a reference

The basic syntax to declare a reference is:

type &referenceName = variable;
  • type is the type of data 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 the implementation of advanced features.

Passing arguments by reference

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

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

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

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

Constant references

When we do not 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;
}

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

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

    return 0;
}

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
NullsCannot 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 advice, we should prefer using references whenever possible. Only in cases where it is not feasible, should we use pointers (if possible, smart pointers)