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
39: Friend Functions and Classes
I was working on a small game project the other day and ran into a classic architectural headache. I had a Player class that tracked the character's coordinates. Naturally, I made those coordinates private because I didn't want some random UI script or an unrelated sound-effect trigger accidentally teleporting my player to the moon.
Hitting the Private Wall
Here is where I started. I have my Player, and I want a standalone function called calculateDistance that takes two players and figures out how far apart they are. It seems like a utility function that doesn't necessarily belong *inside* the Player class.
class Player {
private:
float x, y;
public:
Player(float x, float y) : x(x), y(y) {}
};
float calculateDistance(const Player& p1, const Player& p2) {
// This is where it falls apart
float dx = p1.x - p2.x;
float dy = p1.y - p2.y;
return std::sqrt(dx*dx + dy*dy);
}
The compiler immediately yells at me. 'float Player::x' is private within this context. I can't touch those variables because calculateDistance is just a regular function; it's not a member of the class, so it has no special privileges.
The Temptation of Public
Now, the "easy" way out—and the way I see a lot of juniors do it—is just to make x and y public. Sure, the code compiles, but now any part of my program can change the player's position. I've traded a compiler error for a potential nightmare of bugs where my player starts flickering across the screen because some unrelated function touched the coordinates.
I could write "getter" functions (like getX()), but in a high-performance loop—say, checking distance for 100 NPCs every frame—I don't want the overhead of function calls if I can avoid it, and more importantly, I want to keep the API clean. I don't want everyone to be able to see the coordinates, just this specific distance calculator.
Granting a Specific Hall Pass
This is where the friend keyword comes in. It's basically a way for a class to say, "I don't trust the general public, but I trust this one specific function." I'll go back into my Player class and explicitly name the function as a friend.
class Player {
private:
float x, y;
public:
Player(float x, float y) : x(x), y(y) {}
// I'm granting this specific function access to my privates
friend float calculateDistance(const Player& p1, const Player& p2);
};
float calculateDistance(const Player& p1, const Player& p2) {
// Now this works perfectly!
float dx = p1.x - p2.x;
float dy = p1.y - p2.y;
return std::sqrt(dx*dx + dy*dy);
}
Notice that the friend declaration is inside the class, but the function itself is still defined outside. It's a bit strange—the class is essentially vouching for the function. The function isn't a "member" of the class; it just has a VIP pass to the private sections.
The Full Access Badge
As my game grew, I realized I didn't just have one utility function. I had a whole PhysicsEngine class that needed to handle collisions, gravity, and velocity. If I had to list every single physics function as a friend, my Player class would end up being a giant list of permissions.
C++ lets us grant friendship to an entire class instead. If I make the PhysicsEngine a friend, every single member function inside PhysicsEngine can access Player's private data.
class Player {
private:
float x, y;
public:
Player(float x, float y) : x(x), y(y) {}
// Now anything inside PhysicsEngine is a "friend"
friend class PhysicsEngine;
};
class PhysicsEngine {
public:
void applyGravity(Player& p) {
p.y -= 9.8f; // Legal!
}
void resolveCollision(Player& p1, Player& p2) {
// I can access p1.x and p2.x without any getters
p1.x += 1.0f;
}
};
A word of caution: use this sparingly. Friendship is not symmetric. Just because Player trusts PhysicsEngine doesn't mean PhysicsEngine's private data is open to Player. Also, friendship isn't inherited. If you have a AdvancedPhysicsEngine that inherits from PhysicsEngine, it doesn't automatically get the keys to the Player class. You'd have to grant it friendship explicitly.
It feels like "cheating" the encapsulation rules, but when used for tightly coupled systems—like a class and its manager, or a class and its helper utility—it's often the cleanest way to write the code.
📋 Practical Task
Implementing a Security Override for a Smart Home System
You are building a smart home simulation. Create a class called SmartLock that has a private boolean isLocked and a private string accessCode. Normally, these should not be accessible from outside the class.
However, the home needs a SecurityOverride class that can force a lock to open regardless of the code (for emergencies) and can change the access code during a system reset.
Your task is to:
- Implement the
SmartLockclass with the private members mentioned. - Implement the
SecurityOverrideclass with two methods:forceUnlock(SmartLock& lock)andresetCode(SmartLock& lock, std::string newCode). - Use the
friend classmechanism to allowSecurityOverrideto modify the private state ofSmartLock. - In your
mainfunction, instantiate both classes and demonstrate thatSecurityOverridecan successfully change the code and unlock the door.
There are no comments for now.