Skip to Content
Course content

39: Friend Functions and Classes

Click on the "Edit" button in the top corner of the screen to edit your slide content.

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 SmartLock class with the private members mentioned.
  • Implement the SecurityOverride class with two methods: forceUnlock(SmartLock& lock) and resetCode(SmartLock& lock, std::string newCode).
  • Use the friend class mechanism to allow SecurityOverride to modify the private state of SmartLock.
  • In your main function, instantiate both classes and demonstrate that SecurityOverride can successfully change the code and unlock the door.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.