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
38: Operator Overloading: Subscript and Function Call
Why would I overload the subscript operator instead of just writing a get() method?
You've probably seen get(int index) or at(int index) in a lot of libraries. They work fine, but they aren't "idiomatic." When you're building a class that acts like a container—say, a custom Matrix class for some graphics work—you want it to feel like a first-class citizen of the language.
Overloading operator[] allows you to use the matrix[i] syntax. It's cleaner, it's faster to type, and more importantly, it makes your class intuitive for other C++ developers. If I see square brackets, I immediately know I'm accessing an element by an index.
struct Matrix {
int data[100]; // Simplified 1D array representing 10x10
int& operator[](int index) {
return data[index];
}
};
// Now I can do this:
Matrix myMat;
myMat[5] = 42; // Feels just like a native array
Wait, if I want to read a value from a const object, the code above breaks. How do I fix that?
This is a classic "gotcha" I see all the time. In the example above, operator[] returns a reference (int&), which allows you to modify the data. But if you pass your Matrix into a function by const reference, the compiler will scream at you because it can't call a non-const method on a const object.
The solution is to provide two versions of the operator: one for when the object is mutable and one for when it's constant. I usually call this "const overloading."
struct Matrix { int data[100]; // Used for: myMat[i] = 10; int& operator[](int index) { return data[index]; } // Used for: int x = myMat[i]; (where myMat is const) const int& operator[](int index) const { return data[index]; } };Notice the
constat the end of the second function signature. This tells C++, "this method doesn't change the state of the object." Without both, you're basically telling the compiler your container is either read-only or write-only, which is rarely what you want.What's the deal with operator(), and why not just use a regular function?
Overloading the function call operator—
operator()—turns your object into a "Functor" (a function object). At first glance, it looks like you're just making a class that acts like a function. And that's exactly what you're doing. But here is the secret: unlike a regular function, a functor has state.Imagine you need a function that adds a specific value to a number, but that value changes based on a configuration file loaded at runtime. A regular function would need a global variable (gross) or a captured variable in a lambda. A functor just stores it as a member variable.
class Adder { int amount; public: Adder(int a) : amount(a) {} // This makes the object "callable" int operator()(int value) const { return value + amount; } }; Adder addFive(5); Adder addTen(10); int result = addFive(10); // result is 15 int result2 = addTen(10); // result2 is 20I use these all the time when passing custom logic into STL algorithms like
std::transformorstd::for_each. It gives you the flexibility of a function with the power of a full-blown class.
📋 Practical Task
Implementing a Bound-Checked Virtual Grid
You need to build a class called VirtualGrid that simulates a 1D array but provides a safe way to access elements. Your task is to implement the following requirements:
- The class should hold an internal
std::vector<int>of a fixed size (initialize it to 10 elements in the constructor). - Overload
operator[]to allow both reading and writing. However, sinceoperator[]doesn't traditionally throw exceptions in C++, you should simply return0(or a default value) if the index is out of bounds for reading, and do nothing if the index is out of bounds for writing. - Implement a "Function Call" operator
operator(). This operator should take two integers (row and column) and treat the 1D vector as a 2D grid (assuming a width of 5). It should return the value at that coordinate. - Ensure that you provide a
constversion of the subscript operator so the grid can be accessed in read-only contexts.
Test your class by creating a const VirtualGrid and trying to read from it using both [] and ().
There are no comments for now.