[13] Ondup (Part of C FAQ Lite, Copyright © 1991-2001, Marshall Cline, Cline@parashift.com)
体,, nicrosoft @ sunistudio.com (East day production room, East day document)
FAQS in section [13]:
[13.1] What is the role of operator overload? [13.2] What is the benefit of an operator overload? [13.3] What kind of operator is overloaded? [13.4] But the operator is overloaded to make my class ugly; Is it not clearer? [13.5] What operators / cannot be overloaded? [13.6] I can override Operator == to compare the string comparison between two char []? [13.7] Can I create an Operator ** for the "power" operation? [13.8] How to create a subscript operator for the Matrix class? [13.9] Why should the interface of the Matrix class should not like an array of arrays? [13.10] Is this from the outside (interface priority) or from within (data priority)?
[13.1] What is the role of operator overload?
It allows you to provide an intuition interface for users.
The operator overload allows C / C operators to have a user-defined meaning on the user-defined type (class). The overloaded operator is the syntax modification of the function called:
Class fred {public:
// ...
}; #If 0
// No operator overload:
Fred Add (Fred, Fred); Fred Mul (Fred, Fred); Fred F (Fred A, Fred B, Fred C) {Return Add (ADD (Mul (A, B), MUL (B, C)), MUL (C, A));
// Haha, more comic ...
} #Else
// Once an operator is overloaded:
Fred Operator (Fred, Fred); Fred Operator * (Fred, Fred); Fred F (Fred A, Fred B, Fred C) {RETURN A * B B * C C * a;} #ENDIF
[TOP | BOTTOM | Previous Section | Next Section]
[13.2] What is the benefit of an operator overload?
By overloading standard operators, you can explore the user's intuition. The language used to make the user program is a problem, not the machine.
The ultimate goal is to reduce the learning curve and reduce the error rate.
[TOP | BOTTOM | Previous Section | Next Section]
[13.3] What kind of operator is overloaded?
[Recently Changed SO IT Uses The Std :: SYNTAX (ON 7/00). Click Here to Go To The Next Faq in The "Chain" of Recent Changes
]
There are some examples of operators here:
MyString Yourstring can connect two std :: string object MyDate can add a Date object A * b can multiply the two Number objects A [i] can access an element of an Array object x = * P can be reversed "Spirit" "Smart Pointer" - it actually locates the record pointed to the P on the disk and returns to X. [TOP | BOTTOM | Previous Section | Next Section]
[13.4] But the operator is overloaded to make my class ugly; Is it not clearer?
The operator is overloaded makes the user's work more easily, not for class developers!
Consider the following example:
Class array {public: int & operator [] (unsigned i);
// Some people don't like this grammar
// ...
}; Inline Int & Array :: Operator [] (unsigned i)
// Some people don't like this grammar
{
// ...
}
Some people don't like the Operator keywords or some weird grammar in the body. However, the operator overload syntax is not desirable to make the work of the developer of the class easier. It is desirable to make the user's work more easily:
INT main () {Array A; A [3] = 4;
// User code should be obvious and easy to understand ...
}
Remember: There are a lot of people who use your class in a reusable world, and people build it only (yourself); so you should take care of most instead of doing anything.
[TOP | BOTTOM | Previous Section | Next Section]
[13.5] What operators / cannot be overloaded?
Most of them can be overloaded. Only in C's operators are only. And?: (And SizeOf, technology can be thought of as an operator). C adds some own operators, except :: and. *, Most of them can be overloaded.
This is an example of a subscript (it returns a reference). No operator is overloaded first:
Class array {public: int & elem (unsigned i) {if (i> 99) error (); return data [i];} private: int data [100];}; int main () {Array a; a.lex (10) = 42; A.Elem (12) = a.Elem (13);}
Now use the operator overload to give the same logic:
Class array {public: int & operator [] {if (i> 99) error (); return data [i];} private: int data [100];}; int main () {array a; a [10] = 42; A [12] = a [13];
[TOP | BOTTOM | Previous Section | Next Section]
[13.6] I can override Operator == to compare the string comparison between two char []?
[Recently Changed SO IT Uses The Std :: SYNTAX (ON 7/00). Click Here to Go To The Next Faq in The "Chain" of Recent Changes
]
No: The operator is overloaded, at least one operand must be a user-defined type (most of the time is class).
But don't do this even if C is allowed. Because you should use a class similar to std :: string instead of a character array because arrays are harmful. Therefore, no matter how you don't think so.
[TOP | BOTTOM | Previous Section | Next Section]
[13.7] Can I create an Operator ** for the "power" operation?
Not.
The name, priority, combination, and metadata of the operator are fixed by the language. There is no Operator ** in C , so you can't create it for class types.
If you have any questions, consider x ** y and x * (* y) equivalent (in other words, the compiler assumes Y is a pointer). In addition, the operator overload is just the syntax modification of the function call. Although this special grammatical modification is very wonderful, it does not add anything in nature. I suggest you overload POW (Base, Exponent) (Double Accuracy Version in
By the way, Operator ^ can become a power, but the priority and combination are wrong.
[TOP | BOTTOM | Previous Section | Next Section]
[13.8] How to create a subscript operator for the Matrix class?
[Recently Changed SO IT Uses New-Style Headers and the Std :: Syntax (on 7/00). Click Here To Go To The Next Faq in The "Chain" of Recent Changes
]
Use Operator () instead of Operator [].
When there are multiple subscripts, the clearest way is to use Operator () instead of Operator []. The reason is that Operator [] always takes a parameter, and operator () can take any number of parameters (two parameters required in the case of rectangular matrix).
Such as:
Class Matrix {public: Matrix (unsigned rows, unsigned cols); Double & operator () (unsigned row, unsigned col); Double Operator () (unsigned row, unsigned col)
// ...
~ Matrix ();
// Destructor
Matrix (Const Matrix & M);
// Copy constructor
Matrix & Operator = (Const Matrix & M);
// Assignment Mode
// ...
Private: unsigned rows_, cols_; double * data_;}; inline matrix :: matrix (unsigned rows, unsigned cols): ROWS_ (ROWS), COLS_ (COLS), DATA_ (NEWS) {IF (Rows == 0 || COLS == 0) Throw Badindex ("Matrix Construction Has 0 Size");} inline Matrix :: ~ Matrix () {delete [] data_;} inline Double & Matrix :: Operator () (unsigned Row, Unsigned color {if (row> = rows_ || col> = cols_) throw badindex ("Matrix Subscript out of bounds"); Return Data_ [COLS_ * ROW COL];} Inline Double Matrix :: Operator () (unsigned Row, unsigned color const {if (row> = rows_ || col> = cols_) Throw Badindex ("const mapcript out of bounds); return data_ [cols_ * row col];} then, you can use M (i, j) to access the Elements of Matrix M, not M [I] [J]:
INT main () {Matrix M (10, 10); M (5, 8) = 106.15; std :: cout << m (5, 8);
// ...
}
[TOP | BOTTOM | Previous Section | Next Section]
[13.9] Why should the interface of the Matrix class should not like an array of arrays?
This FAQ is actually about: the Matrix class established by some people, with an Operator [] that returns an Array object. The Array object also has an Operator [], it returns an element of Matrix (for example, a Double). Therefore, they use the syntax of M [I] [J] to access the matrix element, not the syntax of M (i, j).
The array of array is clearly operable, but the lack of flexibility is lacking relative to the Operator () method. In particular, when using the [] [] method, it is easy to complete with the Operator () method, so the [] [] method is likely to result in differences, at least some case details.
For example, the easiest way to implement the [] [] method is to use the physical layout of the dense matrix to save (or listed as the main, I can't remember) in the form of a dense matrix. Instead, the Operator () method completely hides the physical layout of the matrix, in which case it may bring better performance.
It can be seen so that the operator () method is never poor than [] [] method, sometimes better.
Operator () is never poor because it is very easy to implement the physical layout of the dense matrix of behavior with the operator () method. Therefore, when the performance viewpoint is exactly the best layout, the Operator () method is also as simple as the [] [] method (maybe the operator () method is more likely to be a little bit, but I don't want to exaggerate the word). The Operator () method is sometimes better because it is much easier to implement more than [] [] method compared to [] [] method than [] method [] [] method when there is a better layout than a given matrix. As an example of a physical layout, the most recent project occurs in column access matrix elements (i.e., algorithm access all elements in one column, then another column, etc.), if the physical layout is the master, the matrix Access may "Cache invalid". For example, if the size of the row is almost comparable to the processor's Cache size, the access to each element will happen to "cache." In this special project, we get 20% by 20% by turn the mapping from the logical layout (line, column) to the physical layout (column, line).
Of course, there are many examples of such things, and the sparse matrix is another type of example. Typically, use the Operator () method to implement a sparse matrix or the exchange line / column order is easier, and the Operator () method will not lose what, but may get something - it will not be worse, but it may be better.
Use the Operator () method.
[TOP | BOTTOM | Previous Section | Next Section]
[13.10] Is this from the outside (interface priority) or from within (data priority)?
[Recently Changed SO IT Uses New-Style Headers and the Std :: Syntax and Reworded References To STL (on 7/00). Click Here to Go To The Next Faq in The "Chain" of Recent Changes
]
From outside!
A good interface provides a simplified view of user vocabulary. In the case of object-oriented software, the interface is usually a collection of PUBLIC methods of a single class or a set of closely combined classes.
First consider the logical characteristics of the object, rather than intend to create it. For example, assume that you want to create a Stack (stack) class that contains a LinkedList:
Class stack {public:
// ...
PRIVATE: LinkedList List_;};
Does Stack have a get () method that returns LinkedList? Or a set () method with LinkedList? Or a constructor with LinkedList? Obviously, the answer is "not" because the interface should be designed from the outside. That is, the user of the Stack object does not care about LinkedList; they only care about Pushing and Popping.
Now look at another more subtle example. Suppose the LinkedList class uses the Node object's linked list to create, each Node object has a pointer to the next Node:
Class node {
/*...
}; Class LinkedList {public:
// ...
PRIVATE: NODE * first_;}; whether the LinkedList class should have a get () method for users to access the first Node? Does the Node object should have a get () method for the user to access the next node in the chain? In other words, from the outside, what should LinkedList? Is LinkedList actually a chain of a Node object? Or these are just the details of implementation? If only the details of the implementation, how will LinkedList let the user visited each element in LinkedList at a certain time?
Someone's answer: LinkedList is not the Node chain. It may be indeed created with Node, but this is not essential. Its essence is the sequence of an element. Therefore, LinkedList abstraction should provide a "linkedListiterator", and "linkedListiterator" should have an Operator to access the next element and have a pair of GET () / set () to access the value stored in Node (the value in the node element is only The LinkedList user is responsible, therefore has a pair of GET () / set () to allow users to freely maintain this value).
From the user's point of view, we may hope that the LinkedList class supports seeming to use the modular algorithm to access the array of arrays:
Void Usercode (LinkedList & a) {for (LinkedListiterator P = a.begin (); p! = a.end (); P) std :: cout << * p << '/ n';}
Implement this interface, LinkedList requires a begin () method and end () method. They return a "LinkedListiterator" object. The "LinkedListiterator" requires a way forward, P; access to the current element, * p; and a comparison operator, P! = A.end ().
The following code is that the LinkedList class does not have any way to access Node. Node is completely hidden as a realization technology. The LinkedList class may be replaced with a double-lapse table, even an array, which is only in some performance, such as Prepend (ELEM), and APPEND (ELEM) method.
#include
// Poor Man's Exception Handling
Class LINKEDLISTITERATOR; Class LINKEDLIST; Class Node {
// no public members; this is a "private class"
Friend LinkedListiterator;
// Friends
Friend LinkedList; Node * Next_; INT ELEM_;}; Class LINKEDLISTITITOR {public: Bool Operator == (LinkedListItItor i) const; boore Operator! = (LinkedListIterator i) const; void Operator ();
// go to the next element
INT & OPERATOR * ();
// Access the current element
Private: LinkedListiterator (Node * P); Node * P_; Friend LinkedList; // So LinkedList CAN Construct A LinkedListIterator
}; Class LinkedList {public: void append (int elem);
// adds elem after the end
Void Prepend (Int Elem);
// Adds elem before the begnning
// ...
LinkedListiterator Begin (); LinkedListItItIn ();
// ...
PRIVATE: NODE * first_;
These are obviously in lineless methods (possibly in the same header file):
! Inline bool LinkedListIterator :: operator == (LinkedListIterator i) const {return p_ == i.p_;} inline bool LinkedListIterator :: operator = (LinkedListIterator i) const {return p_ = i.p_;!} Inline void LinkedListIterator: : Operator () {assert (p_! = null);
// or if (p _ == null) throw ...
P_ = p _-> next_;} inline int & linkedListiterator :: operator * () {assert (p_! = null);
// or if (p _ == null) throw ...
return p _-> elem_;} inline LinkedListIterator :: LinkedListIterator (Node * p): p_ (p) {} inline LinkedListIterator LinkedList :: begin () {return first_;} inline LinkedListIterator LinkedList :: end () {return NULL;}
Conclusion: The linked list has two different data. The value of the elements stored in the linked list is responsible for the user of the list (and only the user is responsible, the chain list does not prevent the user from turning the third element into the fifth), and the data of the chain surface underlayer (such as NEXT pointer, etc.) value The list is responsible (and only the list is responsible, that is, the linked list does not allow the user to change (even seeing!) The variable NEXT pointer).
Therefore, the GET () / set () method only gets the elements of the linked list, not the underlying structure of the linked list. Since the linked list hides the structure of the underlying pointer, it can make a very strict commitment (for example, if it is a double linked list, it ensures that each rear pointer matches the forward pointer of the next Node).
We read this example, the value of some of the data is responsible by the user (this requires a GET () / set () method for the data), but for the data controlled by the class, there is no need to have get () / set ()method.
Note: The purpose of this example is not to tell you how to write a chain class. Don't do your own chain class yourself, and you should use the "container class" provided by the compiler. In theory, one of the standard containers, such as: std :: list
E-mail the author [C FAQ Lite | Table of Contents | Subject Index | About The Author | © | Download Your Own Copy] Revised Apr 8, 2001