More Effective Terms 27 (on)

zhaozj2021-02-08  277

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

This article contains pictures, can't post it, please download Word documentation. download

Sometimes you want to manage certain objects so that some types of objects can be self-destroy, that is, you can "delete this." It is clear that this type of object is required to be assigned in the heap. And others want to get a guarantee: "Do not assign objects in the heap, so that some types of classes do not have memory leaks." If you work on embedded systems, you may have encountered this situation. Memory leaks that occur on embedded systems are extremely serious, and their stack is very precious. Is there a possibility to write a code to request or ban Heap-based Object in the heap? It is usually possible, but this code will also make the concept of "on the heap" to be blurred than your mind.

Requires establishing an object in the heap

Let us first start to start with the objects that must be established in the heap. To perform this limit, you must find a way to establish an object to establish an object other than the "New". This is easy to do. Non-Heap Objects are automatically constructed in defining it, automatically released at the end of the living time, so this limit can be implemented as long as you use implicit constructor and destructive functions.

A most straightforward way to make these calls is not legal is to declare the constructor and the destructor function as private. This is too side effect. There is no reason to make these two functions are private. It is best to make the destructive function be a private to make the constructor become public. The processing process is similar to the provisions 26, you can introduce a dedicated counterfeit function, use to access the true destructor. The client calls the counterfeit function to release the object they build.

For example, if we want to establish an object that represents Unlimited Precision Numbers (unlimited precision numbers) in the heap, you can do this:

Class Upnumber {

PUBLIC:

Upnumber ();

UPNUMBER (INT INTVALUE);

Upnumber (double initvalue);

Upnumber (Const Upnumber & r H);

// The counterfeit function (a constant function, because

// Even if the Const object can be released. )

Void destroy () const {delete this;}

...

Private:

UPNUMBER ();

}

Then the client is programmed to design:

UPNUMBER N; / / Error! (Legal it here, but

// When its destructor is implicit

// When calling, it is not legal)

UPNUMBER * P = new upnumber; / / correct

...

DELETE P; // Error! Trying to call

// Private destructor

P-> destroy (); // correct

Another method is to declare all constructor as private. The disadvantage of this method is that a class often has many constructor, and the author must remember to declare them as private. Otherwise, if these functions are generated by the compiler, the constructor includes a copy constructor, including the default constructor; the function generated by the compiler is always PUBLIC (see Effecitve C Terms 45). Therefore, it is very simple to declare that the monograph function is private because each class has only one destructor.

The establishment of non-heap objects by restricting access to a class of destructor or its constructor, but in terms 26 have said that this method also prohibits inheritance and inclusion: Class Upnumber {...}; // Declare a destructor or constructor

/ / For private

Class NonnegativeupNumber:

Public UPNUMBER {...}; // error! Destructor or

// Constructor cannot be compiled

Class asset {

Private:

UPNUMBER VALUE;

... // Error! Destructor or

// Constructor cannot be compiled

}

These difficulties are not impossible. By declaring the UPNUMBER's destructor, you can resolve the inheritance problem, you need to include the UPNumber object, which can be modified to include pointers to the UPNumber:

Class Upnumber {...}; // The declaration pattern function is protected

Class NonnegativeupNumber:

Public UPNUMBER {...}; // is now correct; derived class

/ / Can be accessed

// protected member

Class asset {

PUBLIC:

Asset (Int init ";

~ Asset ();

...

Private:

UPNUMBER * VALUE;

}

Ask :: asset (INT initvalue)

: value (new upnumber (initValue) // correct

{...}

AskET :: ~ Asset ()

{Value-> destroy ();} // is also correct

Judging whether an object is in a heap

If we take this method, we must re-examine the meaning of "in the heap". The rough class definitions described above indicate that a non-heap NonnegativeupNumber object is legal:

NonnegativeUPNumber N; / / correct

So now the UPNumber section in the NonnegativeupNumber object n is not in the stack, is this right? The answer must be based on the details of the design and implementation, but let us assume that this is not right, all UPNumber objects - even as the base class for other derived classes - must also be in the heap. How can we force this constraint?

There is no simple way. The constructor of the UPNumber cannot determine if it is called the base class of the heap object. That is to say, there is no way to detect the following two environment:

NonnegativeUPNumber * n1 =

New nonnegativeupnumber; // in the heap

NonnegativeUPNumber N2; // no longer

But you may not believe me. Maybe you want you to play some small trows in the interaction of the construction function called by the New operator, Operator New, and New operator (see Terms 8). Maybe you think you are smart than they, you can modify UPNumber like this, as shown below:

Class Upnumber {

PUBLIC:

// If you create a non-heap object, throw an exception

Class HeapConstraintviology {};

Static void * Operator new (size_t size);

Upnumber ();

...

Private:

Static Bool ONTHEHEAP; / / In the constructor, indicating whether the object is constructed.

... //

}

// Obligatory definition of class static

Bool Upnumber :: ONTHEHEAP = FALSE;

Void * Upnumber :: Operator New (size_t size)

{

Ontheheap = True;

Return :: Operator New (size);

}

Upnumber :: Upnumber ()

{

IF (! ontheheap) {

Throw HeapConstraintviology ();

}

Proceed with normal construction here;

ONTHEHEAP = false; / / Clear tag for the next object

}

If you no longer go deep into research, you won't find anything wrong. This method uses this fact: "When the object is assigned to the object, the Operator New is called to assign RAW MEMORY. OPERATOR NEW Sets Ontheheap to true, each constructor detects ONTHEHEAP, see if the object's RAW MEMORY is The Operator New assigned. If not, an exception of a type HeapConstraintViolation will be thrown. Otherwise, the constructor continues to run as usual. When the constructor ends, ONTHEHEAP is set to false and then reset to the default value for constructing the next object.

This is a very good way, but you can't run. Consider this possible client code:

UPNUMBER * NUMBERARRAY = New UPNUMBER [100];

The first question is to allocate memory for array is Operator New [], not Operator New, but if your compiler supports it), you can easily write Operator New [] functions as writing Operator New. A larger problem is that NumberRay has 100 elements, so a 100 constructor will be called. However, only one assignment of memory is allocated, so only the first call constructor is set to True before the first call constructor. When the second constructor is called, an exception will be thrown, you are really unlucky.

Even without array, Bit-setting operation will fail. Consider this statement:

UPNUMBER * PN = New UPNUMBER (* New Upnumber);

Here we build two UPNumber in the heap, let PN points to one of the objects; this object initializes with the value of another object. This code has a memory leak, let's ignore this leak, which facilitates the following to this expression, what happens when it is performed:

New Upnumber (* new upnumber)

It contains two calls for the New operator, so call two Operator New and call two UPNumber constructors (see Terms 8). Programmers generally expect these functions to perform in order:

Call the Operator New of the first object

Call the constructor of the first object

Calling the Operator New in the second object

Call the constructor of the second object

But the C language does not guarantee that this is the order it is called. Some compilers generate functions in such order:

Call the Operator New of the first object

Calling the Operator New in the second object

Call the constructor of the first object call the constructor of the second object

The compiler generates this code is not wrong, but SET-A-bit tips in Operator New cannot be used with this compiler. Because of the first step and the second step set, the third step is cleared, then when the structure of the object is called, it will be considered that the object is no longer stack, even if it is indeed.

These difficulties have no negation to allow each constructor to detect the core idea of ​​this pointer in the heap, but they simply indicate that the bit set in Operator New (or Operator New [] is not a reliable judgment method. We need a better way to judge.

If you fall into extreme desperation, you may fall into the unmistable domain. For example, you decide to use a fact that exists on a lot of systems, the address space of the program is used as linear address management, and the program's stack extends down the top of the address space, and the stack is extended from the bottom:

In the system where the program is managed in this method (many systems are, there are many not this), you may want to use the following functions to determine if a particular address is in the heap:

// Incorrect attempt to determine if an address is in a heap

Bool onheap (const void * address)

{

CHAR ONTHESTACK; / / Local stack variable

Return Address <& ontheStack;

}

This function is very interesting behind the thoughts. On the onheap function is a partial variable. So it is on the stack. When calling onHeap, its stack frame (which is also its Activation Record) is placed at the top of the program stack, because the stack is extended down (tend to low address), the address of the ONTHESTACK is definitely ratio Variables in any stack or the address of the object is small. If the address of the parameter Address is less than the address of the ONTHESTACK, it will not be on the stack, but is sure to be on the heap.

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

New Post(0)