C++ friend Keyword: Access Control, Operators,
이 글의 핵심
friend functions and classes grant access to private and protected members. When to use friend for operators and factories, when to avoid it, and how it compares to getters.
What is friend?
By default, private members of a class are accessible only by the class’s own member functions. The friend keyword grants another function or class permission to access those private members:
class Box {
int width_; // private
public:
Box(int w) : width_(w) {}
// Declare printWidth as a friend — it can access width_
friend void printWidth(const Box& box);
};
// Not a member, but has friend access to Box's private members
void printWidth(const Box& box) {
std::cout << "Width: " << box.width_ << '\n'; // OK — friend access
}
int main() {
Box b(42);
printWidth(b); // prints: Width: 42
}
The friend declaration lives inside the class definition, but the friend function itself is not a member — it’s a regular free function with special access.
Friend Functions vs Member Functions
The choice between a friend function and a member function matters for operators:
class Vector2D {
double x_, y_;
public:
Vector2D(double x, double y) : x_(x), y_(y) {}
// Member function: first operand must be a Vector2D
Vector2D operator+(const Vector2D& other) const {
return {x_ + other.x_, y_ + other.y_};
}
// Member function: can access private fields directly
double length() const {
return std::sqrt(x_ * x_ + y_ * y_);
}
// Friend non-member: both operands treated symmetrically
friend Vector2D operator*(double scalar, const Vector2D& v) {
return {scalar * v.x_, scalar * v.y_};
}
// operator<< must be non-member (ostream is on the left)
friend std::ostream& operator<<(std::ostream& os, const Vector2D& v) {
return os << '(' << v.x_ << ", " << v.y_ << ')';
}
};
int main() {
Vector2D a(1, 2), b(3, 4);
auto sum = a + b; // member operator+: OK
auto scaled = 2.0 * a; // friend operator*: OK (2.0 on left)
// auto scaled2 = a * 2.0; // also works if you add that overload
std::cout << sum << '\n'; // calls friend operator<<
std::cout << a.length() << '\n'; // member function
}
Rule: if the left operand is not your class type (or you want symmetric treatment), make it a non-member friend. operator<< is the most common case — ostream is always on the left.
Friend Classes
A friend class gets access to all private and protected members of the grantor:
class Engine; // forward declaration
class Car {
int fuel_;
int speed_;
friend class Engine; // Engine can access Car's private members
public:
Car() : fuel_(100), speed_(0) {}
int speed() const { return speed_; }
};
class Engine {
public:
void accelerate(Car& car, int amount) {
if (car.fuel_ > 0) { // friend access — reads private fuel_
car.speed_ += amount; // friend access — modifies private speed_
car.fuel_ -= amount / 10; // friend access — modifies private fuel_
}
}
int fuel(const Car& car) const {
return car.fuel_; // friend access
}
};
int main() {
Car car;
Engine engine;
engine.accelerate(car, 30);
std::cout << "Speed: " << car.speed() << ", Fuel: " << engine.fuel(car) << '\n';
}
Friend classes represent a stronger coupling than friend functions. All methods of Engine can see all private members of Car. This is appropriate when the two classes are tightly coupled by design (e.g., iterator and container).
Friend Member Functions
You can grant friendship to a specific member function of another class (not the whole class):
class Logger;
class Server {
int port_;
std::string secret_key_;
// Only Logger::logStatus can access Server's private members
friend void Logger::logStatus(const Server&);
public:
Server(int port) : port_(port), secret_key_("abc123") {}
};
class Logger {
public:
void logStatus(const Server& s) {
// Access to private members — but only for logStatus
std::cout << "Port: " << s.port_ << '\n';
// std::cout << s.secret_key_; // OK here but we choose not to log it
}
void otherMethod(const Server& s) {
// std::cout << s.port_; // compile error — not a friend function
}
};
This is more precise than making the whole class a friend, but requires Logger to be fully defined before Server.
Factory Functions Using Friend
Private constructors + friend factory is a pattern to control how objects are created:
class SecureToken {
std::string value_;
// Private constructor — cannot be called directly
explicit SecureToken(std::string val) : value_(std::move(val)) {}
friend SecureToken createToken(const std::string& key);
public:
const std::string& value() const { return value_; }
};
SecureToken createToken(const std::string& key) {
// Validate key, generate token, etc.
if (key.empty()) throw std::invalid_argument("Key required");
return SecureToken{"tok_" + key}; // friend — can call private constructor
}
int main() {
// SecureToken t("raw"); // compile error — constructor is private
auto token = createToken("mykey"); // must go through factory
std::cout << token.value() << '\n'; // tok_mykey
}
Practical: Fraction with Arithmetic Operators
#include <numeric>
#include <stdexcept>
#include <iostream>
class Fraction {
int num_, den_;
void reduce() {
int g = std::gcd(std::abs(num_), den_);
num_ /= g;
den_ /= g;
}
public:
Fraction(int num, int den) : num_(num), den_(den) {
if (den_ == 0) throw std::invalid_argument("Denominator cannot be zero");
if (den_ < 0) { num_ = -num_; den_ = -den_; }
reduce();
}
// Arithmetic as non-member friends — symmetric, natural syntax
friend Fraction operator+(const Fraction& a, const Fraction& b) {
return {a.num_ * b.den_ + b.num_ * a.den_, a.den_ * b.den_};
}
friend Fraction operator*(const Fraction& a, const Fraction& b) {
return {a.num_ * b.num_, a.den_ * b.den_};
}
friend bool operator==(const Fraction& a, const Fraction& b) {
return a.num_ == b.num_ && a.den_ == b.den_;
}
friend std::ostream& operator<<(std::ostream& os, const Fraction& f) {
if (f.den_ == 1) return os << f.num_;
return os << f.num_ << '/' << f.den_;
}
};
int main() {
Fraction half(1, 2), third(1, 3);
std::cout << half + third << '\n'; // 5/6
std::cout << half * third << '\n'; // 1/6
std::cout << (half == Fraction(2, 4)) << '\n'; // 1 (true — 1/2 == 2/4)
}
friend vs Getters
Both friend and public getters allow external code to read private data. They differ in scope:
friend | Public getter | |
|---|---|---|
| Who can access | Specific named functions/classes | Everyone |
| Encapsulation impact | Limited — known collaborators only | Lower — exposed to all |
| Performance | Direct access | Possible indirection |
| Typical use | Operators, tight collaborators | General API |
Prefer public getters for data that any caller legitimately needs. Use friend when the collaboration is tight and you want to prevent the data from being accidentally accessed by everyone.
Common Mistakes
Over-friending: declaring many classes as friends defeats encapsulation. Keep the friend list to a minimum — usually just operator<< and one or two specific collaborators.
Assuming friendship is symmetric: if A declares B as a friend, B can access A’s private members. But A cannot access B’s private members unless B also declares A as a friend.
Assuming friendship is inherited:
class Base {
int secret_;
friend void readSecret(const Base& b); // only for Base
};
class Derived : public Base {
int derived_secret_;
};
void readSecret(const Base& b) {
b.secret_; // OK
}
void readSecret(const Derived& d) {
// d.derived_secret_; // ERROR — not a friend of Derived
// static_cast<const Base&>(d).secret_; // OK — accessing Base's member
}
Key Takeaways
friendgrants a specific function or class access to private and protected membersoperator<<almost always needs to be a non-member friend (stream is on the left)- Symmetric binary operators (
+,-,*) benefit from non-member friend status — botha+bandb+awork naturally - Friend classes grant access to all methods — appropriate for tightly coupled pairs (iterator/container, builder/product)
- Factory functions with private constructors use
friendto control object creation - Friendship is not inherited and not symmetric
- Prefer public getters for general access; use
friendfor specific named collaborators with a tight coupling justification
자주 묻는 질문 (FAQ)
Q. 이 내용을 실무에서 언제 쓰나요?
A. friend functions and classes grant access to private and protected members. When to use friend for operators and factori… 실무에서는 위 본문의 예제와 선택 가이드를 참고해 적용하면 됩니다.
Q. 선행으로 읽으면 좋은 글은?
A. 각 글 하단의 이전 글 또는 관련 글 링크를 따라가면 순서대로 배울 수 있습니다. C++ 시리즈 목차에서 전체 흐름을 확인할 수 있습니다.
Q. 더 깊이 공부하려면?
A. cppreference와 해당 라이브러리 공식 문서를 참고하세요. 글 말미의 참고 자료 링크도 활용하면 좋습니다.
같이 보면 좋은 글 (내부 링크)
이 주제와 연결되는 다른 글입니다.
- [C++ Classes and Objects: Constructors, Access Control, and](/en/blog/cpp-class-object-beginner/
- [C++ this Pointer: Chaining, const Methods, Lambdas, and CRTP](/en/blog/cpp-this-pointer/
- [C++ Functions: Parameters, Return Values, Overloading, and](/en/blog/cpp-function-basics/
이 글에서 다루는 키워드 (관련 검색어)
C++, friend, access control, OOP, operators 등으로 검색하시면 이 글이 도움이 됩니다.