"Effective C + +" study notes-clause 28

Source: Internet
Author: User

*************************************** Reprint Please specify the Source: Http://blog.csdn.net/lttree ********************************************





Wu, implementations




Rule 28:avoid Returning "handles" to object internals

Rule 28: Avoid returning handles points to the inner component of the object




Suppose our program involves a rectangle. Each rectangle is represented by its upper-left and lower-right corners. In order for a rectangle object to be as small as possible, you might decide not to place these points of the defined rectangle in the rectangle object, but instead put them in an auxiliary struct and let rectangle refer to it:

    class Point {//Description Public:point (int x, int y), ... void SetX (intnewval); void Sety (Intnewval); ...}; struct Rectdata  {    //Use these "dot" data to represent a rectangular point ulhc;    Upper left corner point LRHC;    Lower right corner};class Rectangle  {...private:std::tr1::shared_ptr<rectdata> pData;};


To calculate the range of rectangle, this class provides upperleft functions and lowerright functions because point is a user-defined function, so these functions return reference, representing the underlying pointer object:

Class Rectangle  {public: ... Point&upperleft () const {  returnpdata->ulhc;} Point&lowerright () const {  returnpdata->lrhc;} ...};


Although this design can be compiled, it is wrong. In fact, it's self-contradictory .

①upperleft and Lowerright are declared as const member functions because they are intended only to provide the client with a way to know the coordinates of the rectangle, rather than letting the customer modify the rectangle;

② Two functions return reference point to the private internal data, and the caller can then change the internal data through these reference.

An obvious example:

Point Coord1 (0,0); Point Coord2 (100,100); Const Rectangle REC (COORD1,COORD2);    Define the Matrix Rec, upper left corner (0,0), lower right Corner (100,100) Rec.upperleft (). SetX (    ). Change Rec to upper left corner (50,0), lower right corner (100,100)


Obviously, Upperleft callers can use the returned reference to change the members. But Rec should actually be immutable.

The above example tells us two points:

? The encapsulation of a member variable is at most equal to the access level of the function that returns its reference.

? If the const member function is out of a reference, and the data is associated with the object itself, and it is stored outside the object, the caller of the function can modify that data. This is a side result of bitwise constness.

Everything we said above is due to the "member function returning reference". If they are returning pointers or iterators, the same thing will happen for the same reason--reference, pointers, and iterators are all so-called handles, and returning a handle that "represents the object's internal data" comes with the risk of "reducing the encapsulation of objects."

PS: The "interior" of an object that we generally consider to refer to is its member variable, but a member function (that is, protected or private) that is not publicly used is also part of the object "inside".

The solution: "The return pointer points to a member function" is not uncommon, so let's focus on the rectangle class and its upperleft and Lowerright member functions. Two of the problems we encounter with these functions can be easily removed by simply adding a const to their return type:

Class Rectangle  {public:...const point& upperleft () const {return pdata->ulhc;  } Const point& Lowerright () const {return pdata->lrhc;  } ...};


After this change, the user can only read the rectangle's points, but cannot modify them. Therefore, the original upperleft and upperright as a const is no longer empty shell. As for encapsulation, because we are willing to let users see rectangle's peripheral points, so here deliberately relaxed encapsulation.
However, even so, Upperleft and Lowerleft returned to the "inside of the object" handles, which is likely to cause problems in other situations. More accurately, it may lead to dangling handles (the empty number plate): The object of this handles is no longer present. The most common source of this "nonexistent object" is the function return value. For example, a function returns the bounding box of a GUI object, which takes a rectangular form:
<span style= "FONT-FAMILY:KAITI_GB2312;FONT-SIZE:14PX;" >class guiobject  {...}; Const Rectangle BoundingBox (const guiobject& obj);    Returns a rectangle by the value method </span>


now, it is possible for the user to use this function:
<span style= "FONT-FAMILY:KAITI_GB2312;FONT-SIZE:14PX;" >guiobject* PGO;    Let PGO point to a guiobject...const point* Pupperleft = & (BoundingBox (*PGO). Upperleft ());    Get a pointer to the top left point of the box </span>


? The call to BoundingBox gets a new, temporary rectangle object. This object temporarily calls it temp (for the convenience of the following narrative). The upperleft then acts on the temp body, returning a reference that points to the internal component of temp, more specifically to a points that indicates temp. So pupperleft points to that point object. So far everything is fine, but the story is not over, because after that statement, the return value of BoundingBox, which is what we call temp, will be destroyed, which indirectly leads to the destruction of the points within temp. Eventually lead to Pupperleft point to an object that no longer exists, that is, once the output pupperleft the end of the statement, Pupperleft will become empty suspension, virtual crane! ? This is why the function is always dangerous if "returning a handle represents the inner component of an object". Whether the so-called handle is a pointer or an iterator or a reference, and whether the handle is const or not, the member function that returns handle is const. The only key here is that a handle is passed out, which leads to the "handle may be longer than the object it refers to". ? but This does not mean that member functions should never be returned to handle, which sometimes has to be done. For example, operator[] allows you to obtain individual elements of strings and vectors, and these operator[]s return references points to "data within the container", and those data will be destroyed as the container is destroyed. Nonetheless, such a function is, after all, an exception, not the norm.


☆ Please remember ★Avoid returning handles (including references, pointers, iterators) to the inside of the object. Adherence to this clause increases encapsulation, helps the const member function behave like a const, and minimizes the likelihood of a "virtual lift number card" occurring.




*************************************** Reprint Please specify the Source: Http://blog.csdn.net/lttree ********************************************

"Effective C + +" study notes-clause 28

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.