article 22: Declaring a member variable as privateThe idea of explaining this article is to first see why the member variable should not be public, the same idea applies to the protected member variable, and finally concludes that the member variable should be private. start with the syntax consistency first (clause 18), if the member variable is not public, the only way the client accesses the member is through the member function (if there is no friend function). If everything inside the public interface is a function, when the client is using this object, there is no need to question whether it is accessing the variable or the function, because the member variable cannot be accessed at this time. Perhaps consistency is not the reason for convincing you. Another reason: the use of functions allows you to have more precise control over the handling of member variables. If the member variable is public, then everyone can read and write, but if the function reads or writes its value, then it can achieve "no access", "read-only access" and "read-write access", and even implement "write access", which is a bit like get, set in C #.
Class Accesslevels{public:......int Getreadonly () const {return readOnly;} void Setreadwrite (int value) {Readwrite=value;} int Getreadwrite () Const{return readWrite;} void setwriteonly (int value) {Writeonly=value;} Private:int noaccess;//No access action int readonly;//can read int readwrite;//can read can write int writeonly;//can only write};
If the above reasons are not enough, there is one more important reason: encapsulation. If you access a member variable through a function, you can later replace the variable with a calculation, and the customer of class does not know that the internal implementation has changed.
For example, you are writing an automatic Velocimetry program, and when the car passes, its speed is filled into a speed collector:
Class speeddatacollection{......public:void AddValue (int speed);//Add a new data double Averagesofar () const;//return the average velocity ...};
Now consider how to implement the function Averagesofar. One approach is to design a variable within class to record the average of all the velocities so far; when Averagesofar is called, it's just a good idea to return that member variable. Another option is to have Averagesofar recalculate the average every time it is called, and this function has permission to read every speed value in the collector.
The first of these practices (to maintain an average at any time) makes each Speeddatacollection object larger, since it is necessary to allocate space for each member variable that holds the current mean, cumulative total, and number of data points; but this makes averagesofar very efficient, It can be just an inline function that returns the current average (clause 30). The second approach, "Calculate average per query" will cause Averagesofar to perform slower, but then the Speeddatacollection object takes up less space.
The specific approach is better, depending on the situation. The second approach is appropriate in memory-constrained machines, such as embedded devices, or in applications that do not need to calculate averages frequently. But in an application where averages are frequently needed, the first approach is better if the speed of reaction is important and memory is not a factor. Both of these implementations use functions to access the average (that is, encapsulate it), and you can replace different implementations, with the client having to recompile at most. (If you follow clause 31, you don't even need to recompile)
Hiding member variables behind the function interface gives you the flexibility to "all possible implementations." For example, this makes it easy to notify other objects when a member variable is read or written, a constraint that validates a class, and the precondition and post-state of a function, and can perform synchronization control in a multithreaded environment ... Wait a minute.
The importance of encapsulation may be more important than you think. If you hide member variables (encapsulate them) against your customers, you can ensure that the class constraints are maintained and that you retain the right to implement future changes. If not encapsulated, the ability to change public things later is extremely binding because modifying the public variable affects too many customer code. The encapsulation of protected members appears to be higher than public, but this is not the case, modifying protected member variables, how many derived classes need to be modified, or how much customer code that uses derived objects needs to be modified.
In clause 23, you will see that "the encapsulation of something" is proportional to "the amount of code damage that may result when the content changes in the current period." Once a member variable is declared public or protected, it can be difficult to change everything involved in that member variable. Because too much code needs to be rewritten, re-tested, re-written, and recompiled. From a package perspective, there are only two access rights: private (encapsulated) and others (not encapsulated).
Summarize:
1. Declare the member variable as private. This can give the customer access to data consistency, fine-grained access control, allow constraints to be guaranteed, and provide class authors with full flexibility.
2, protected is not more encapsulation than public.
clause 23: Prefer to replace the member function with Non-member and Non-friend
This article explains the difference between a member function and a friend function.
Consider a class to clear the browser of some records, this class has clear to tell the buffer function, there is clear access to the URLs of the function, as well as the function of clearing cookies:
Class Webbrowser{public:......void Clearcash (), void ClearHistory (), void Removecookies (), ...};
in general, you need to perform these three actions at the same time, so WebBrowser can provide such a function:
Class Webbrowser{public:......void cleareverything () {Clearcash (); ClearHistory (); Removecookies ();} ......};
Another approach is to call the appropriate member function with a non-member function
void Clearbrowser (webbrowser& wb) {Wb.clearcash (); Wb.clearhistory (); Wb.removecookies ();};
Which of the above two methods is better? The answer is that the Non-member function is better.
The object-oriented thought requires that the data be encapsulated as much as possible, and the encapsulation of member function cleareverything is lower than the Non-member function Clearbrowser. The Non-member function provides a large package elasticity (packaging flexibility) for class-related functions, resulting in a lower compilation dependency and increased class extensibility.
Encapsulation means not visible. The more things are encapsulated, the less people can see it, the greater the elasticity we have to change it. The less code can see the data (accessing the data), the more data can be encapsulated, the more freedom we have to change the object data. The more functions can access it, the lower the encapsulation of the data.
Clause 22 It is said that the member variable should be private, otherwise there will be an infinite number of functions can access it, there is no encapsulation to say. Functions that can access private member variables are only class member functions, friend functions. Make a choice between a member function and a non-member, non-friend function, and if both provide the same function, it is obvious that the latter provides a larger package, which is why the Clearbrowser function is selected above.
There are two points to be aware of in this package. 1, this exposition only applies to Non-member, Non-friend function. 2, because the encapsulation, let the function become class's Non-member function, but this does not mean that it can not be another class's member function.
In C + +, to achieve the above functions, it is more natural to put the Clearbrowser function and the WebBrowser class in a namespace:
Namespace Webbrowserstuff{class webbrowser{...}; void Clearbrowser (webbrowser& we), ...}
It's not just looking neat. Namespace can span multiple source files, class cannot. Functions like Clearbrowser are just for convenience, it is non-member, Non-friend, no special access rights to WebBrowser. A class like WebBrowser may have a number of convenience functions, such as some related to bookmarks, some related to printing, some to cookies .... Usually customers use it when they are only interested in some of them. They are usually separated during encoding: the bookmark-related convenience function is declared in a header file, the cookie-related function is declared to another header file, and the print-related function is declared to the third header file ....
Header file Webbrowser.h, this header file for class WebBrowser itself and WebBrowser core functions namespace Webbrowserstuff{class webbrowser{......};//core functions ... Non-member function}//header file Webbrowserbookmarks.hnamespace webbrowserstuff{......//convenience function related to bookmarks}// Header file Webbrowsercookies.hnamespace webbrowserstuff{......//convenience function related to cookies}
This is how the C + + standard library is organized. The standard library has dozens of header files (<vector>,<algorithm>,<memroy>, etc.), and each header file declares some of the functions of Std. If the customer wants to use vector-related functions, only #include<vector> is required. This also allows customers to build dependencies on only a small portion of their use (clause 31, which discusses other practices that reduce compilation dependencies).
Putting all the convenience functions in multiple folders but under the same namespace means that the customer can easily extend this set of convenience functions, all they have to do is add more non-member functions and non-friend functions to the namespace, which is something that class cannot do. Of course the customer can inherit the class to extend the new class, but the derived class cannot access the private members encapsulated in the base class, so the extended function has only secondary identities.
Summary: Replace the member function with the Non-member and Non-friend functions, which can increase encapsulation, wrapping elasticity and functional extensibility.
Clause 24: If all parameters require type conversion, use the Non-member function for this
In general, class should not support implicit type conversions, as this can lead to unexpected problems. There are exceptions to this rule, the most common exception being when building a numeric type. For example, write a fractional management class that allows implicit type conversions
Class rational{public:rational (int numerator=0, int denominator=1),//non-explicit, allow implicit conversion ...};
If you want to support operations such as subtraction, are overloaded operators supposed to be overloaded with member functions or Non-member functions, or non-member friend functions?
If you write the member function
Class Rational{public:......const Rational operator* (const rational& RHS);
This allows the multiplication of two rational numbers
Rational oneight (1,8); Rational onehalf; Rational Result=oneight*onehalf;result=result*oneight;
if the blending operation
result=onehalf*2;//correct, equivalent to onehalf.operator* (2), result=2*onehalf;//Error, equivalent to 2.operator* (onehalf);
Cannot satisfy the Exchange law. Because 2 is not a rational type, it cannot be used as the left operand. ONEHALF*2 will convert 2 implicitly to rational type.
In both of these approaches, the first can occur implicitly, and the second is not, because only if the parameter is listed in the parameter column (parameter list), this parameter is the qualified participant of the implicit type conversion. The second approach, not yet to "parameter is listed in the parameter column", 2 is not a rational type, does not call operator*.
If you want to support blending operations, you can make operator* a non-member function so that the compiler can perform implicit type conversions on the arguments.
Const Rational operator* (const rational& LHS, const rational& RHS);
This allows for mixed operations. Then there is the question of whether operator* should be a friend function. If you can get the internal data through the public interface, then it is not a friend function, otherwise, if you read private data, then you have to become a friend function. Here's another important conclusion: the inverse of the member function is the Non-member function, not the friend function. If you can avoid being a friend function, it is best to avoid it, because friend's encapsulation is lower than non-friend.
When you need to consider a template, there are some new solutions when you change class to class template. This is mentioned in clause 46 below.
Summary: If a type conversion is required for all parameters of a function, including the metaphor parameter pointed to by this pointer, the function must be a non-member function
"Effective C + +" resource management: clause 22-clause 24