C++
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Object-Oriented C++
-
Section 4: Memory Management
-
Section 5: Templates and Generic Programming
-
Section 6: The Standard Template Library
-
Section 7: Modern C++ Features
-
Section 8: Error Handling
-
Section 9: Multithreading
-
Section 10: Operator Overloading and Type Conversion
-
Section 11: Advanced Topics
-
Section 12: Tooling and Build Systems
-
Section 13: Design Patterns in C++
-
Section 14: Interfacing with C and Systems Programming
-
Section 15: Networking and IPC Basics
-
Section 16: Graphics and Game Programming Basics
-
Section 17: The Boost Libraries
-
Section 18: Data Structures and Algorithms in C++
-
Section 19: Practical Projects
-
Section 20: More Concurrency Patterns
-
Section 21: More OOP and Design Practice
-
Section 22: File I/O and Streams
-
Section 23: More Standard Library
-
Section 24: Practice Exercises
-
Section 25: Interview and Algorithm Practice
-
Section 26: Compiler and Language Internals
-
Section 27: GUI and Application Frameworks Overview
-
Section 28: Testing and Quality Practices
-
Section 29: Numerics Library
-
Section 30: Concepts Library (C++20)
-
Section 31: Ranges Library (C++20) In Depth
-
Section 32: More Utility Library
75: std::array for Fixed-Size Arrays
I was working on a small RPG prototype earlier today, and I needed a way to store a character's core attributes—Strength, Dexterity, and Intelligence. Since every character always has exactly these three stats, I didn't need a dynamic list. I just needed a fixed-size container.
The old way: C-style arrays and the decay problem
My first instinct was to just use a plain C-style array. It's the most basic tool we have. I wrote a quick function to print the stats, and this is where I hit the classic C++ annoyance:
int stats[3] = {10, 15, 8};
void printStats(int arr[]) {
// Wait, how big is arr?
// I can't use sizeof(arr) here because it decayed to a pointer.
}
If you've spent any time with C-style arrays, you know exactly what I'm talking about. The moment I pass stats into printStats, it "decays" into a pointer to the first element. The size information is just... gone. To fix this, I'd have to pass the size as a second argument: void printStats(int arr[], size_t size). It's tedious, it's error-prone, and frankly, it feels like we're writing code from 1998.
Switching to std::array
I decided to swap this out for std::array. If you haven't used it yet, think of it as a thin wrapper around a C-style array that actually remembers its own size and behaves like a proper C++ object. Here is how I refactored that snippet:
#include <array>
#include <iostream>
void printStats(const std::array<int, 3>& arr) {
// Now I can actually use .size()!
for (size_t i = 0; i < arr.size(); ++i) {
std::cout < "Stat " < i < ": " < arr[i] < "\n";
}
}
int main() {
std::array<int, 3> stats = {10, 15, 8};
printStats(stats);
return 0;
}
Notice the type: std::array<int, 3>. The size is part of the type itself. This is a huge win for the compiler—it knows exactly how much memory to allocate on the stack, and it can optimize the heck out of it. I'm passing it by reference (const std::array<int, 3>&) because, unlike a pointer, a std::array is a value type. If I passed it without the &, C++ would copy the entire array every time the function was called. For three integers, that's trivial. For 1,000 integers? That's a performance nightmare.
Safety first: .at() vs []
While I was testing this, I wondered what would happen if I tried to access a stat that doesn't exist. I tried accessing index 3 (the fourth element) using the square bracket operator:
std::array<int, 3> stats = {10, 15, 8};
std::cout < stats[3] < "\n"; // Out of bounds!
The program didn't crash immediately; it just printed some garbage value from a random spot in memory. This is "Undefined Behavior," and it's the primary cause of those impossible-to-track bugs that keep you up until 3 AM. If I want the program to actually tell me when I've messed up, I should use the .at() method instead:
try {
std::cout < stats.at(3) < "\n";
} catch (const std::out_of_range& e) {
std::cerr < "Caught an error: " < e.what() < "\n";
}
The .at() method does bounds checking. It's slightly slower than [], but during development, that safety is worth every single nanosecond. I usually use .at() while I'm prototyping and only switch to [] in the final release for hot paths where performance is absolutely critical.
📋 Practical Task
Implementing a 4-Slot Equipment Loadout
Create a program that manages a character's equipment slots. You must use std::array to ensure the character can never have more or fewer than exactly 4 equipment slots.
- Define a
std::arrayof strings with a size of 4 to hold item names (e.g., "Iron Sword", "Leather Shield", etc.). - Initialize the array with four starting items.
- Create a function called
updateEquipmentthat takes the array by reference and two integers: theslotIndexand a newitemName. - Inside
updateEquipment, use the.at()method to update the item. Wrap this call in atry-catchblock to handle cases where an invalid slot index is provided. - In your
mainfunction, callupdateEquipmentonce with a valid index and once with an invalid index (like 5) to demonstrate that your error handling works.
There are no comments for now.