(David reading note) More Effective C ++ Item attached 2: an auto

xiaoxiao2021-03-06  106

David's Note: I am NOTE A good Writer, And to Write A Fully New Article Is A Little Hard for Me. But Writing Some Remark ON Good Articles May Be Also Helpful for Others and Easier To Me. So, From Now ON, I WILL Try to Add A New Column "David's Reading" in My Blog, And Post My Thought and IDEA About Good Articles I Have Read.more Effective C

Item attached

2: Example of an auto_ptr

Implementation example of auto_ptr

Items M9, M10, E26, E31 and E32 demonstrate an unusual role of the Auto_PTR template class. Unfortunately, there are currently few compilers to provide a "correct" implementation (note

1). Items M9 and M28 roughly describe how you implement one, but there is a more detailed version when you engage in actual projects.

Here is the implementation of two Auot_PTRs. The first version of the documentation has implemented all the member functions outside the class interface and implements all member functions outside the class definition. The second version will be implemented in the definition of all members functions. In style, the second implementation is not as first as it is not separated from the implementation from the implementation. But Auto_PTR is just a simple class, so the second implementation is much more clear than the first.

This is an Auto_PTR template with special interface declared:

Template

<

Class T

>

Class auto_ptr

{

public

:

Explicit Auto_Ptr

(T

* P

=

0

);

// Item M5 has a description of "explicitfor" //

Template

<

Class U

>

// Copy constructor member template auto_ptr

(Auto_PTR

> & Rhs

);

// (see item m28): // Initialize a new auto_ptr object with another type-compatible // auto_ptr object //

~ auto_ptr

();

Template

<

Class U

>

// Assignment Operation member template auto_ptr

> &

// (see Item M28):

Operator

= (Auto_PTR

> & Rhs

);

// assign it with another type-compatible // auto_ptr object

T

&

Operator

* ()

Const

;

// see item m28 t

*

Operator

-> ()

Const

;

// see item m28 t

* Get

()

Const

;

// Return to the // current value of the inclusive pointer

T

* Release

();

// Abandon the kettle pointer // ownership, / / ​​and return its current value void reset

(T

* P

=

0

);

/ / Delete the inclusive pointer, // Get the ownership of the pointer P

Private

:

T

* Pointee

;

// David: Why is this? I don't know, I will not use the private member of Auto_Ptr in this implementation.

Template

<

Class U

>

/ / Let all auto_ptr classes

Friend class auto_ptr

"

/ / Become a friend

}

// David: The following is the template Auto_Ptr member function implementation

Template

<

Class T

>

Inline auto_ptr

> :: auto_ptr

(T

* P

: Pointee

(p

) {}

Template

<

Class T

>

Inline auto_ptr

> :: auto_ptr

(Auto_PTR

> & Rhs

: Pointee

(RHS)

.velease

()) {}

Template

<

Class T

>

Inline auto_ptr

> :: ~ auto_ptr

() {

Delete Pointee

}

// David: FAINT, you can also do this! Template template.

Template

<

Class T

>

Template

<

Class U

>

Inline auto_ptr

> & Auto_ptr

> ::

Operator

= (Auto_PTR

> & Rhs

) {

IF

(

THIS

! = & Rhs

) Reset

(RHS)

.velease

())

Return

*

THIS

}

// David: What to pay attention to here is that Assign Operation will use the Release meticulum auto_ptr object, the above copy construct is also the case, so as not to appear multiple delete an error, this also shows that we are passing Auto_PTR It is best not to pass the object when the object is time, and you can pass the reference (of course, you can also pass the pointer, but please do not use the pointer when you can use it, because reference is more efficient and more simpler), otherwise you may be a program The behavior is inexplicably, and the hosting object may be died when you don't realize it, this problem may not exist for the Reference Count's Smart Pointer. The following program can explain this

/ * # include #include #include using namespace std; void f (auto_ptring> Apstring) // copy-constructor is called {cout << "in f () << ENDL } int Main (int Argc, char ** argv) {auto_ptring> Apstring (new string ("abc")); f (APSTRING); cout << * Apstring << endl; // error! The String Object has been deleted by f () // To solve this problem, you should use pass apString by reference auto_ptr apString2;! apString2 = apString; // assign-operator is called cout << * apString << endl; // Error Too! // To Solve this Problem, Don't Use Copy Constructor of Auto_Ptr // The Following Method Is Also Wrong: // Auto_Ptr Apstring2 (* Apstring); Return 0;} * /// David : According to the above analysis, I think Auto_PTR's implementation should disable the copy constructor and Assignment of Auto_Ptr, which makes the release of objects are very confusing

Template

<

Class T

>

Inline T

& Auto_ptr

> ::

Operator

* ()

Const

{

Return

* Pointee

}

Template

<

Class T

>

Inline T

* Auto_PTR

> ::

Operator

-> ()

Const

{

Return Pointee

}

Template

<

Class T

>

Inline T

* Auto_PTR

> :: get

()

Const

{

Return Pointee

}

Template

<

Class T

>

Inline T

* Auto_PTR

> :: Release

() {

T

* OldPointee

= POINTEE

;

Pointee

=

0

;

Return OldPointee

}

Template

<

Class T

>

inline

Void auto_ptr

> :: RESET

(T

* P

) {

IF

(Pointee)

! = P

) {

Delete Pointee

;

Pointee

= P

}}

The following is the Auto_PTR template defined in the class defined body.

// David: The following code is completely equivalent to the above, but the function is implemented in the template. JJhou's CH3 borrows the following code, but there is a deletion, people are a bit inexplicable, if there is no Solution, you can refer to this code. Template

<

Class T

>

Class auto_ptr

{

public

:

Explicit Auto_Ptr

(T

* P

=

0

: Pointee

(p

) {}

Template

<

Class U

>

Auto_ptr

(Auto_PTR

> & Rhs

: Pointee

(RHS)

.velease

()) {} ~ Auto_ptr

() {

Delete Pointee

}

Template

<

Class U

>

Auto_ptr

> &

Operator

= (Auto_PTR

> & Rhs

) {

IF

(

THIS

! = & Rhs

) Reset

(RHS)

.velease

())

Return

*

THIS

}

T

&

Operator

* ()

Const

{

Return

* Pointee

}

T

*

Operator

-> ()

Const

{

Return Pointee

}

T

* Get

()

Const

{

Return Pointee

}

T

* Release

() {

T

* OldPointee

= POINTEE

;

Pointee

=

0

;

Return OldPointee

}

Void RESET

(T

* P

=

0

) {

IF

(Pointee)

! = P

) {

Delete Pointee

;

Pointee

= P

}}

Private

:

T

* Pointee

;

Template

<

Class U

>

Friend class auto_ptr

>};

If the compiler you use is not supported "

Explicit, you can safely use #define to cancel it:

#define explicit This will not cause any functionality of Auto_Ptr to weaken, but the slight security is weakened. See Item M5 for details.

If your compiler does not support member templates, you can use a non-template AUTO_PTR copy constructor and assignment operation (described in Item M28). This will cause your auto_ptr in use some small inconvenience, but there is no other method to imitate the behavior of member templates, hehe! If the member template (or other features in the language) is very important to you, tell your compiler provider. The more users requiring new language features, the more providers will realize them.

* Note

1:

This is mainly because the standard of Auto_PTR has not been determined for a long time. Its final description was adopted

1997

November. Its details can refer to the home page of this book. Note that the auto_ptr version here is implemented than the official version in the specific details: actually Auto_PTR is located in the namespace STD (see Item M35) and its member function promises not to throw any exceptions.

// David: Original to this end, the following content is added by himself, no longer specifically indicate.

According to the above analysis

The above AUTO_PTR has realized too many things that are not satisfactory.

So how is it very popular with SGI STL?

?

The following is taken from STLPORT

: _AUTO_PTR

.h

#ifndef _STLP_AUTO_PTR_H # define _stlp_auto_ptr_h_stlp_begin_namespace

// Add a base class here, what special use?

Class __ptr_base

{

public

:

Void

* _M_P

;

Void __set

(

Const

Void

* P

) {_M_P

= __CONST_CAST

(

Void

*, P

}

Void __set

(

Void

* P

) {_M_P

= P

;}};

/ / Do you have any special effects this class?

Template

<

Class _tp

>

Class Auto_PTR_REF

{

public

:

__ptr_base

& _M_R

;

_TP

*

Const_M_P

;

Auto_PTR_REF

(__ptr_base

& __R

, _Tp

* __P

: _M_R

(__r

), _M_p

(__P

) {}

_TP

* Release

()

Const

{_M_r

.__ set

(("

Void

*)

0

);

Return_M_P

;}};

Template

<

Class _tp

>

Class auto_ptr

:

Public __ptr_base

{

public

:

Typedef _tp element_type

;

Typedef auto_ptr

<_Tp

> _SELF

;

_Tp

* Release

() {

_Tp

* __Px

=

THIS

-> Get

();

THIS

-> _ m_p

=

0

;

Return __px

}

Void RESET

(_TP

* __Px

=

0

) {

_Tp

* __Pt

=

THIS

-> Get

();

IF

(__px

! = __Pt

)

Delete __pt

;

THIS

-> __ set

(__px

}

_TP

* Get

()

Const

{

Return __reinterpret_cast_cast

(_TP

*, __ const_cast

(

Void

*, _ M_p

));

# Ration! Defined (_stlp_no_arrow_operator) _tp

*

Operator

-> ()

Const

{

_STLP_VERBOSE_ASSERT

(Get)

()! =

0

_STLMSG_AUTO_PTR_NULL

)

Return Get

();

# ENDIF _TP

&

Operator

* ()

Const

{

_STLP_VERBOSE_ASSERT

(Get)

()! =

0

_STLMSG_AUTO_PTR_NULL

)

Return

* Get

();

Auto_ptr

() {

THIS

-> _ m_p

=

0

}

Explicit Auto_Ptr

(_TP

* __Px

) {

THIS

-> __ set

(__px

}

#if Defined (_stlp_member_templates) # i! defined (_stlp_no_template_conversions)

Template

<

Class_tp1

> Auto_PTR

(Auto_PTR

<_Tp1

> & __R

) {

_TP

* __ConversionCheck

= __R

.velease ();

THIS

-> __ set

(__ConversionCheck

}

# Endif

Template

<

Class_tp1

> Auto_PTR

<_Tp

> &

Operator

= (Auto_PTR

<_Tp1

> & __R

) {

_TP

* __ConversionCheck

= __R

.velease

();

RESET

(__ConversionCheck

);

Return

*

THIS

}

#ENDIF / * _STLP_MEMBER_TEMPLATES * /

Auto_ptr

(_SELF

& __R

) {

THIS

-> __ set

(__r

.velease

());

// Copy construct, the original object is also released, which is necessary for ordinary smart pointer, but it is easy to cause problems.

_Self

&

Operator

= (_ Self

& __R

) {

// Assign Operator, also Release the original object RESET

(__r

.velease

())

Return

*

THIS

} ~ Auto_ptr

() {

/ * Boris: reset (0) might be better * /

DELETE THIS

-> Get

();

Auto_ptr

(Auto_PTR_REF

<_Tp

> __R

) {

THIS

-> __ set

(__r

.velease

());

_Self

&

Operator

= (Auto_PTR_REF

<_Tp

> __R

) {

RESET

(__r

.velease

())

Return

*

THIS

}

# If defined (_stlp_member_templates) &&! Defined (_stlp_no_template_conversions)

Template

<

Class_tp1

>

Operator auto_ptr_ref

<_Tp1

> () {

Return Auto_PTR_REF

<_Tp1

> (*

THIS

,

THIS

-> Get

());

Template

<

Class_tp1

>

Operator auto_ptr

<_Tp1

> () {

Return auto_ptr

<_Tp1

> (Release

());

# Else

Operator auto_ptr_ref

<_Tp

> () {

Return Auto_PTR_REF

<_Tp

> (*

THIS

,

THIS

-> Get

());

# Endif

}

_STLP_END_NAMESPACE

#ENDIF / * _STLP_AUTO_PTR_H * /

The auto_ptr implementation of SGI STL is basically the same as above

Also there is also a Copy Constructionor and Assign

Operator problem.

How is the general REFERENCE COUNT-based Auto_PTR achieved?

, LOOK

:

Template

<

Class T

>

Class CComptr

{

public

:

TypedEf T _PTRCLASS

;

CComptr

() {

p

= NULL

}

CComptr

(T

* LP

) {

IF

(P

= LP

)! = NULL)

p

-> AddRef

();

CComptr

(

Const CComptr

> & Lp

)

// Copy constructor, there is no need to operate the trusted object, only increase the reference count

{

IF

(P

= LP

.P

)! = NULL

)

p

-> AddRef

();} ~ Ccomptr

() {

IF

(p

)

p

-> Release

();

Void Release

() {

Iunknown

* PTEMP

= P

;

IF

(PTEMP

) {

p

= NULL

;

PTEMP

-> Release

();

Operator T

* ()

Const

{

Return

(T

*) P

}

T

&

Operator

* ()

Const

{

Atlassert

(p

= NULL

);

Return

* P

}

// The assert on operator & us. IF this is real, take the address of the p member explicitly. T

**

Operator

& () {

Atlassert

(p

== NULL

);

Return

& p

}

_NoaddrefreleaseoncComptr

> *

Operator

-> ()

Const

{

Atlassert

(p

= NULL

);

Return

(_NoaddRefreleaseoncComptr

> *) P

}

T

*

Operator

T

* LP

) {

Return

(T

*) ATLCOMPTRASSIGN

((IUNKNOWN)

**) & P

LP

}

/ / For the assignment between CComptr , the above implementation cannot be used.

*

Operator

=

Const CComptr

> & Lp

) {

Return

(T

*) ATLCOMPTRASSIGN

((IUNKNOWN)

**) & p

LP

.P

}

// The following n function and its implementation

T

* P

};

among them

The function ATLCOMPTRASSIGN implements the following

:

Atlinline Atlapi_

(IUNKNOWN)

*) ATLCOMPTRASSIGN

(IUNKNOWN)

** PP

, IUnknown

* LP

) {

IF

(LP

= NULL

)

LP

-> AddRef

();

// Increase the REFERENCE COUNT of the right operand

IF

(* PP

) (* Pp

) -> Release

();

/ / Reduce the REFERENCE COUNT of left operating

* PP

= LP

;

/ / Re-assapore the left operation

Return LP

}

As can be seen from above

, Smart Pointer, which is based on the reference count, does not have a Copy Constructor and Assign that there is no previous auto_ptr.

Operator problem.

of course

The above discussion is not to say that we must use the SMART Pointer based on the reference count.

After all, the reference count is an additional overhead.

And because of the need to consider thread synchronization

Need more additional overhead. and so

The best solution is to choose according to the needs, try to avoid the values ​​when using Auto_PTR. But as a C

programmer

, Hand allocated objects to Auto_PTR to manage

Do you really worry?

Anyway, I am not a lot of auto_ptr

(Can you give an example of which you need AUTO_PTR, or apply Auto_PTR to significantly optimize the program?).

Note: 1. Write this article, accidentally found that more Effective C M28 has given a similar conclusion above, but this article has been written, not to bear it, so it is still announced. 2. There is a question about I have in the text. Where is your meeting? 3. Welcome to the criticism.

转载请注明原文地址:https://www.9cbs.com/read-100735.html

New Post(0)