Writing an Operator That Isnt a Member Function
Writing an Operator That Isn t a Member Function
Problem
You have to write a binary operator, and you can't or don't want to make it a class member function.
Solution
Use the operator keyword, a temporary variable, and a copy constructor to do most of the work, and return the temporary object. Example 15-5 presents a simple string concatenation operator for a custom String class.
Example 15-5. Concatenation with a nonmember operator
#include #include class String { // Assume the String class declaration // has at least everything shown here public: String( ); String(const char* p); String(const String& orig); ~String( ) {delete buf_;} String& append(const String& s); size_t length( ) const; const char* data( ) const; String& operator=(const String& orig); // ... }; String operator+(const String& lhs, const String& rhs) { String tmp(lhs); // Copy construct a temp object tmp.append(rhs); // Use a member function to do the real work return(tmp); // Return the temporary } int main( ) { String s1("banana "); String s2("rancher"); String s3, s4, s5, s6; s3 = s1 + s2; // Works fine, no surprises s4 = s1 + "rama"; // Constructs "rama" automatically using // the constructor String(const char*) s5 = "ham " + s2; // Hey cool, it even does it backward s6 = s1 + "rama " + s2; std::cout << "s3 = " << s3.data( ) << ' '; std::cout << "s4 = " << s4.data( ) << ' '; std::cout << "s5 = " << s5.data( ) << ' '; std::cout << "s6 = " << s6.data( ) << ' '; }
Discussion
A standalone operator is declared and defined similarly to a member function operator. In Example 15-5, I could have implemented operator+ as a member function by declaring it like this:
String operator+(const String& rhs);
In most cases, this will work the same way regardless of whether you define operator+ as a member or nonmember function, but there are at least a couple of reasons why you would want to implement it as a nonmember function. The first is conceptual: does it make sense to have an operator that returns a new, distinct object? operator+ as a member function is not an inspector of the object's state, nor does it alter the object's state. It's a general utility function that happens to operate on Strings, and, therefore, should not be a member function.
The second reason is technical. You can't do the following with a member operator (from the example):
s5 = "ham " + s2;
This won't work because a character string doesn't have an operator+ that takes a String parameter. If, on the other hand, you have defined your standalone operator+ that takes two String parameters, your compiler will look to see if the String class has a constructor that takes a const char* argument (or whatever type you are using with a String) and construct a temporary object at runtime. The above code, therefore, is equivalent to this:
s5 = String("ham ") + s2;
The compiler saves you the extra keystrokes by just looking it up and invoking the constructor for you.
Overloading the left- and right-shift operators (<< and >>) for streams also requires that you use nonmember operators. For example, to put your new object to a stream using left-shift, you would have to declare operator<<, like this:
ostream& operator<<(ostream& str, const MyClass& obj);
Of course, you can subclass one of the standard library stream classes, and add all the left-shift operators you want, but is that really a good idea? If you do that, only code that uses your new stream class will be able to write your custom class's objects to it. If you use a standalone operator, any code in the same namespace can just write your object to an ostream (or read it from an istream) with no problem.