Author: Hub Sutter Translator: plpliuly
/ * This article is a GotW (GURU of the Week) series of self-entertainment translations, the original copyright is the author of Hub Sutter (the famous C expert, "Exceptional C " author). The translation of this article did not have the consent of the original author, only for learning discussion. - Translator * /
# 4 of Class Mechanics Difficulty: 7.5 / 10
How much is your specific details that define a class? This Gotw not only discusses some mistakes that are easily made when writing a class, but also discuss how to make you written in a professional style.
Question: Suppose you are watching a code. It contains the following type definition. There are several styles in this class definition, and there are few true errors. You can find a few, how to modify it?
Class complex {public: complex (double real, double imaginary = 0): _Real (real), _imaginary (imaginary) {};
Void OPERATOR {_Real = _Real Other._real; _imaginary = _imaginary other._image;
Void Operator << (Ostream OS) {OS << "(" << _Real << "," << _imaginary << ")"
Complex operator () { _ real; return * this;}
Complex Operator (int) {complex temp = * this; _ real; return temp;}
PRIVATE: DOUBLE _REAL, _IMAGINARY;
answer:
Where there is a way to modify or improve in this class definition, it is far more than that we will mention below. We propose this issue to discuss the configuration of class definitions (for example, "<< operator standard form What kind of "," "" operator should be defined as member functions? "), Not to discuss which interface is not too bad. But I still want to mention the suggestions of subsequent, as I 0 Locally worthwhile: 0. Since there have been multiple classes in the standard library you have to define a COMPLEX class yourself? (Moreover, the classes defined in the standard library will not have any of the problems mentioned below, The best person with years of experience is written. Or modest to reuse these classes directly) [Advice] Try to use the standard library algorithm directly. That will write a fast, and it is difficult to make mistakes.
Class complex {public: complex (double real, double imaginary = 0): _real (real), _imaginary (imaginary) {}; 1. style question: This constructor can be used as a single parameter function, which may be used as a hidden Use of translation functions. But it may not be implicit conversions you expect. [Advice] Beware of the type of conversion operation that is not aware of noticeable occurs when you don't want it. A good way to avoid it is: possibly If you use the explicit modifier limit constructor. Void OPERATOR (Complex Other) {_real = _Real Other._real; _Imaginary = _imaginary other._imaginary;} 2. Style Question: In terms of efficiency, the parameters should be Const & Type, And "a = a b" forms should be replaced with "A = B" statement. [Guidelines] Try to use the const & parameter type instead of copy pass transmission value type parameters. [Advice] For arithmetic operation, try to use "A OP = b "instead" a = a op b ". (Pay attention to some classes - such as a class written by someone - may change the relationship between OP and OP = through the overload of the operator) 3. Style problem: operation The compass should not be defined as a member function. If it is defined as a member function in the above code, you can only use the "A = 1" form of statements, such as "a = 1 b" statement. In this way, you need to provide the definition of Operator (int) (int, comple), and Operator (int, complex). [Guideline] When considering the determination of an operator function as a member function or a non-member function, try to refer to the following principles: (Lakos96: 143-144; 591-595; MURRAY93: 47-49) - One dollar (single parameter) operator function should define member functions - =, (), [], and -> operator should define member functions - =, - =, / =, * =, (etc.) should be defined as a member function - the remaining operators should be defined as non-member functions 4. Error: Operator should not change the object itself. It should return A temporary object containing addresses. Please note that in order to prevent operation like "A B = C", this operator is returned to the class. Type should be "const complex" (not just "complex"). (In fact, the above code is more like defined = operator function code) 5. Style problem: Generally, when defining OP operations The OP = operator should also be defined at the same time, so there should be a = operator here. The above code should be = definition (but the return type requires modification, see the discussion below).
Void operator << (Ostream OS) {OS << "(" << _Real << "," << _imaginary << ")";} (Note: For a real << operator, you should check Stream Current format flags to support general usage (translator: iostream controls the output format by setting the flag). Please refer to your favorite STL books to learn about details) 6. Error: Operator << should not be defined as a member function ( See the discussion above), and the parameters should be "Ostream &, Const Complex &". Note that as James Kanze pointed out, try not to define a friend function! It is best to define it to call a "print" PUBLIC Member function works. 7. Error: This operator should return "Ostream &", and should end with "Return OS". This allows you to link multiple output operation links (such as "cout << a << b; "). [Guidelines] Operator << and >> Both should return to Stream references .complex operator () { _ real; return * this;} 8. Style problem: You should return Complex & So, so that the caller can More direct operation. (Translator: In fact, I think this is not just a problem problem. Because the standard definition of the previously increased standard, the syntax of " A" should be supported, and it should be induced before twice. For the object's own operation, if the Complex type is returned, the second preceding call is self-increment operation.)
Complex Operator (int) {complex temp = * this; _ real; return temp;} 9. Style problem: After the increase should return "const complex". This can prevent the use of "A ". This sentence is not Will like some people think that the A object will continue to be used for the A object. 10. Style problem: If the self-increment operation is achieved, the self-amplifier function will be better. (Translator: will " _Real; "Replace with" (* this); ") [Guidelines] Try to achieve the self-increment operation after the previous self-increment operation. Private: double _real, _imaginary;}; 11. Style problem: Try to avoid use The underscore starts naming the variable. I used to define variables like this, even in the famous "Design Patterns". However, because of the standard library's implementation, there are many underscore start identifiers, if we want to use The underscore defines that your own variable will remember that all the identifiers that have been retained may be avoided, which is too difficult. (Since using the underscore as a sign of the member variable is easy to conflict with the retention identifier, then I will lower the line on the end of the variable. Finally, do not consider other defects in the design and style of the above, we can get the following Credits: Class Complex {public: Explicit Complex (Double Real, Double Imaginary = 0): Real_ (Real ), imaginary_ (imageary) {}
Complex & Operator = (const complex & other) {real_ = other.real_; imaginary_ = other.imaginary_; return * this;} complex & operator () { real_; return * this;}
Const Complex Operator (int) {complex Temp = * this; (* this); return temp;}
Ostream & Print (Ostream & OS) Const {Return OS << "(" << real_ << "," << imaginary_ << ")";
Private: Double Real_, Imaginary_; Friend Ostream & Operator << (Ostream & OS, Const Complex & C);
Const Complex Operator (Const Complex & LHS) {Complex Ret (LHS); RET = rhs; Return Ret;
Ostream & Operator << (Ostream & OS, Const Complex & C) {RETURN C.PRINT (OS);} ----- (end)