Coming Soon
This lesson is currently being developed
Array indexing and length using enumerators
Access container elements by index using enums.
What to Expect
Comprehensive explanations with practical examples
Interactive coding exercises to practice concepts
Knowledge quiz to test your understanding
Step-by-step guidance for beginners
Development Status
Content is being carefully crafted to provide the best learning experience
Preview
Early Preview Content
This content is still being developed and may change before publication.
16.9 — Array indexing and length using enumerators
In this lesson, you'll learn how to use enumerators to make array indexing more meaningful, safe, and maintainable. Instead of using magic numbers for array indices, you'll discover how enums can make your code self-documenting and less prone to errors.
The problem with magic numbers
When working with arrays, it's common to use numeric indices that represent specific meanings:
#include <vector>
#include <iostream>
int main()
{
// Student information: [name_length, age, grade_count, total_score]
std::vector<int> student = {10, 20, 5, 450};
// ❌ What do these numbers mean?
std::cout << "Name length: " << student[0] << std::endl;
std::cout << "Age: " << student[1] << std::endl;
std::cout << "Grade count: " << student[2] << std::endl;
std::cout << "Total score: " << student[3] << std::endl;
// ❌ Error-prone: easy to mix up indices
double average = static_cast<double>(student[3]) / student[2]; // Which is which?
std::cout << "Average: " << average << std::endl;
return 0;
}
Output:
Name length: 10
Age: 20
Grade count: 5
Total score: 450
Average: 90
This code works, but it's hard to understand and maintain. What if someone adds a new field or reorders the data?
Introduction to enumerators for indexing
Enumerators can solve this problem by providing meaningful names for array indices:
#include <vector>
#include <iostream>
int main()
{
// Define meaningful names for array indices
enum StudentFields
{
NAME_LENGTH = 0,
AGE = 1,
GRADE_COUNT = 2,
TOTAL_SCORE = 3,
STUDENT_FIELDS_COUNT = 4 // Useful for array size
};
// Student information using the enum indices
std::vector<int> student(STUDENT_FIELDS_COUNT);
student[NAME_LENGTH] = 10;
student[AGE] = 20;
student[GRADE_COUNT] = 5;
student[TOTAL_SCORE] = 450;
// ✅ Much clearer and self-documenting
std::cout << "Name length: " << student[NAME_LENGTH] << std::endl;
std::cout << "Age: " << student[AGE] << std::endl;
std::cout << "Grade count: " << student[GRADE_COUNT] << std::endl;
std::cout << "Total score: " << student[TOTAL_SCORE] << std::endl;
// ✅ Clear which values are being used
double average = static_cast<double>(student[TOTAL_SCORE]) / student[GRADE_COUNT];
std::cout << "Average: " << average << std::endl;
return 0;
}
Output:
Name length: 10
Age: 20
Grade count: 5
Total score: 450
Average: 90
Practical examples
Example 1: Color representation (RGB)
#include <vector>
#include <iostream>
int main()
{
// RGB color representation
enum ColorComponents
{
RED = 0,
GREEN = 1,
BLUE = 2,
COLOR_COMPONENT_COUNT = 3
};
// Create some colors
std::vector<int> white(COLOR_COMPONENT_COUNT);
white[RED] = 255;
white[GREEN] = 255;
white[BLUE] = 255;
std::vector<int> purple(COLOR_COMPONENT_COUNT);
purple[RED] = 128;
purple[GREEN] = 0;
purple[BLUE] = 128;
// Function to display color
auto displayColor = [](const std::vector<int>& color, const std::string& name)
{
std::cout << name << ": RGB("
<< color[RED] << ", "
<< color[GREEN] << ", "
<< color[BLUE] << ")" << std::endl;
};
displayColor(white, "White");
displayColor(purple, "Purple");
// Calculate color intensity
int whiteIntensity = white[RED] + white[GREEN] + white[BLUE];
int purpleIntensity = purple[RED] + purple[GREEN] + purple[BLUE];
std::cout << "White intensity: " << whiteIntensity << std::endl;
std::cout << "Purple intensity: " << purpleIntensity << std::endl;
return 0;
}
Output:
White: RGB(255, 255, 255)
Purple: RGB(128, 0, 128)
White intensity: 765
Purple intensity: 256
Example 2: Financial data
#include <vector>
#include <iostream>
int main()
{
// Monthly financial data
enum FinancialData
{
INCOME = 0,
EXPENSES = 1,
SAVINGS = 2,
TAXES = 3,
FINANCIAL_DATA_COUNT = 4
};
// Create financial records for 3 months
std::vector<std::vector<double>> monthlyData = {
{5000.0, 3500.0, 1000.0, 500.0}, // January
{5200.0, 3600.0, 1100.0, 520.0}, // February
{4800.0, 3400.0, 900.0, 480.0} // March
};
std::vector<std::string> months = {"January", "February", "March"};
std::cout << "Financial Summary:" << std::endl;
std::cout << "==================" << std::endl;
double totalIncome = 0, totalExpenses = 0, totalSavings = 0, totalTaxes = 0;
for (std::size_t month = 0; month < monthlyData.size(); ++month)
{
const auto& data = monthlyData[month];
std::cout << months[month] << ":" << std::endl;
std::cout << " Income: $" << data[INCOME] << std::endl;
std::cout << " Expenses: $" << data[EXPENSES] << std::endl;
std::cout << " Savings: $" << data[SAVINGS] << std::endl;
std::cout << " Taxes: $" << data[TAXES] << std::endl;
// Calculate net for the month
double net = data[INCOME] - data[EXPENSES] - data[SAVINGS] - data[TAXES];
std::cout << " Net: $" << net << std::endl;
std::cout << std::endl;
// Add to totals
totalIncome += data[INCOME];
totalExpenses += data[EXPENSES];
totalSavings += data[SAVINGS];
totalTaxes += data[TAXES];
}
std::cout << "Quarterly Totals:" << std::endl;
std::cout << "Total Income: $" << totalIncome << std::endl;
std::cout << "Total Expenses: $" << totalExpenses << std::endl;
std::cout << "Total Savings: $" << totalSavings << std::endl;
std::cout << "Total Taxes: $" << totalTaxes << std::endl;
return 0;
}
Output:
Financial Summary:
==================
January:
Income: $5000
Expenses: $3500
Savings: $1000
Taxes: $500
Net: $0
February:
Income: $5200
Expenses: $3600
Savings: $1100
Taxes: $520
Net: $-20
March:
Income: $4800
Expenses: $3400
Savings: $900
Taxes: $480
Net: $20
Quarterly Totals:
Total Income: $15000
Total Expenses: $10500
Total Savings: $3000
Total Taxes: $1500
Using enumerators with 2D arrays
Enumerators are particularly useful with 2D arrays or matrices:
#include <vector>
#include <iostream>
int main()
{
// Game board positions
enum BoardRows
{
TOP_ROW = 0,
MIDDLE_ROW = 1,
BOTTOM_ROW = 2,
BOARD_HEIGHT = 3
};
enum BoardCols
{
LEFT_COL = 0,
CENTER_COL = 1,
RIGHT_COL = 2,
BOARD_WIDTH = 3
};
// Create a tic-tac-toe board
std::vector<std::vector<char>> board(BOARD_HEIGHT, std::vector<char>(BOARD_WIDTH, ' '));
// Place some moves
board[TOP_ROW][LEFT_COL] = 'X';
board[TOP_ROW][CENTER_COL] = 'O';
board[MIDDLE_ROW][MIDDLE_ROW] = 'X'; // Center position
board[BOTTOM_ROW][RIGHT_COL] = 'O';
// Display the board
std::cout << "Tic-Tac-Toe Board:" << std::endl;
std::cout << "==================" << std::endl;
for (int row = 0; row < BOARD_HEIGHT; ++row)
{
for (int col = 0; col < BOARD_WIDTH; ++col)
{
std::cout << " " << board[row][col] << " ";
if (col < BOARD_WIDTH - 1) std::cout << "|";
}
std::cout << std::endl;
if (row < BOARD_HEIGHT - 1)
{
std::cout << "---|---|---" << std::endl;
}
}
// Check for winning conditions using meaningful names
bool topRowWin = (board[TOP_ROW][LEFT_COL] == board[TOP_ROW][CENTER_COL] &&
board[TOP_ROW][CENTER_COL] == board[TOP_ROW][RIGHT_COL] &&
board[TOP_ROW][LEFT_COL] != ' ');
bool diagonalWin = (board[TOP_ROW][LEFT_COL] == board[MIDDLE_ROW][CENTER_COL] &&
board[MIDDLE_ROW][CENTER_COL] == board[BOTTOM_ROW][RIGHT_COL] &&
board[TOP_ROW][LEFT_COL] != ' ');
if (topRowWin)
{
std::cout << "Top row wins!" << std::endl;
}
else if (diagonalWin)
{
std::cout << "Diagonal wins!" << std::endl;
}
else
{
std::cout << "Game continues..." << std::endl;
}
return 0;
}
Output:
Tic-Tac-Toe Board:
==================
X | O |
---|---|---
| X |
---|---|---
| | O
Game continues...
Scoped enumerations (enum class)
For better type safety, use enum class
instead of plain enum
:
#include <vector>
#include <iostream>
int main()
{
// Scoped enumeration - better type safety
enum class Coordinate
{
X = 0,
Y = 1,
Z = 2,
DIMENSION_COUNT = 3
};
// Need to cast to use as array index
std::vector<double> position(static_cast<int>(Coordinate::DIMENSION_COUNT));
position[static_cast<int>(Coordinate::X)] = 10.5;
position[static_cast<int>(Coordinate::Y)] = 20.3;
position[static_cast<int>(Coordinate::Z)] = -5.7;
std::cout << "Position: ("
<< position[static_cast<int>(Coordinate::X)] << ", "
<< position[static_cast<int>(Coordinate::Y)] << ", "
<< position[static_cast<int>(Coordinate::Z)] << ")" << std::endl;
// Helper function to make casting easier
auto idx = [](Coordinate coord) { return static_cast<int>(coord); };
std::cout << "Using helper: ("
<< position[idx(Coordinate::X)] << ", "
<< position[idx(Coordinate::Y)] << ", "
<< position[idx(Coordinate::Z)] << ")" << std::endl;
return 0;
}
Output:
Position: (10.5, 20.3, -5.7)
Using helper: (10.5, 20.3, -5.7)
Creating utility functions with enumerators
#include <vector>
#include <iostream>
#include <string>
// Statistics array indices
enum class StatIndex
{
COUNT = 0,
SUM = 1,
MIN = 2,
MAX = 3,
STAT_COUNT = 4
};
class StatisticsCalculator
{
private:
static int idx(StatIndex stat)
{
return static_cast<int>(stat);
}
public:
static std::vector<double> calculateStats(const std::vector<int>& data)
{
std::vector<double> stats(idx(StatIndex::STAT_COUNT));
if (data.empty())
{
return stats; // All zeros
}
stats[idx(StatIndex::COUNT)] = data.size();
stats[idx(StatIndex::SUM)] = 0;
stats[idx(StatIndex::MIN)] = data[0];
stats[idx(StatIndex::MAX)] = data[0];
for (int value : data)
{
stats[idx(StatIndex::SUM)] += value;
if (value < stats[idx(StatIndex::MIN)])
stats[idx(StatIndex::MIN)] = value;
if (value > stats[idx(StatIndex::MAX)])
stats[idx(StatIndex::MAX)] = value;
}
return stats;
}
static void printStats(const std::vector<double>& stats)
{
std::cout << "Statistics:" << std::endl;
std::cout << " Count: " << stats[idx(StatIndex::COUNT)] << std::endl;
std::cout << " Sum: " << stats[idx(StatIndex::SUM)] << std::endl;
std::cout << " Min: " << stats[idx(StatIndex::MIN)] << std::endl;
std::cout << " Max: " << stats[idx(StatIndex::MAX)] << std::endl;
std::cout << " Average: " << stats[idx(StatIndex::SUM)] / stats[idx(StatIndex::COUNT)] << std::endl;
}
};
int main()
{
std::vector<int> testScores = {85, 92, 78, 90, 88, 95, 82, 87, 93, 89};
auto stats = StatisticsCalculator::calculateStats(testScores);
std::cout << "Test Scores: ";
for (int score : testScores)
{
std::cout << score << " ";
}
std::cout << std::endl << std::endl;
StatisticsCalculator::printStats(stats);
return 0;
}
Output:
Test Scores: 85 92 78 90 88 95 82 87 93 89
Statistics:
Count: 10
Sum: 879
Min: 78
Max: 95
Average: 87.9
Best practices
1. Use meaningful names
// ✅ Good: Descriptive enum values
enum class PlayerStats
{
HEALTH = 0,
MANA = 1,
EXPERIENCE = 2,
STAT_COUNT = 3
};
// ❌ Bad: Generic or abbreviated names
enum Stats
{
S1 = 0,
S2 = 1,
S3 = 2
};
2. Include a count value
// ✅ Good: Include count for array sizing
enum class WeekDays
{
MONDAY = 0,
TUESDAY = 1,
WEDNESDAY = 2,
THURSDAY = 3,
FRIDAY = 4,
SATURDAY = 5,
SUNDAY = 6,
DAY_COUNT = 7 // Useful for array sizing
};
std::vector<int> dailySteps(static_cast<int>(WeekDays::DAY_COUNT));
3. Consider using enum class for type safety
// ✅ Better: Scoped enumeration prevents accidental misuse
enum class Direction
{
NORTH = 0,
SOUTH = 1,
EAST = 2,
WEST = 3,
DIRECTION_COUNT = 4
};
// Prevents accidental mixing with other enums
// Direction dir = Color::RED; // Compile error with enum class
4. Create helper functions for casting
// ✅ Helper function reduces casting noise
template<typename E>
constexpr auto idx(E enumerator) noexcept
{
return static_cast<std::underlying_type_t<E>>(enumerator);
}
// Usage:
std::vector<int> data(idx(MyEnum::COUNT));
data[idx(MyEnum::FIRST_ITEM)] = 42;
Common pitfalls to avoid
1. Don't assume enum values
// ❌ Bad: Assuming enum values start at 0 and increment by 1
enum BadExample
{
FIRST = 10, // Not 0!
SECOND = 20, // Not 1!
THIRD = 30 // Not 2!
};
// ✅ Good: Explicitly set values for array indexing
enum GoodExample
{
FIRST = 0,
SECOND = 1,
THIRD = 2,
COUNT = 3
};
2. Don't forget bounds checking
template<typename E>
bool isValidIndex(E enumValue, std::size_t arraySize)
{
auto index = static_cast<std::size_t>(enumValue);
return index < arraySize;
}
// Use before accessing:
if (isValidIndex(MyEnum::SOME_VALUE, myArray.size()))
{
// Safe to access myArray[static_cast<int>(MyEnum::SOME_VALUE)]
}
Summary
Using enumerators for array indexing provides several benefits:
Advantages:
- Self-documenting: Code becomes more readable and maintainable
- Type safety: Prevents mixing up different types of indices
- Error reduction: Less likely to use wrong magic numbers
- Refactoring safety: Easy to reorder or add new fields
Best practices:
- Use descriptive names for enum values
- Include a COUNT value for array sizing
- Consider
enum class
for better type safety - Create helper functions to reduce casting noise
- Always validate indices when working with user input
When to use:
- Arrays with fixed, meaningful structure
- When indices represent specific concepts
- Multi-dimensional arrays with logical dimensions
- Data that benefits from named access patterns
In the next lesson, you'll learn about std::vector resizing and capacity management.
Quiz
- What advantages do enumerators provide over magic numbers for array indexing?
- Why is it useful to include a COUNT value in an enumeration used for indexing?
- What's the difference between
enum
andenum class
when used for array indexing? - How can you make casting enum values to integers more convenient?
- What should you consider when defining enum values for array indices?
Practice exercises
Try these exercises to practice using enumerators with arrays:
-
Game character stats: Create an enumeration for RPG character statistics (health, mana, strength, dexterity, intelligence) and use it to manage a character's stats in a vector.
-
Weekly planner: Create an enumeration for days of the week and use it to manage a weekly schedule stored in a vector. Include functions to add events, display the schedule, and find the busiest day.
-
RGB color mixer: Create enumerations for RGB color components and use them to implement color mixing functions that can blend two colors or adjust brightness.
-
Monthly budget tracker: Design an enumeration for budget categories (housing, food, transportation, entertainment, savings) and create a budget tracking system using vectors indexed by these enumerations.
Explore More Courses
Discover other available courses while this lesson is being prepared.
Browse CoursesLesson Discussion
Share your thoughts and questions