Valid tive c ++ Item 28 do not return handles or internalshandles that point to the internal data (internals) of the object.

Source: Internet
Author: User

Valid tive c ++ Item 28 do not return handles or internalshandles that point to the internal data (internals) of the object.
 

Suppose you are operating on a Rectangle class. Each rectangle can be represented by a vertex in the upper left corner and a vertex in the lower right corner. To ensure that a Rectangle object is as small as possible, you may decide not to store the vertex defining the rectangular range in the Rectangle class, but to put it into an auxiliary struct, in Rectangle, declare a pointer to it:

 1 class Point {                           // class for representing points 2  3 public:                                   4  5 Point(int x, int y);                  6  7 ...                                           8  9 void setX(int newVal);          10 11 void setY(int newVal);          12 13 ...                                          14 15 };                                          16 17 18 19 struct RectData {                                       // Point data for a Rectangle20 21 Point ulhc;                                                 // ulhc = “ upper left-hand corner”22 23 Point lrhc;                                                 // lrhc = “ lower right-hand corner”24 25 };                                                              26 27 class Rectangle {                                      28 29 ...                                                              30 31 private:                                                    32 33 std::tr1::shared_ptr<RectData> pData;      // see Item 13 for info on34 35  36 37 };                                                                // tr1::shared_ptr

 

1. Two problems caused by returning the reference to the internal data of the object 1.1 Problem Analysis

Because the customers of Rectangle need to be able to know the range of a Rectangle, the class provides the upperLeft and lowerRight functions. However, Point is a custom type, so you need to pay attention to Item 20: for user-defined types, transferring by reference is more efficient than passing by value, these functions return references to the underlying Point object:

 1 class Rectangle { 2  3 public: 4  5 ... 6  7 Point& upperLeft() const { return pData->ulhc; } 8  9 Point& lowerRight() const { return pData->lrhc; }10 11 ...12 13 };

 

This design can be compiled, but it is wrong. In fact, it is self-contradictory. On the one hand, upperLeft and lowerRight are declared as const member functions, because they are only used to provide the customer with methods to know the points contained in the matrix, and do not allow the customer to modify the matrix (Item 3 ). On the other hand, both functions return a reference pointing to private internal data, which can be used by the caller to modify internal data! For example:

 1 Point coord1(0, 0); 2  3 Point coord2(100, 100); 4  5 const Rectangle rec(coord1, coord2); 6  7 // rec is a const rectangle from 8  9 10 // (0, 0) to (100, 100)11 rec.upperLeft().setX(50); // now rec goes from12 // (50, 0) to (100, 100)!

 

Note how the caller of upperLeft uses the returnedPointReference of Point data members in recTo modify this member. But rec is the const variable.

We can learn two points.First, the encapsulation of a data member is the same as that of a data member (Most accessible) member functions are consistent. In this case, although ulhc and lrhc are private For Rectangle, they are actually public, because the public functions upperLeft and lowerRight return references pointing to them.Second, ifThe const member function returns a reference to the data, and the data is stored outside the current object, so the caller of the function can modify the data.. (This is the incidental result Item 3 of bitwise constness limitations ).

1.2 Handle (handles)Not only references, but also pointers and iterators

All of the discussions are about returning referenced member functions. However, if they return pointers or iterators, the same problem may occur for the same reason. References, pointers, and iterators are both handles. Returning A handle pointing to the internal data of an object is often at risk of damaging the encapsulation type. The status of the object passed out from the const member function is also modified.

1.3 internal data (internals data) not only contains data members, but also includes member functions

We usually think that the "Internal data" of an object is only for data members, but non-public member functions are also part of the internal data of the object. Therefore, it is equally important to disable the return of the handle pointing to them. This means that you should never return a pointer to a function with lower access level from the member function. If you do this, the effective access level is the function with a higher access level, because the customer can obtain the pointer to the function with a lower access level, and then call this function through the pointer.

 

2. To solve the above two problems, add const for Reference

 

It is not common to return a function pointer to a member function. Let's focus on the Rectangle class and its upperLeft and lowerRight member functions. The two problems we found can be solved simply by adding const to the return value:

1 class Rectangle {2 public:3 ...4 const Point& upperLeft() const { return pData->ulhc; }5 const Point& lowerRight() const { return pData->lrhc; }6 ...7 };

 

With this modified design, you can read and define the points of a rectangle, but cannot modify them. This means that the upperLeft and lowerRight statements are no longer a lie because they no longer allow callers to modify the object state. For encapsulation problems, our intention is to let customers see the points that make up the rectangle, So we deliberately relaxed the encapsulation type. More importantly, this is a "relaxation" with limitations: these functions are read-only.

3. Returning the const reference will introduce new problems

Even so, upperLeft and lowerRight still return the handle pointing to the internal data of the object, which may cause other problems. In particular, it can cause pointer suspension:The handle pointing to some objects no longer exists.The most common cause of this object disappearance problem is the function returned by value. For example, a function returns a border box for a GUI object. The border box is represented by a matrix:

1 class GUIObject { ... };2 3 const Rectangle                                      // returns a rectangle by4 5 boundingBox(const GUIObject& obj);     // value; see Item 3 for why6 7 8 // return type is const

 

Now, if a customer uses this function:

1 GUIObject *pgo; // make pgo point to2 ... // some GUIObject3 const Point *pUpperLeft = // get a ptr to the upper4 &(boundingBox(*pgo).upperLeft()); // left point of its5 // bounding box

 

This call to boundingBox will return a new temporary Rectangle object. This object has no name, we call it temp. UpperLeft will be called on temp. This call returns a reference to the temp internal data, which is a Point of the rectangle. PUpperLeft will point to this Ponit object. So far, everything is normal, but it is not complete yet, because at the end of the declaration, the return value of boundingBox -- temp -- will be destroyed, which will indirectly cause the temp Point object to be destructed. In this way, pUpperLeft points to an object that no longer exists. pUpperLeft becomes a pointer when the statement to be created ends!

That's whyAny function that returns a handle pointing to the internal data of an object is dangerous. Whether the handle is a pointer, a function, or an iterator. Whether the function is declared as const or not. Whether or not the handle returned by the member function is const. The key to the problem is whether the function has a return handle, because once it is returned, there is a risk that the handle is longer than the object it points.

4. Exceptions

This does not mean that you should never let a member function return a handle. Sometimes you have to do this. For example, operator [] allows you to extract a single element from a string or vector. The operator [] returns a reference to the internal data of the container (Item 3) -- when the container is destroyed, the data in it is also destroyed. However, such a function is just an exception.

5. Summary

Avoid returning a handle (reference, pointer, or iterator) pointing to the internal data of the object ). Without returning a handle, the encapsulation can be enhanced to help the const member function behave as a real const, reducing the possibility of hanging pointers being created.

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.