310 likes | 424 Views
C++ Certificate Program C++ Intermediate. Operator Overloading. Why?. Operator overloading provides no new capabilities in C++ Semantic sugar Provides conventional (and more convenient) notation for manipulating class objects
E N D
C++ Certificate ProgramC++ Intermediate Operator Overloading
Why? • Operator overloading provides no new capabilities in C++ • Semantic sugar • Provides conventional (and more convenient) notation for manipulating class objects • Often better presents class semantics (particularly for value classes)
Generic Programming and Operators • However, operator overloading can be crucial in template functions and classes: template <typename T> T sum (T x) { return (x < 1 ? 0 : x + sum(x-1)); } • The above function treats class and built-in types equivalently (with the same syntax)
Traditional String Manipulation char const * str1 (“abc”); char const * str2 (“def”); char const * str3 (“ghi”); char full_string[64]; strcpy(full_string, str1); strcat(full_string, str2); strcat(full_string, str3); fprintf(“string is now ‘%s’.\n”, full_string); // outputs “string is now ‘abcdefghi’.”
Operators Overloaded std::string const str1 (“abc”); std::string const str2 (“def”); std::string const str3 (“ghi”); std::string full_string (str1); // copy ctor full_string += str2; // operator += overloaded full_string += str3); cout << “string is now ‘“ << full_string << ‘’.\n”; // outputs “string is now ‘abcdefghi’.”
->, = Operators • ‘pointer to’ operator T *tp = new class T; tp->myMethod(); // same as (*tp).myMethod(); • Equality operator a == b; // overload needed for class types
Limitations • Anything you can do with operators can be done via methods (reverse is not true) • Some operators may not be overloaded • Can only overload existing operators – new operators may not be defined • Limited to syntax defined in the C++ grammar (explained in more detail later)
What Can be Overloaded • ‘Normal’ Operators + - * / % & ^ | > < ! = ~+= -= *= /= %= &= ^= |= >= <= != == ~= && || ++ -- >>= <<= • ‘Special’ operators [] () -> ->* , << >> new, delete, new[], delete[]
What Cannot be Overloaded :: . .* • Scope resolution operator, as in namespace::identifier • Member selection, as in T.member • Member selection through pointer to member, as in T.*member • These all take a name rather than a value as the second operand
Declaration • returntype ‘operator’ symbol (parameters) • Perhaps most common is the assignment operator T& operator= (T const &);
Binary Operators • Non-static member function taking one argument class X { public: X & operator = (X const &); } • The LHS is the object itself
Binary Operators • Non-member function taking two arguments void operator + ( X a, X b ); • This function has no special access to internals of class X • For operator @,aa@bb can be interpreted as aa.operator@ (bb) or operator@(aa,bb)
Unary Operators • Non-static member function taking no arguments class X { public: X & operator ++(); // prefix ++ }; • Again, the LHS is the object itself • For operator @, @aa can be interpreted as aa.operator@ () or operator@(aa)
Unary Operators • Non-member function taking one argument void operator* (X& x); // deref op • As with binary operators, this function has no special access to internals of class X
Member vs Non-Member Recommendations • Method (member function): All unary operators • Must be a method: = () [] -> • Method: += -= /= *= ^= &= |= %= >>= <<= • All other binary operators: Non-member (global) function
“Special” operators • May be thought of as having no strongly predefined meaning • For example: “Array” subscript operator, functor aids, memory allocation, conversion operators
Operator Details • Most binary and unary operators can be either methods or global functions • Method versions are applied to LHS of the expression (binary ops) or directly to object (unary ops) • Example expression: x = y + 2; either a global or a member version works
Grammatical Coherence • Operator “arity” cannot be changed class X { X operator/(); // err, not unary op X operator &(X,X); // err, not ternary }; X operator % (X); // err, not unary
Predefined Meanings • Only a few assumptions are made operator= operator[] operator() operator-> • Must be non-static member functions, are assured an L-value is possible
Redundant Operators and Pre-Defined Meanings • Built-in type operator equivalences not carried over – for int: a++ means a+=1, means a=a+1 • For class operator overloading, no equivalences: X operator++(int) // postfix version • does not automatically define X operator+= (int) • Associativity and precedence cannot be changed from built-in grammar rules
Redundancy • Given x = x + 2, x = 2 + xis NOT necessarily valid (transitivity is not assumed) • Compiler cannot assume intent from operator semantics • For example, two operators are needed: T operator+ (int left, T const& right );T operator+ (T const& left, int right ); • First version (left op is integer) can’t be method (since would require adding method to built-in type int)
Redundant Operators x = x + 1; x += 1; x++; ++x; • Gives 5 possible operations – all must be overloaded for class (user-defined) types (although copy assignment might be compiler generated)
Operators and Class Types • An operator function must be either a member function or take at least one argument of class (user-defined) type • C++ not intended to be mutable - cannot change expression rules, but meanings in the context of a user-defined object can be defined • Thus, operators cannot be defined that operate exclusively on pointers • For member functions, the first operand must be a user-defined object
Operators in Namespaces namespace std { class ostream { ostream &operator << (char const *); }; extern ostream cout; typedef basic_string<char> string; ostream & operator << (ostream &, string const &); } // end namespace std int main() { char const * p = “hello”; std::string s (“world”); std::cout << p << “, “ << s << endl; }
Actual Function Calls std::cout << p becomes std::cout.operator <<(char const *) and std::cout << str becomes std::operator<<(std::cout, s)
Name Lookup Rules • Name resolution follows same rules as regular functions, namespace std searched for appropriate operator overload: • x @ y is resolved: • If X is class type, look for operator @ as a member of X or base of X • Look for declarations of operator @ in the context surrounding x @ y, and • If X is defined in namespace N, look for declarations of operator @ in N • If Y is defined in namespace M, look for declarations of operator @ in M
Constructors and Conversions int operator +(X,Y); int operator +(Y,X); int operator +(X,Z); int operator +(Z,X); int operator +(Y,Z); int operator +(Z,X);
Constructors and Conversions • If conversions defined: X(Y); X(Z); • Would only then need to code: int operator +(X,X); The statement int val (x + y); would result in x + X(y) invoking int operator + (X,X);
Prefer Prefix Increment vs Postfix • Using prefix ++ typically more efficient than postfix (never less efficient) • Postfix ++ requires a temporary value to be stored and returned by value • Built-in types may be optimized, class types typically not (in particular, iterator classes) • Good style – prefer prefix increment, only use postfix when necessary as part of expression
Share Implementations When Appropriate class BigInt { // … BigInt& operator+= (int); BigInt& operator++ (); // prefix increment BigInt operator++ (int); // return by value }; BigInt& BigInt::operator+= (int) { // … return *this; } BigInt& operator++ () { return *this += 1; } BigInt operator++ (int) { BigInt tmp(*this); *this += 1; return tmp; }