How to use pointers correctly in modern C++

How to use pointers correctly in modern C++

Many developers, even after mastering the basics of C++, still feel uncomfortable when it comes to pointers. Some fear them, others overuse them — creating memory leaks and hard-to-debug issues. In modern C++, pointers remain an important part of the language, but the approach to them has changed significantly.

In this article, we’ll break down how to work with pointers correctly today — in 2026 — taking into account the capabilities of C++11, C++17, and C++20.

1. Why pointers are still needed

Pointers allow you to:

  • Work with dynamic memory
  • Pass large objects to functions without copying
  • Implement polymorphism
  • Build data structures (lists, trees, graphs)

However, modern C++ strongly recommends using raw pointers only in very limited cases.

Teal 'C++' text with digital lines on a black background

2. Modern C++ rule: avoid raw pointers whenever possible

Instead of new and delete, almost always prefer:

// Instead of this
int* ptr = new int(42);
delete ptr;

// Better to use
auto value = std::make_unique<int>(42);
// or
std::shared_ptr<int> shared = std::make_shared<int>(42);
  • std::unique_ptr — when the object has a single owner
  • std::shared_ptr — when the object can have multiple owners
  • std::weak_ptr — to break cycles in shared_ptr ownership

3. When raw pointers are still acceptable

Raw pointers (T*) can be used only in these situations:

  • You are working with external APIs (e.g., C libraries)
  • You are writing low-level code or a custom allocator
  • You are implementing a data structure where maximum performance is critical

Even in these cases, it is recommended to use std::span (C++20) instead of raw pointers to arrays.

4. Common mistakes and how to avoid them

  • Never return a pointer to a local variable
  • Never use delete on arrays (always use delete[])
  • Never forget to check for nullptr before dereferencing
  • Never create dangling pointers

Correct modern approach example:

std::span<const int> processData(std::span<const int> data) {
    // Safe work with data without taking ownership
    return data;
}

5. Practical recommendations for 2026

  1. Use std::unique_ptr by default
  2. For array-like data — prefer std::vector or std::span
  3. For observation without ownership — use std::weak_ptr or plain references
  4. Avoid new and delete in regular application code
  5. Always prefer std::make_unique and std::make_shared

Following these simple rules makes your code safer, cleaner, and much easier to maintain in modern C++ projects.

If you’re still writing raw new/delete in 2026 — it’s time to switch to smart pointers and spans. Your future self (and your colleagues) will thank you.

Back to blog