More Effective C ++ Terms 27 (below)

zhaozj2021-02-08  337

Terms 27: Requirements or prohibit objects in the heap (below)

So far, this logic is very correct, but it is not deep enough. The most fundamental problem is that the object can be assigned in three places, not two. Yes, stacks and stacks can accommodate objects, but we forgot static objects. Static objects are those that can only initialize once in the program. Static objects not only include objects that are displayed as static, but also objects in global and namespaces (see Terms 47). These objects are certainly located in some places, and these places are neither stacks nor a pile.

Their positions are based on the system, but in the system extension in many stacks and piles, they are located at the bottom of the heap. Previous memory management pictures to tell facts, but many systems have the facts, but did not tell us all the facts of these systems, after the static variables, this picture is as follows:

The reason why Onhead cannot work immediately, it is clear that the difference between the heap object and static object cannot be identified:

Void AllocatesomeObjects ()

{

Char * pc = new char; // Pile Object: onheap (PC)

// will return TRUE

CHAR C; / / Stack Object: Onheap (& C)

// will return false

Static char sc; // Static object: onheap (& sc)

// will return TRUE

...

}

Now you may find a way to distinguish between a stack of objects and stack objects. When you walk, you want to make your mind in portability, but you will be so deactivated to make a deal that you can't get the right result? Never do it. I know that you will refuse to use this kind of attractive but unreliable "address comparison" skill.

What is sad is not only a portable method to determine if the object is on a heap, but also the "accurate portable" method that can work normally in most times. If you really have to judge whether an address is on a heap, you must use a completely unmogrant method, which is actually done on system calls, can only do this. So you'd better redide your software so that you can do not need to judge whether the object is in the heap.

If you find that you are actually the object is troubled in the pile, a possible reason is whether you want to know if an object can call DELETE on it. This deletion often uses "Delete this" declaration of the Wolf. However, I know "Can I safely delete a pointer" "and" only know if a pointer points to a stack "is different, because not all things in the stack can be securely delete. Consider the Asset object containing the UPNumber object:

Class asset {

Private:

UPNUMBER VALUE;

...

}

AskET * PA = New Asset

Obviously * PA (including its member value) is on the heap. It is also clear that DELETE is not safe to point to PA-> Value, because the pointer is not returned by New.

Fortunately, "It is easy to determine if a pointer" ratio "is to determine if a pointer is pointed to. Because for the former, we only need an address set returned by Operator New. Because we can write the Operator New function (see Effective C Terms 8 - Terms 10), it is easy to build such a collection. As shown below, we solve this problem like this:

Void * Operator New (size_t size)

{

Void * p = getMemory (size); // call some functions to allocate memory,

// Handling insufficient memory

Add P to a collection of assigned addresses; return P;

}

Void Operator Delete (Void * PTR)

{

ReleaseMemory (PTR); // Return Memory To

// Free store

Remove the PTR from the collection of the assigned address;

}

Bool Issafetodelete (Const Void * Address)

{

Returns whether Address is in the collection of the assigned address;

}

This is very simple, Operator New adds an element in the address assignment collection, Operator delete shifts from the collection, Issafetodelete finds and determines if an address is in a collection. If the Operator New and Operator Delete functions are in a global scope, it can apply to all types, even the built-in type.

Among the actual actual factors, there are three factors to restrict the use of this design. The first is that we are extremely unwilling to define anything in the global domain, especially those that have some meaning, like Operator New and Operator delete. As we know, there is only one full-local domain, only one Operator New and Operator delete have a normal feature form (that is, parameter type). This will make our software and other software that also implements global version of Operator New and Operator Delete (such as many object-oriented database systems) are incompatible.

The second factor we consider is efficiency: If we don't need these, why should I bear an additional overhead for the address returned?

The last point may be some usual, but it is very important. It is impossible to implement Issafetodelete to make it work properly. The difficulty is to inherit the class or inheritance of the class with the virtual base class has multiple addresses, so it cannot be guaranteed to pass the address returned by the iperator new, even if the object is established in the stack. See Terms 24 and Terms 31 for details.

We hope that these functions can provide these functions that can not pollute the global namespace, without additional overhead, no correctness. Fortunately, C uses an abstract Mixin base class to meet our needs.

The abstract base class is a base class that cannot be instantiated, that is, at least a base class having a pure virtual function. The Mixin class provides a particular feature and can be compatible with other functions provided by its inheritance class (see Effective C Terms 7). This type is almost all abstract classes. Therefore, we can use the abstract mixed (Mixin) base class to provide the ability to determine the pointer to the ability to assign by the Operator New. The class is as follows:

Class Heaptracked {// Mixed class; tracking

Public: // Returned from Operator New PTR

Class missingaddress {}; // Abnormal class, see the following code

Virtual ~ Heaptracked () = 0;

Static void * Operator new (SIZE_T SIZE);

Static void Operator Delete (Void * Ptr);

Bool isonheap () const;

Private:

TypeDef const vid * rawaddress;

Static List addresses;

}

This class uses the List (Lin table) data structure tracking all pointers returned from Operator New, part of the List standard C library (see Effective C Terms 49 and this book Terms 35). The Operator New function assigns memory and adds the address to the list; Operator delete is used to release the memory and remove the address element from the list. IsonHeap determines whether an object's address is in LIST. The HEAPTRACKED class (I think the importation "is very simple, call the global Operator New and Operator Delete functions to complete the memory allocation and release, the function in the list of the List class performs insertion operation And delete the operation, and perform single-language lookup operations. The following is all of Heaptracked all of the work:

// mandatory definition of static class member

List Heaptracked :: Addresses

The destructor of // Heaptracked is a pure virtual function that makes the class become an abstract class.

// (See Effective C Terms 14). However, the destructor must be defined.

// So we did an empty definition. .

Heaptracked :: ~ Heaptracked () {}

Void * Heaptracked :: Operator New (size_t size)

{

void * memptr = :: operator new (size); // Get memory

Addresses.push_front (Memptr); // Put the address to the front end of the List

RETURN MEMPTR;

}

Void Heaptracked :: Operator Delete (Void * PTR)

{

// Get a "item" to identify the PTR contained in the List element;

/ / Refer to Terms 35

List :: item t =

Add (Addresses.Begin (), Addresses.end (), PTR);

IF (it! = addresses.end ()) {// If you find an element

Addresses.rase (it); // deletes the element

:: Operator Delete (PTR); // Release Memory

} else {/ / otherwise

Throw missingaddress (); // PTR is not using Operator New

} // Allocated, so throw an exception

}

Bool Heaptracked :: isonheap () const

{

/ / Get a pointer, pointing to the start of the memory space occupied by * this,

/ / Refer to the discussion below

Const void * rawaddress = Dynamic_cast (this);

/ / The pointer is checked in the address list returned in Operator New

List :: item t =

Add (Addresses.Begin (), ADDRESS.END (), RAWADDRESS;

Return it! = addresses.end (); // Return to IT to be found

}

Although you may not be familiar with the other parts of the List class and the standard C library, the code is still very a bit. Terms 35 will explain everything here, but the comments in the code have been able to explain how this example is running. Only one place may make you feel confused, that is, this statement (in the isonheap function)

Const void * rawaddress = Dynamic_cast (this);

I have said that there will be several addresses with multiple inheritance or virtual base classes, which makes it complicated in writing global functions. This problem will still be encountered in IsonHeap, but because ISONHEAP is only used in HeaPTracked objects, we can use a special feature of the Dynamic_cast operator to eliminate this problem. Just simply put Dynamic_CAST, turn a pointer Dynamic_CAST into a VOID * type (or const void * or volatile void * .....), the generated pointer points to the beginning of "original pointer pointing object memory". But Dynamic_cast can only be used on a pointer to at least one virtual function. We damn the Issafetodelete function can be used to point to any type of pointer, so Dynamic_CAST cannot help it. IsonHeap is more selective (it can only test pointers to the Heaptracked object), so you can turn the THIS pointer Dynamic_CAST into const void *, which is made into a pointer to the current object start address. If Heaptracked :: Operator New Assigns memory for the current object, this pointer is the pointer returned by Heaptracked :: Operator NEW. If your compiler supports the Dynamic_cast operator, this tip is completely portable.

Using this class, even the initial programmers can also add the functions of the pointer in the trace stack in the class. What they need to do is to let their classes inherited from HeaPtracked. For example, if we want to judge whether the Assert object pointer points to whether it is a heap object:

Class asset: public heaptracked {

Private:

UPNUMBER VALUE;

...

}

We can query the assert * pointer like this as follows:

Void Inventoryasset (const asset * AP)

{

IF (ap-> isonheap ()) {

AP is a heap-based asset - inventory it as such;

}

Else {

AP is a non-heap-based asset - replaord it.

}

}

There is a disadvantage like HeaPtracked mixed classes that cannot be used for built-in types because the types like INT and CHAR cannot inherit from other types. However, it is generally necessary to determine if "delete this" is to be called, you cannot call it on the built-in type because the built-in type does not have this pointer.

Prohibited heap object

Decision of whether the object is tested in the heap to the end. The opposite area is "banned in the stack". The establishment of usual objects three situations: objects are directly instantiated; objects are instantiated as the base class of derived classes; the object is embedded in other objects. We will discuss them in order.

It is forbidden to instantiate the object directly, because it always calls New to create this object, you can ban the client call New. You can't affect the availability of the New operator (this is within the language), but you can use the New operator to always call the Operator New function (see Terms 8) to achieve the purpose. You can declare this function yourself, and you can declare it as private .. For example, if you want to let the client build an UPNumber object in the stack, you can write: class upnumber {

Private:

Static void * Operator new (SIZE_T SIZE);

Static void Operator Delete (Void * Ptr);

...

}

Now the client can only do what they do:

Upnumber n1; // okay

Static Upnumber N2; // Also Okay

UPNUMBER * P = new upnumber; // error! Attempt to Call

// Private Operator New

It is enough to declare the Operator New as Private, but declare the Operator New as Private, and declare the Iperator Delete as public, which is a bit weird, so unless there is absolute need, don't separate them separately, it is best class One part declares them. If you also want to ban the UPNumber heap object array, you can declare the Operator New [] and Operator Delete [] (see Terms 8) also declared as Private. (Operator New and Operator Delete are much stronger than most people think. About the freshness of the relationship between them, you can see the Sidebar section of my article in Counting Objects.)

Interestingly, the Operator New declares that Private often hinders the UPNumber object as a base class that is located in the stack in the heap is instantiated. Because if Operator New and Operator Delete are not declared in the derived class as public, they will inherit, inherit the class of the base class Private function, as shown below:

Class Upnumber {...}; //

Class NonnegativeupNumber: / / Assume this class

Public Upnumber {// No declaration Operator New

...

}

NonnegativeUPNumber N1; / / correct

Static NonnegativeUPNumber N2; // is also correct

NonnegativeUPNumber * P = // Error! Tryed call

New NonnegativeUpnumber; // Private Operator New

If the derived class declares its own Operator New, when the derived object is assigned in the heap, this function must be invoked, and a different method must be found to prevent the UPNUMBER base class from being wrapped here. Similarly, UPNumber's Operator New is this PRIVATE, which does not have any impact on the objects that allocate the UPNumber objects of members:

Class asset {

PUBLIC:

Asset (int initvalue); ...

Private:

UPNUMBER VALUE;

}

Ask * PA = New Asset (100); / / correct, call

// asset :: Operator New or

// :: Operator new, not

// Upnumber :: Operator New

In fact, we have returned to this question, ie "If the UPNumber object is not constructed in the heap, we want to throw an exception." Of course this time is "If the object is in the heap, we want to throw an exception." Just like there is no portable method to determine if the address is in the heap, there is no portable method to determine whether the address is not in the heap, so we are very busy, but this is not surprising, after all, if we can distinguish a certain The address is on the pile, we can also distinguish an address is not posted. But we can't tell anything.

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

New Post(0)