Objective C ++, 3rd edition, item 28: avoid returning the "handles" ("handle") of the internal component of the object ")

Source: Internet
Author: User

Item 28: avoid returning the "handles" ("handle") of the internal component of the object ")

By Scott Meyers

Translator: fatalerror99 (itepub's nirvana)

Release: http://blog.csdn.net/fatalerror99/

Suppose you are working on a rectangle-related application. Each rectangle can be expressed in the upper left corner and lower right corner. To keep a rectangle object in a small State, you may decide which points in the rectangle area should not be stored in the rectangle itself, A more suitable method is to place it in a secondary structure directed by rectangle:

Class Point {// class for representing points
Public:
Point (int x, int y );
...

Void setx (INT newval );
Void sety (INT newval );
...
};

Struct rectdata {// point data for a rectangle
Point ulhc; // ulhc = "upper left-hand corner"
Point lrhc; // lrhc = "lower right-hand corner"
};

Class rectangle {
...

PRIVATE:
STD: tr1: shared_ptr <rectdata> pdata; // see item 13 for info on
}; // Tr1: shared_ptr

Because rectangle customers need to be able to manipulate a rectangle region, this class provides the upperleft and lowerright functions. However, point is a user-defined type, so pay attention to item 20's, the user-defined type (user-defined type) is more efficient than the value passing method. These function returns a reference to the underlying point objects:

Class rectangle {
Public:
...
Point &Upperleft () const {return pdata-> ulhc ;}
Point &Lowerright () const {return pdata-> lrhc ;}
...
};

This design can be compiled, but it is incorrect. In fact, it is self-contradictory. On the one hand, upperleft and lowerright are declared as const member functions (member functions) because they are designed to only provide customers with a method to obtain rectangle points, the customer is not allowed to change the rectangle (see item 3 ). On the other hand, both functions return a reference that leads to private internal data (private internal data)-the caller can be used to modify references to those internal data! For example:

Point coord1 (0, 0 );
Point coord2 (100,100 );

ConstRectangle Rec (coord1, coord2); // REC is a const rectangle from
// (0, 0) to (100,100)

Rec. upperleft (). setx (50 );// Now rec goes from
//(50, 0) to (100,100 )!

Note: here, how can the upperleft caller change this member by referencing the returned data to the point data members (data member) in rec. But REC is assumed as const!

This leads to two lessons. First, a data member (data member) can only be encapsulated into a function with the highest accessible level to return a reference to it. In the current situation, although ulhc and lrhc are considered private by their rectangle (the original text here is incorrect and modified based on the author's website ), but they are still publicly available, because the public functions upperleft and lowerright return references to them. Second, if a const member function (member function) returns a reference to a data that is related to an object and stored outside of the object itself, the caller of this function can change that data (which is a side effect of bitwise constness (Binary bit constants) (see item 3 ).

Everything we have done involves returning referenced member functions (member functions). However, if they return pointers or iterators, the same problem also exists for the same reason. References, pointers, and iteratorsHandles(Handle) (the method that holds other objects), while the handle (handle) of Internals (internal component) that returns an object always faces the risk of endangering the object's encapsulation (encapsulation. As we can see, it also allows const member functions (member function) to change the state of an object.

We usually think that the "internals" ("internal component") of an object is its data members (data member), but it cannot be accessed by the general public) (that is, it is protected or private) is also part of the internals (internal component) of the object. Similarly, it is important not to return their handles (handles. This means that you should never have a member function (member function) to return a pointer to a member function (member function) with a small accessible level. If you do this, its effective accessible level will be the same as the function with a large accessible level, because the customer can get a pointer to this function with a small accessible level, then the user can call this function through this pointer.

However, it is rare to return the pointer to member functions (member function), so let's focus on returning the rectangle class and its upperleft and lowerright member functions (member function). All the problems we have picked out in these functions can be eliminated simply by using const for their return types:

Class rectangle {
Public:
...
ConstPoint & upperleft () const {return pdata-> ulhc ;}
ConstPoint & lowerright () const {return pdata-> lrhc ;}
...
};

With this modified design, you can read the points that define a rectangle, but they cannot write them. This means that the upperleft and lowerright statements (the original text here is incorrect, according to the author's website; the Translator's note) are declared as const, which is no longer a blank sentence because they no longer allow callers to change the object state. As for the encapsulation (encapsulation) problem, we always intentionally let the customer see the points that constitute a rectangle, so this is a deliberate relaxation of encapsulation (encapsulation. More importantly, it isLimited(Limited) Relaxation: only read access is permitted by these functions, and write access is still prohibited.

Even so, upperleft and lowerright still return the handles (handle) of an object's Internals (internal component), which may be problematic in other aspects. In particular, this causesDangling handles(Empty handle): refers to the handles (handle) of a component that no longer exists in objects ). The most common source of this disappearing objects is the function return value. For example, consider a function that returns the bounding box of a GUI object in the form of a rectangle:

Class guiobject {...};

Const rectangle// Returns a rectangle
Boundingbox (const guiobject & OBJ); // value; see item 3 for why
// Return type is const

Now, consider how customers may use this function:

Guiobject * PGO; // make PGO point
... // Some guiobject

Const point * pupperleft = // get a PTR to the upper
&(Boundingbox (* PGO). Upperleft (); // left point of its
// Bounding box

A new temporary rectangle object is returned when you call boundingbox. This object has no name, so let's call itTemp. So upperleft is inTempIs called, this call returns a leadTempIn particular, it is composed of points. Then pupperleft points to this point object. So far, everything is normal, but we cannot continue, because at the end of this statement, the return value of boundingbox --Temp-- It is destroyed, which indirectly leadsTempThe analysis structure of points. Next, the remaining pupperleft points to an object that no longer exists; pupperleft hangs at the end of the statement for creating it!

This is why any handle (handle) function that returns the internal component of an object is dangerous. This has nothing to do with whether the handle is a pointer, a reference, or an iterator. It has nothing to do with whether it is subject to cosnt restrictions. This has nothing to do with whether the handle (handle) returned by the member function (member function) is a const. All of the problems are that a handle is returned, because once this is done, you are faced with the risk that this handle is longer than the object it references.

This does not mean that you should never have a member function (member function) return a handle (handle ). Sometimes you must. For example, operator [] allows you to extract separate elements from strings and vectors, and these operator [] S is directed to containers (container) through the return) data Reference in (see itme 3) -- when the containers (container) itself is destroyed, the data will also be destroyed. However, such a function is a special case rather than a convention.

Things to remember

  • Avoid returning the handles (handle) (reference, pointer, or iterator) of the object Internals (internal component of the object ). This will improve encapsulation (encapsulation), help const member functions (member function) to produce cosnt effect, and minimize the possibility of dangling handles (empty hanging handle.

 

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.