Chapter Review:
The 1th chapter of effective C + + makes himself accustomed to c++-reading notes
Effective C + + 2nd structure/destructor/assignment operation (1)-Reading notes
Effective C + + 2nd structure/destructor/assignment operation (2)-Reading notes
"Effective C + +" Chapter 3rd resource Management (1)-Reading notes
"Effective C + +" Chapter 3rd Resource Management (2)-Reading notes
"Effective C + +" 8th custom new and delete-reading notes
Article 15: Provide access to the original resource in the resource management class
Many APIs directly refer to resources, so unless you never use them, you will bypass the resource management object to access the original resources directly. Suppose you use Tr1::shared_ptr to manage objects.
Std::tr1::shared_ptr<investment> PINV (Createinvestment ());
The declaration of the function daysheld is this:
int daysheld (const investment *pi);
The following method of invocation is definitely wrong:
int days = Daysheld (PINV); // Error
Because the function requires a pointer, you pass it as a Tr1::shared_ptr<investment> object. So you need a function to convert the Raii object into a primitive resource that is included. There are two ways to do this: implicit conversion and display conversion.
(1) Display conversion
Both Tr1::shared_ptr and auto_ptr provide a member function get to return the internal raw pointer, which is an explicit conversion.
int days = Daysheld (PINV. Get()); // Okay, no problem .
(2) Implicit conversion
Both Tr1::shared_ptr and auto_ptr overload the operators operator-> and operator*, allowing implicit conversions to the original pointer. Example: Suppose the investment class has a member function bool Istaxfree () const; Then the following call is OK:
bool taxable1 =! (Pinv->istaxfree ()); // Okay, no problem . bool taxable2 =! ((*PINV). Istaxfree ()); // Okay, no problem .
The question now is where the original pointer is needed (for example, a function parameter) and how to replace it with a smart pointer. The workaround is to provide an implicit conversion function . Here is an example of a font class:
Fonthandle GetFont (); // get the font handle void Releasefont (fonthandle FH); // release handle class font{public: Explicit Font (Fonthandle fh): F (FH) {} ~ Font () { releasefont (f); } Private : Fonthandle F;};
If the C API handles Fonthandle instead of a Font object, you can, of course, provide a get () function like Tr1::shared_ptr and auto_ptr:
Get Const return F; } // Show Conversion function
This is possible, but the customer is still in trouble, it is necessary to define an implicit conversion function.
class font{public : ... operator Const return F;} ...};
Note: Suppose you already know the use of implicit conversion functions. For example: You must define a member function, do not allow conversions to arrays and function types, and so on.
After completing the above work, the call to the following function is OK:
void int newSize); Font f (GetFont ()); int newfontsize;changefontsize (f, newfontsize); // OK, the font is implicitly converted to Fonthandle.
implicit type conversions also add a risk . For example, the following code is available:
= F1; // The font is incorrectly written as Fonthandle, and the compilation still passes.
F1 is implicitly converted to Fonthandle, at this time F1 and F2 co-management of a resource, F1 is destroyed, font release, this time you can imagine the state of F2 (forgive me the word I will not say), and then destroy F2, will inevitably cause a run error. It is usually better to provide a display transform get function because it avoids unintentional type conversion errors, which are estimated to take a long time to debug (the situation I have encountered).
Please remember:
(1) Some APIs require access to raw resources, so each RAII class should provide a way to "get the resources it manages".
(2) access to the original resource can be either explicitly converted or implicitly converted. Generally explicit conversions are more secure, but implicit conversions are more convenient for customers.
Article 16: Paired use new and delete to take the same form
I'm sure you'll see the following code at a glance:
std::stringnew std::string[+]; Delete Stringarray;
The behavior of the program is undefined. Stringarray contains 100 string objects, 99 may not be deleted because their destructors are not called.
Delete must know how many objects are deleted and how many destructors are called.
The memory layout of a single object and array is certainly different, and the memory occupied by the arrays may contain a "array size" record. (What the compiler does) you can tell the compiler whether to delete an array or a single object:
std::stringnew std::string; std::stringnew std::string [+]; delete stringPtr1; // Delete an object Delete [] stringPtr2; // Delete an array
The rule is simple, but one thing to note:
typedef std::string addresslines[4];std::stringnew addresslines; delete pal; // not good, behavior undefined. Delete [] pal; // very good.
Therefore, it is better not to do a typedef array.
Please remember: If [] is used in the new expression, [] must be used in the corresponding delete expression, and [] must not be used in the corresponding delete expression if [] is not used in the new expression.
Article 17: Placing a Newed object into a smart pointer with a standalone statement
Suppose you have the following function, which means we can ignore it first:
int Priority (); void int priority);
The first thing to note is that when you call Processwidget in the following form, it must be wrong:
Processwidget (new Widget, priority ());
Compilation error. The Tr1::shared_ptr constructor requires a raw pointer, but the constructor is explicit, which prohibits implicit type conversions. So, the normal situation you should call:
Processwidget (std::tr1::shared_ptr<widget> (new Widget), priority ()); // Okay, no problem .
In this invocation, there are three things to do before executing the function body:
(1) call priority.
(2) execute the "new Widget".
(3) call the Tr1::shared_ptr constructor
It is certain (2) to be executed before (3), but the Order of execution (1) cannot be determined. Assuming (1) is executed in the second, the pointer returned by the new widget will be lost if the call priority occurs because the Tr1::shared_ptr constructor has not yet been executed. Therefore, a resource leak occurred.
Avoiding such problems is simple: use separate statements.
Std::tr1::shared_ptr<widget> PW (new Widget);p rocesswidget (PW, priority ()); // will never cause a leak .
keep in mind that the Newd object is stored in a smart pointer in a standalone statement. If you do not, once an exception is thrown, it is possible to cause an imperceptible resource leak.
"Effective C + +" Chapter 3rd Resource Management (2)-Reading notes