Article 18: Make the interface easy to use correctly, not easy to misuse
1, good interface is easy to use correctly, it is not easy to be misused. You should strive to achieve these properties in all of your interfaces.
2, the "promote positive use" approach includes interface consistency and compatibility with built-in types of behavior.
3, the "block misuse" approach includes establishing new types, restricting operations on types, constraining object values, and eliminating customer resource management responsibilities.
4,SHARED_PTR supports custom-made filters. This prevents DLL problems and can be used to automatically unlock mutexes.
Article 19: Design class like design type
How to design your class:
1, how should objects of the new type be created and destroyed?
Design functions affected: constructors, destructors to have memory allocation functions and deallocation functions (operator new,operator new[],operator delete,operator Delete []).
2, what is the difference between object initialization and assignment of objects?
Depends on the behavior of the constructor and the assignment operator.
3, what does the new type object mean if it is passed-by-value?
The copy constructor is used to define the pass-by-value of a type if implemented.
4, what is the "legal value" of the new type?
The constructor must be effectively instrumented.
5, does your new type need to match an inheritance graph?
If you inherit from another class, be bound by other classes and be aware of the effects of virtual and no-virtual. If there are other classes that inherit this class, consider whether you want to design the destructor as a virtual function.
6, what kind of conversion do you need for your new type?
If you want other types to be converted to type types that you have designed, you need the corresponding non-explicit constructor to exist. If you need to convert the type of the design to another type, you need to define the type conversion function operator T.
7, what kind of operations and functions are reasonable for this new type.
Depends on what functions you declare for your class. Some of these may be member functions, while others are not.
8, what kind of standard makes the function should be dismissed?
Those are exactly the ones you have to declare as private individuals.
9, who should take a member of the new type?
This question helps you decide which member is public, which is protected, and which is private. It also helps you decide which class or function should be friends and whether it is reasonable to nest them within another.
10, how generalized is your new type?
If you define a whole types, should you define a new class template.
11, what is the "undeclared interface" of the new type?
12, do you really need a new type?
If you just define a new derived class to add functionality to an existing class, you might be able to achieve the goal by simply defining one or more non-member functions or templates.
Clause 20: Prefer to replace pass-by-value with Pass-by-reference-to-const
Pass-by-value causes more overhead for constructors and destructors, and class cutting occurs when a derived class is passed to the base class interface.
The above rules do not apply to built-in types, as well as STL iterators and function objects. For them, pass-by-vaule tend to be more appropriate.
Article 21: When you must return an object, don't be paranoid about returning its reference
Never return point or reference to a local stack object, or return reference to a Heap-allocated object, or return point or reference to a local A static object may require multiple such objects at the same time.
Let's consider a class of rational numbers and a friend to multiply by defining a rational number for it:
class rational{public: Rational (int numerator = 0, denominator = 1); n, D; friend const rational operator* ( const rational& LHS, Span style= "color:blue;" >const rational& RHS);
Now let's design this friend function, whose function is to return the product of two rational objects, and we have 3 options:
Programme IConstrational& operator* ( Const rational& lhs< Span style= "color:black;" >, const rational& rhsrational result ( Span style= "Color:gray;" >lhs.n*rhs.N, lhs.d*rhs.D" return result;}
This function returns a reference to result, but result is a local object that is destroyed at the end of the call to the function, and its reference is meaningless to the undefined object.
Programme IIConstRational& Operator* (const rational& lhsconst rational& ) {rational* result=rationallhs.n*rhs.N, lhs.d*rhs.D" return *result;}
In a scenario where result is not a local object, but a pointer to a dynamically allocated object from the heap, the question is who is responsible for the delete pointer, if there is an expression like this:
W, x, y, z;w = x*y*z;
The expression on this side actually calls two operator* operations, which is the creation of two dynamic memory areas, but there is no way to get their pointers.
Programme IIIConstRational& Operator* (Constrational& lhs, const rational& rhsrational result; result = rationallhs.n*rhs.N, lhs.d*rhs.d); return result;}
This time, the static object makes the result out of the function, but like all the static object designs, this one immediately creates our doubts about multithreading security. And if you have the following code:
operator== (rationalrational& RHS); A, B, C, D; ( (a*b) = = (C*d)) {}else{}
The IF condition in the above code is always true because A*b returns a reference to a static object, and C*d returns a reference to the same static object, so it is always equal.
So, our final choice is to return the new object through Pass-by-value.
const rational operator* (rational< Span style= "color:black;" >& const rational& rhs) {return rationallhs.n*rhs.N, lhs.d*rhs.d);
Article 22: Declaring a member variable as private
The first is the consistency of the code (no member or function is considered when calling the public member).
Second, encapsulation, which is written as a function, provides the possibility to modify the access method later, without affecting the method of use. In addition, public affects all users, and protected affects all inheritors, so it is not recommended to declare member variables.
Remember to declare the member variable as private. This gives the customer access to data consistency, fine-grained access control, assurance of promise conditions, and the availability of class authors to fully achieve resiliency.
Protected is not more encapsulated than public.
Clause 23: Prefer to replace the member function with Non-member and non-friend
Imagine a class to represent a Web browser. Among the many functions that this class might provide, there is a cache area (cache of downloaded elements) that clears the download element, clears the history of URLs that have been visited, and removes all cookies in the system.
Webbrowse{public: removecookies ();};
Many users want a function to perform the entire action, because some webbrowse also provide such a function:
Webbrowse{public: cleareverything (); Call ClearCache (), ClearHistory (), Removecookies ()} in turn;
Of course this function can also be done by a non-member function:
Cleareverything (WEBBROWSE WB WB WB WB. removecookies ();}
So which one is better?
According to the object-oriented code requirements, the data and the functions of the operation should be bundled together, which means that the member function is a better choice. Unfortunately, this suggestion is not correct. Object-oriented requirements data should be encapsulated as much as possible.
The member function is less closed than the Non-member function because it does not increase the number of functions that "have access to the private component within class."
In addition, the Non-member function can be provided to allow greater flexibility for WEBBROWSE related functions, which eventually leads to lower compilation dependencies and increases the extensibility of webbrowse.
If we design webbrowse-related functions as Non-member functions, then the declaration of function-related functions can be placed in a single header file, and then under a namespace. At this point, if we extend these functions, we just need to put the declaration into the header file like any other function function.
This method of cutting does not apply to class member functions because a class must be defined as a whole and cannot be cut into fragments.
Please remember
Rather take the Non-member non-friend function to replace the member function. This can increase encapsulation, package elasticity, and functional extensibility.
Clause 24: If all parameters require type conversion, take the Non-member function for some
When we design an overloaded function of a multiplication operator for a rational number class, if we use it as a member of the class:
Rational{public: operator* (&lhs);};
When we try to mix arithmetic, you'll find that only half of it makes sense:
result, Onehalf;result = onehalf * 2;result = 2 * onehalf;
The second assignment statement above is wrong because it is equivalent to result=2.operator* (Onehalf), which is obviously wrong.
The 2nd statement is equivalent to Result=onehalf.operator (2), which can be executed successfully because 2 has an implicit type conversion because rational has a non-explicit constructor that accepts int parameters.
So, if we want to do the above, we need to design this overloaded function as the Non-member function.
Operator* (rational& RHS);
This function must be a non-member if you need to convert all parameters of a function, including the metaphorical parameter pointed to by the this pointer.
Article 25: Consider writing a swap function that does not throw an exception
1, when Std::swap is not efficient for your type, provide a swap member function and make sure that the function does not throw an exception.
2, if you provide a member swap, also should provide a non-member swap to call the former, for classes (not templates), please also special std::swap.
3, you should use the using declaration for Std::swap when you call swap, and then call swap without any namespace qualification adornments.
4, for the "user-defined type" of the STD template full specificity is good, but do not try to include in the STD some new things for Std.
"Effective C + +" design and declaration