Objective C ++, 3rd edition, item 13: use objects to manage resources

Source: Internet
Author: User

Item 13: use objects to manage resources

By Scott Meyers

Translator: fatalerror99 (itepub's nirvana)

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

Suppose we work with a library of simulated investments (such as stocks and bonds), and various investment forms are inherited from a root class (root class) Investment:

Class investment {...}; // root class of hierarchy
// Investment Types

Let us further assume that this library provides us with the method of the specific ststment objects (object) through a factory function (factory function) (see item 7:

Investment * createinvestment (); // return PTR to dynamically allocated
// Object in the investment hierarchy;
// The caller must delete it
// (Parameters omitted for simplicity)

As pointed out in the comment, when the object (object) returned by the createinvestment function is no longer used, the caller of createinvestment is responsible for deleting it. Then, consider writing a function f to fulfill this role:

Void F ()
{
Investment * pinv = createinvestment (); // call factory function
... // Use pinv
Delete pinv; // release object
}

This seems okay, but there are several situations where f fails to delete the investment object (object) it obtained from createinvestment. There may be a return statement in advance somewhere in the "..." section of this function. If such a return is executed, the control process will no longer be able to reach the delete statement. A similar situation may also occur if createinvestment is used in a loop with delete, and this loop exits early with a continue or GOTO statement. Also, some statements in "..." may throw an exception ). In this case, the control process will not reach that Delete. No matter how the delete is skipped, what we leak is not only to accommodate the memory of the investment object, but also to include any resources held by the object.

Of course, careful programming can prevent such errors, but considering that the Code may change over time. To maintain the software, some may add a return or continue statement without fully grasping the impact of other parts of the function's resource management policy. What's more, the "..." part of F may call a function that is never used to throw an exception, but it suddenly does so after it is "Improved. Trust F can always reach its Delete statement.

To ensure that the resources returned by createinvestment can always be released, we need to put those resources into an object (object) and destructor (destructor) of this object (object) resources are automatically released when the control process leaves F. In fact, this is only half of the thinking behind this item: By putting resources inside the objects (object, we can rely on C ++'s automatic destructor Invocation (automatic destructor call) to ensure that resources are released. (We will discuss the other half of the idea of this item later .)

Most resources are dynamically allocated (dynamically allocated) on the stack, used in a separate block or function, and should be removed from that block in the control flow) or function is released. The auto_ptr of the standard library is tailored to this situation. Auto_ptr is a pointer-like object (class pointer object) (Smart pointer(Smart pointer), its destructor (destructor) automatically calls delete on the things it points. The following describes how to use auto_ptr to prevent F's potential resource leakage:

Void F ()
{
STD: auto_ptr <investment>Pinv (createinvestment (); // call Factory
// Function
... // Use pinv
// Before
} // Automatically
// Delete pinv
// Auto_ptr's dtor

This simple example demonstrates two important aspects of using objects to manage resources:

  • After obtaining the resource, hand it over to resource-management objects (resource management object ).Previously, the resources returned by createinvestment were used to initialize the auto_ptr to be used to manage it. In fact, the concept of using objects to manage resources is often called because it is so common to obtain a resource and initialize a resource-management object in the same statement.Resource Acquisition is initialization(RaiI ). Sometimes the obtained resources are assigned to resource-management objects instead of initializing them, however, both methods hand over the resource to a resource-management object immediately when obtaining the resource ).
  • Resource-managing objects (resource management objects) Use their Destructors (destructor) to ensure that resources are released.Because Destructors is automatically called when an object (object) is destroyed (for example, when an object (object) leaves its activity range ), resources are correctly released no matter how the control process leaves a block. If the action to release the resource will cause the exceptions (exception) to be thrown, things will become tricky, but that is what item 8 describes, so we don't have to worry about it here.

Because when an auto_ptr is destroyed, it will automatically delete what it points to, so it is very important not to point more than one auto_ptr to an object. If this happens, the object will be deleted more than once, and your program will enter undefined behavior (undefined behavior) through shortcuts ). To prevent such problems, auto_ptrs has an unusual feature: copying them (using copy constructor (copy constructor) or copy assignment operator (copy assignment operator )) is to leave them empty, and the copied pointer gets the unique ownership of the resource.

STD: auto_ptr <investment> // pinv1 points to
Pinv1 (createinvestment (); // object returned from
// Createinvestment

STD: auto_ptr <investment> pinv2 (pinv1); // pinv2 now points to
// Object; pinv1 is now null

Pinv1 = pinv2; // now pinv1 points to
// Object, and pinv2 is null

This strange copy behavior increases the potential demand, that is, resources managed through auto_ptrs must never point to them more than one auto_ptr, this means that auto_ptrs is not the best way to manage all dynamically allocated resources (resources are dynamically allocated. For example, STL containers (container) requires that its content can exhibit "normal" copying behavior, so auto_ptrs containers are not allowed.

Compared with auto_ptrs, another optional solution isReference-counting smart pointer(Rcsp) (reference counting smart pointer ). An rcsp is a continuous tracking of how many objects (objects) point to a specific resource, and can automatically delete its smart pointer (smart pointer) when there is no longer anything pointing to that resource ). In this case, rcsps provide behavior similar to garbage collection. However, unlike the garbage collection, rcsps cannot break through circular references (for example, two objects (objects) without other users direct to each other ).

Tr1: shared_ptr of tr1 (see item 54) is an rcsp, so you can write F as follows:

Void F ()
{
...

STD: tr1: shared_ptr <investment>
Pinv (createinvestment (); // call factory function
... // Use pinv as before
} // Automatically delete
// Pinv via shared_ptr's dtor

The code here looks almost the same as the code using auto_ptr, but copying shared_ptrs is more natural:

Void F ()
{
...

STD: tr1: shared_ptr<Investment> // pinv1 points to
Pinv1 (createinvestment (); // object returned from
// Createinvestment

STD: tr1: shared_ptr<Investment> // both
Pinv1 and pinv2 now
Pinv2 (pinv1); // point to the object

Pinv1 = pinv2; // Ditto-nothing has
// Changed
...
} // Pinv1 and pinv2 are
// Destroyed, and
// Object they point to is
// Automatically deleted

Because copying tr1: shared_ptrs works "as expected", they can be used in STL containers (containers) and other environments that are incompatible with the unorthodox copy behavior of auto_ptr.

However, do not misunderstand that this item is not about auto_ptr, tr1: shared_ptr or any other type of smart pointer (smart pointer. It is about the importance of using objects to manage resources. Auto_ptr and tr1: shared_ptr are only examples of objects (objects) that do these tasks. (For more information about tr1: shared_ptr, see item 14,18 and 54 .)

Auto_ptr and tr1: shared_ptr both use delete in their Destructors instead of Delete []. (Item 16 describes the differences between the two .) This means that using auto_ptr or tr1: shared_ptr for dynamically allocated arrays (dynamically allocating arrays) is a bad idea. However, unfortunately, you can compile it:

STD: auto_ptr<STD: String> // bad idea! The wrong
APS (NewSTD: String[10]); // Delete form will be used

STD: tr1: shared_ptr<Int> SPI (NewInt[1024]); // Same problem

You may be surprised to find that C ++ does not have anything similar to auto_ptr or tr1: shared_ptr that can be used in dynamically allocated arrays (Dynamic Allocation array), or even tr1. That's because vector and string can almost always replace dynamically allocated arrays (Dynamic Array allocation ). If you still think there is a better classes (class) Like auto_ptr and tr1: shared_ptr that can be used for arrays, you can check boost (see item 55 ). There, you will find boost: scoped_array and boost: shared_array to provide the actions you are looking.

This item's guidance on using objects (object) to manage resources indirectly indicates that if you manually release resources (for example, using Delete instead of in a resource-managing class) you are looking for trouble. Pre-canned resource-managing classes (pre-made resource management class) such as auto_ptr and tr1: shared_ptr usually make it easy to follow the recommendations of this item, but sometimes you use a resource, these pre-fab classes cannot do what you want. In this case, you need to carefully create your own resource-management classes ). That is not terrible, but it contains some nuances that require your careful consideration. The theme that needs to be considered is item 14 and item 15.

As a final comment, I must point out that the raw pointer (bare pointer) returned by createinvestment is a request for resource leak (resource leakage, this is because it is too easy for callers to call delete on their retrieved pointers. (Even if they use an auto_ptr or tr1: shared_ptr to perform the delete operation, they must still remember to store the returned values of createinvestment to a smart pointer object (smart pointer object .) To solve this problem, call the createinvestment interface, which is the topic I arranged in item 18.

Things to remember

  • To prevent resource leaks (resource leakage), use the Constructors (constructors) of raiI objects (object) obtain resources and release them in their Destructors (destructor.
  • Two common raiI classes (classes) are tr1: shared_ptr and auto_ptr. Tr1: shared_ptr is usually a better choice, because its copy behavior is intuitive. Copying an auto_ptr is to leave it empty.

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.