Ready to practice?
Sign up to access interactive coding exercises and track your progress.
Non-Member Operator Overloading
Implement operators as standalone functions using the public interface.
Overloading Operators Using Normal Functions
In the previous lesson, we overloaded operator+ as a friend function:
#include <iostream>
class Meters
{
private:
int m_meters{};
public:
Meters(int meters)
: m_meters{meters}
{}
friend Meters operator+(const Meters& m1, const Meters& m2);
int getMeters() const { return m_meters; }
};
// Friend function - not a member
Meters operator+(const Meters& m1, const Meters& m2)
{
// Direct access to m_meters (friend function)
return Meters{m1.m_meters + m2.m_meters};
}
int main()
{
Meters distance1{15};
Meters distance2{28};
Meters total{distance1 + distance2};
std::cout << "Total: " << total.getMeters() << " meters\n";
return 0;
}
Friend functions are convenient because they provide direct access to private members. However, if you don't need that access, you can write overloaded operators as normal functions.
The Meters class provides getMeters() to access m_meters without needing friend access, so we can rewrite operator+ as a normal function:
#include <iostream>
class Meters
{
private:
int m_meters{};
public:
Meters(int meters)
: m_meters{meters}
{}
int getMeters() const { return m_meters; }
};
// Normal function - not a member, not a friend
Meters operator+(const Meters& m1, const Meters& m2)
{
// Use public interface instead of direct member access
return Meters{m1.getMeters() + m2.getMeters()};
}
int main()
{
Meters distance1{15};
Meters distance2{28};
Meters total{distance1 + distance2};
std::cout << "Total: " << total.getMeters() << " meters\n";
return 0;
}
Normal and friend function versions work almost identically - they just have different access levels to private members. Generally, we won't differentiate between them unless the distinction matters.
One difference: the friend function declaration inside the class serves as both a prototype and declaration. With normal functions, you must provide your own prototype.
Meters.h:
#pragma once
class Meters
{
private:
int m_meters{};
public:
Meters(int meters)
: m_meters{meters}
{}
int getMeters() const { return m_meters; }
};
// Explicit prototype needed for normal function version
Meters operator+(const Meters& m1, const Meters& m2);
Meters.cpp:
#include "Meters.h"
Meters operator+(const Meters& m1, const Meters& m2)
{
return Meters{m1.getMeters() + m2.getMeters()};
}
main.cpp:
#include "Meters.h"
#include <iostream>
int main()
{
Meters distance1{15};
Meters distance2{28};
Meters total{distance1 + distance2}; // Without prototype, this would fail
std::cout << "Total: " << total.getMeters() << " meters\n";
return 0;
}
Prefer overloading operators as normal functions instead of friends if possible without adding extra accessor functions.
Don't add accessor functions just to avoid making an operator a friend - that defeats the purpose of encapsulation.
Summary
Normal function operators: Overloaded operators can be implemented as regular non-member functions when they don't require access to private members.
Difference from friend functions: Normal functions work identically to friend functions but use the public interface instead of accessing private members directly. The friend declaration inside the class also serves as a prototype, while normal functions require explicit prototypes in header files.
When to use normal functions: Prefer normal functions over friends when the class already provides sufficient public accessor functions to implement the operator, maintaining better encapsulation by relying on the public interface.
Normal functions and friend functions produce nearly identical code - the choice between them depends on whether direct private member access is needed. Both are preferable to member functions for symmetric binary operators that don't modify their operands.
Non-Member Operator Overloading - Quiz
Test your understanding of the lesson.
Practice Exercises
Operators as Normal Functions
Implement operator overloading using normal (non-friend, non-member) functions. Practice using public getters to access private data for operator implementations.
Lesson Discussion
Share your thoughts and questions
No comments yet. Be the first to share your thoughts!