Objective C ++ Reading Notes (14)

Source: Internet
Author: User

Cla23: Native replaces the member function with non-member and non-friend.

Prefer non-member non-friend functions tomember functions

Imagine a class that represents a web browser. Among the many functions provided by such a class, some functions are used to clear the download element cache, clear the accessed URL history, and remove all cookies from the system:

Class WebBrowser {
Public:
...
Void upload AchE ();
Void clearHistory ();
Void removeCookies ();
...
};

Many users want to execute all these actions together, so WebBrowser may also provide a function to do this:

Class WebBrowser {
Public:
...
Void clearEverything ();
// Callreceivache, clearHistory, and removeCookies
...
};

Of course, this function can also be provided through non-member functions to call appropriate member functions:

Void clearBrowser (WebBrowser & wb)
{Wb. receivache (); wb. clearHistory (); wb. removeCookies ();}

So which one is better? Does the member function clearEverything or the non-member function clearBrowser?

The object-oriented principle states that data and functions that operate on them should be bound together, and it is recommended that member functions be a better choice. Unfortunately, this suggestion is incorrect. The object-oriented principle indicates that data should be encapsulated as much as possible. What is not intuitive is that the member function clearEverything actually causes worse encapsulation than the non-member function clearBrowser. In addition, non-member functions are provided to allow greater packaging elasticity of WebBrowser-related functions, resulting in fewer compilation dependencies and enhanced WebBrowser scalability. Therefore, non-member functions are better than member functions in many aspects.

We will start with encapsulation. Encapsulation provides us with an elasticity to change things, but only affects limited customers. Considering the data in an object, the less code you can see the data (access to it), the more data encapsulation, the more freedom we have to change the object data. For example, number and type of data members. How can we measure how many codes can view data? We can calculate the number of functions that can access data: the more functions can access data, the weaker the Data encapsulation.

We have said that data members should be private, otherwise they are not encapsulated at all. For private data members, the number of functions that can be accessed is the membership function of the class plus the membership function, because only the Members and the membership function can access the private member. Assume that a member function (not only the private data of a class can be accessed, but also the private function, enumeration, typedefs, and so on) you can select between a non-member non-friend function that provides the same function (you cannot access those functions above) to obtain more encapsulated non-member non-friend functions. This explains why clearBrowser (a non-member non-friend function) is more desirable than clearEverything (a member function): it can gain stronger encapsulation for WebBrowser.

There are two things worth noting at this point. First, this argument applies only to non-member non-friend functions. You can access the private member of a class like a member function, thus affecting encapsulation. From the perspective of encapsulation, the choice is not between a member and a non-member function, but between a member function and a non-member function.

Second, making a function a non-member of a class because of the encapsulation does not mean that it cannot be a member of another class. This is a moderate comfort for programmers who are used to languages where all functions must belong (for example, Eiffel, Java, C #, etc. For example, we can make clearBrowser a static member function of a tool class. As long as it is not a part of WebBrowser (or a friend), it will not affect the encapsulation of private Members of WebBrowser.

In C ++, it is natural to make clearBrowser a non-member function in the same namespace as WebBrowser:

Namespace WebBrowserStuff {

ClassWebBrowser {...};
Void clearBrowser (WebBrowser & wb );
...
}

The namespace can span multiple source files, but the class cannot. This is very important, because functions similar to clearBrowser provide convenient functions. If they are neither members nor friends, they do not have special access rights to WebBrowser, so they cannot provide any function that WebBrowser customers cannot obtain in other ways. For example, if clearBrowser does not exist, the customer can directly call receivache, clearHistory, and removeCookies.

A class similar to WebBrowser can have a large number of convenience functions, some of which are related to bookmarks, some printing, and some are related to cookie management. Generally, most customers are only interested in some of them. There is no reason for a customer who is only interested in convenience functions related to bookmarks to depend on other functions during compilation. The straightforward way to separate them is to separate the header file declaration:

// Header "webbrowser. h"-targeting WebBrowser itself and its core functions
Namespace WebBrowserStuff {
Class WebBrowser {...};
... // Core functions, such as non-member functions used by everyone
}

// Header "webbrowserbookmarks. h"
Namespace WebBrowserStuff {
... // Convenient functions related to bookmarks
}

// Header "webbrowsercookies. h"
Namespace WebBrowserStuff {
... // Convenient functions related to cookies
}

...

This is exactly how the C ++ standard library is organized. The standard library does not have a single, large <C ++ StandardLibrary> header file and contains everything in std namespace. They are in many header files (for example, <vector>, <algorithm>, <memory>, etc.), each of which declares some functions of std. This allows the customer to compile only the part of the system they actually use. When a function comes from a member function of a class, it is impossible to use this method to split it, because a class must be defined as a whole and cannot be split apart.

Put all convenience functions into multiple head files, but belong to a namespace, which means that the customer can easily expand the set of convenience functions, all you need to do is add more non-member non-friend functions to the namespace. For example, if a WebBrowser client decides to write a convenient function for downloading images, you only need to create a new header file, including the declarations of those functions in WebBrowserStuff namespace, this new function is now available and integrated just like other convenience functions. This is another feature that a class cannot provide, because the class definition cannot be extended for customers. Of course, the customer can derive a new class, but the derived class cannot access the encapsulated (private) member in the base class. Therefore, such an "Extended Function" is only a secondary identity.

· Replace Member functions with non-member non-friend functions. This can improve encapsulation, packaging elasticity, and functional expansion.

 

Clause 24: If all parameters require type conversion, use the non-member function for this purpose.

Declare non-member functions when typeconversions shocould apply to all parameters

It is usually a bad idea to allow a class to support implicit type conversion. Of course, there are some exceptions to this rule. The most common one is when creating a value type. For example, if you design a class to express rational numbers, it does not seem unreasonable to allow implicit conversions from Integers to rational numbers:

Class Rational {
Public:
Rational (int numerator = 0, int denominator = 1 );
// Non-explicit, allows int-to-Rational implicit conversion of int-to-Rational
Int numerator () const; // access function of the numerator and denominator
Int denominator () const;

Private:
...
};

Arithmetic operations, such as addition and multiplication, should be supported, but it cannot be determined whether they are implemented through member functions, non-member functions, or non-member friend functions. When you are not sure, you should stick to the object-oriented principle. Therefore, the multiplication of Rational numbers is related to Rational classes. Therefore, it seems more normal to implement Rational operators * in the Rational class. Let's first turn operator * into a Rational member function:

Class Rational {
Public:
...
Const Rational operator * (const Rational & rhs) const;
};

Rational oneEighth (1, 8 );

Rational oneHalf (1, 2 );

Rational result = oneHalf * oneEighth; // fine

Result = result * oneEighth; // fine

This design allows you to multiply Rational numbers without any effort, but you also want to support mixed-mode operations so that Rational can be multiplied by other types (such as int. After all, it is normal to multiply two numbers even if they happen to be different types of values.

Result = oneHalf * 2; // fine

Result = 2 * oneHalf; // error!

Only half of them work, but multiplication must be interchangeable. When the two substatements are rewritten in the form of corresponding functions, the problem is clearly indicated:

Result = oneHalf. operator * (2 );

Result = 2. operator * (oneHalf );

The object oneHalf is a class instance containing operator *, so the compiler calls that function. However, integer 2 does not have the operator * member function. The compiler also looks for non-member operator * that can be called as follows (that is, operator * in the namespace or global scope *):

Result = operator * (2, oneHalf );

However, in this example, no non-member accepts the int and Rational operator * functions, so the search fails. Let's look at the successful call. Its second parameter is Integer 2, while Rational: operator * holds a Rational object as its parameter. Implicit type conversion occurs here. The compiler knows that you pass an int, And that function requires a Rational. by calling the Rational constructor with the int you provide, they can make a matching Rational. In other words, they think of that call more or less as follows:

Const Rational temp (2); // create a temporary Rational object based on 2

Result = oneHalf * temp; // equivalent to oneHalf. operator * (temp );

Of course, the compiler only provides a non-explicit it constructor. If the Rational constructor is explicit, the two statements cannot be compiled, but at least the statements are consistent.

 

One of the two statements can be compiled, but the other is that it is eligible for implicit type conversion only when the parameter series are in the parameter list. Now the method that supports mixed operations may be clear: let operator * be used as a non-member function, so implicit type conversion is allowed to be applied to all parameters:

Class Rational {... // does not include operator *};
Const Rational operator * (const Rational & lhs, const Rational & rhs)
{Return Rational (lhs. numerator () * rhs. numerator (),

Lhs. denominator () * rhs. denominator ());}
Rational oneFourth (1, 4 );
Rational result;
Result = oneFourth * 2; // fine
Result = 2 * oneFourth; // it works!

In addition, just because a function should not be a member does not automatically mean it should be a friend.

· If you need to use type conversion on all parameters of a function (including those directed by this pointer), this function must be a non-member.

 



From pandawuwyj's column

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.