Abnormal processing in C and C ++ 8

zhaozj2021-02-16  43

1. Automatic deletion, NEW and DELETE, Placement New and Placement Delete

At the end of the last time, I expect: When a newly generated object is not completely constructed, the memory it occupies is automatically released. Fortunately, the C Standards Committee adds this feature to the language (and unfortunately, this feature is too late, many compilers do not support it). Both Visual C 5 and 6 support this "automatic delete" feature (but, if we will see, Visual C 5 support is incomplete).

1.1 Automatic deletion

To actually verify it, add the Operator New and Operator Delete functions that track information in the last example 6:

// EXAMPLE 7

#include

#include

#include

#include

Void * Operator new (size_t const n)

{

Printf (":: Operator new / n");

Return Malloc (n);

}

Void Operator Delete (Void * Const P)

{

Std :: cout << ":: operator delete << std :: end1;

Free (p);

}

Class B

{

PUBLIC:

B (int const id): ID_ (id)

{

Std :: cout << id_ << "b :: b enter" << std :: end1;

IF (id_> 2)

{

Std :: cout << std :: endl;

Std :: cout << "throw" << std :: endl;

Std :: cout << std :: endl;

Throw 0;

}

Std :: cout << id_ << "b :: b exit" << std :: end1;

}

~ B ()

{

Std :: cout << id_ << "b :: ~ b" << std :: end1;

}

Private:

INT const id_;

}

Class A

{

PUBLIC:

A (): B1 (New B (1)), B2 (New B (2)), B3 (New B (3))

{

Std :: cout << "A :: A" << std :: endl;

}

~ A ()

{

Std :: cout << "A :: ~ a" << std :: endl;

}

Private:

Std :: auto_ptr Const B1;

Std :: auto_ptr const b2;

Std :: auto_ptr const b3;

}

int main ()

{

Try

{

A a a;

}

Catch (...)

{

Std :: cout << std :: endl;

Std :: cout << "catch" << std :: endl;

Std :: cout << std :: endl;

}

Return 0;

}

The program will use our own Operator New and Operator Delete instead of the version provided by the standard runtime. In this way, we will be able to track all the allocation and release memory operations when all dynamically created objects. (I also modified other tracking information in small and small, so that the output information is easier to read.) Note that our Operator New calls Printf instead of std :: cout. Originally, I did use Std :: Cout, but the program created an invalid page error in the running library. The debugger displays the run library to call Operator New before initializing std :: cout, and Operator New has tried to call Std :: cout, the program has collapsed.

I am running the program in Visual C 6, I get the output of my head:

:: Operator New

:: Operator New

:: Operator New

:: Operator New

:: Operator New

:: Operator New

:: Operator Delete

:: Operator Delete

:: Operator New

:: Operator New

:: Operator New

:: Operator New

:: Operator New

:: Operator New

:: Operator Delete

:: Operator Delete

1 b :: b Enter

1 b :: b exit

:: Operator New

2 b :: b enter

2 b :: b exit

:: Operator New

3 b :: b Enter

Throw

:: Operator Delete

2 b :: ~ b

:: Operator Delete

1 b :: ~ b

:: Operator Delete

Catch

:: Operator Delete

:: Operator Delete

:: Operator Delete

:: Operator Delete

:: Operator Delete

Blech.

I can't identify useful information from the middle. The reason is very simple: our code, standard runtime code, and code that the compiler's dark generated code calls Operator New and Operator Delete. We need some ways to isolate our interested calls and only output their tracking information.

1.2 Category New and Delete

C saved us. Do not use the global Operator New and Operator Delete, we can track the version of its class. Since we are interested in the allocation and release process of the B object, we only need to move Operator New and Operator Delete to class B:

// EXAMPLE 8

#include

#include

Class B

{

PUBLIC:

Void * Operator new (size_t const n)

{

Std :: cout << "b :: operator new" << std :: endl;

Return :: Operator new (n);

}

Void Operator Delete (Void * Const P)

{

Std :: cout << "b :: operator delete" << std :: endl;

Operator delete (p);

}

// ... rest of class b unchanged

}

// ... Class a and main unchanged

The compiler invokes these functions for the object of B, and the allocation of other objects and releases the function versions in the standard runtime.

By adding such a local operation function by your own class, you can manage this type of object that is dynamically created. For example, programmers for embedded systems often assign certain objects in a special mapped device or fast memory, which can control how to assign these objects. For our example, special heap management is unnecessary. Therefore, I calls the global version in the class Operator New and Operator delete and no longer Malloc and Free, and remove the contained to the header file . Thus, the actual semantics of all objects and the actual semantics of the release are maintained.

At the same time, because our Operator New is not in the global scope, it will not be running before constructing std :: couk, so I can call Std :: Cout in it. I also dropped because I no longer call Printf.

Compile and run Example 8. It is useful to find out the output information:

B :: Operator New

1 b :: b Enter

1 b :: b exit

B :: Operator New

2 b :: b enter

2 b :: b exit

B :: Operator New

3 b :: b Enter

Throw

B :: Operator Delete

2 b :: ~ b

B :: Operator Delete

1 b :: ~ b

B :: Operator Delete

Catch

The tracking information of three B :: Operator NEW corresponds to the construction of A.B1, A.B2, and A.B3. Among them, A. B1 and A.B2 are fully constructed (their constructor enters and exit), and A.B3 is not (its constructor is just entering without exiting). Note this:

3 b :: b Enter

Throw

B :: Operator Delete

It shows that call A.B3 constructor, thrown an exception, then the compiler automatically releases the memory occupied by A.B3. Next tracking information:

2 b :: ~ b

B :: Operator Delete

1 b :: ~ b

B :: Operator Delete

It indicates that the fully constructed object A.B2 and A.B1 are destructed before the release thereof.

Conclusion: The destructor of all fully constructed objects is called, and all objects are released.

1.3 Placement New

Example 8 uses a "normal" non-placement new statement to construct three B objects. Now consider this change:

// EXAMPLE 9

// ... preamble unchanged

Class B

{

PUBLIC:

Void * Operator new (size_t const n, int)

{

Std :: cout << "B :: Operator new (int) << std :: end1

Return :: Operator new (n);

}

// ... rest of class b unchanged

}

Class A

{

PUBLIC:

A (): B1 (NEW (0) B (1)), B2 (NEW (0) B (2)), B3 (NEW (0) B (3)) {

Std :: cout << "A :: A" << std :: endl;

}

// ... rest of class a unchanged

}

// ... main unchanged

This new statement

New (0) b (1)

There is a Placement parameter 0. Because the type of parameters is int, the compiler requires an overload version of the Operator New to accept the additional INT parameter. I have added a B :: Operator new function that meets the requirements. This function does not actually use this extra parameter, this parameter is just a placeholder, used to distinguish Placement New or non-placement new. Because Visual C 5 does not fully support Placement New and Placement delete, Example 9 cannot be compiled under it. The program can be compiled under Visual C 6, but the warning of three Level 4 is generated on this line:

A (): B1 (New (0) B (1)), B2 (NEW (0) B (2)), B3 (NEW (0) B (3))

The content is:

'void * b :: Operator new (unsigned int, int):

No matching operator delete found;

Memory Will NOT BE FREED IF INITIALIZATION

THROWS An EXCEPTION

Want to know why the compiler warns, run the program, then compare the output with example 8:

B :: Operator New (int)

1 b :: b Enter

1 b :: b exit

B :: Operator New (int)

2 b :: b enter

2 b :: b exit

B :: Operator New (int)

3 b :: b Enter

Throw

2 b :: ~ b

B :: Operator Delete

1 b :: ~ b

B :: Operator Delete

Catch

The output is the same, only one key is different:

3 b :: b Enter

Throw

As in Example 8, the constructor of A.B3 entered and throws an abnormality therein; however, the exception is that the memory of A.B3 is not automatically deleted. We should pay attention to the compiler's warning!

1.4 Final, Placement Delete!

If you want to "automatically delete", the overload version of Operator New's Operator DELETE that matches an exception object must be available. From C standard (Subclause 5.3.4p19, "New"):

If the number of parameters is the same and the type is consistent with the first parameter (after the automatic type conversion of the parameters), a Placement release function matches the allocation function of a placement. All non-Palcement release functions matches a non-placement allocation function. If you find and only find a matching release function, this function will be called; otherwise, no release function is called.

Therefore, allocation functions for each Placement

Void Operator New (SIZE_T, P2, P3, ..., PN);

Have a corresponding Placement release function

Void * Operator Delete (Void *, P2, P3, ..., PN);

Here

P2, P3, ..., PN

Generally the same parameter queue. I said "general" is because, according to standards, the parameters can be converted to the parameters. Recently, Subclause 8.3.5p3, "Functions"), based on readability, slightly modified:

These types will be converted to determine the type of function to determine the type of function:

l All parameter type const / volatile descriptor will be deleted. These CV descriptors are only affecting the definition of the shape in the function, does not affect the type of function itself.

For example: type

void (*) (const Int)

Become

void (*) (int)

l If a storage type descriptor is in a parameter type, this descriptor is deleted. This storage type descriptor is only affects the definition of the shape in the function, does not affect the type of function itself.

E.g:

Register char *

become

Char *

The converted parameter type list is the list of parameter types of functions.

By the way, this rule also affects the overload judgment of the function, Signatures and Name Mangling. Basically, the appearance of the CV descriptor on the function parameters and the appearance of the storage type descriptor does not affect the identity of the function. For example, this means that all of the following statements are defined by the same function.

l void f (int)

l void f (const Int)

l void f (register int)

l void f (Auto Const Volatile Int)

Add the Placement Operator delete function that matches our Placement Operator New:

// EXAMPLE 10

// ... preamble unchanged

Class B

{

PUBLIC:

Void Operator Delete (Void * Const P, Int)

{

Std :: cout << "B :: Operator delete (int) << std :: end1;

:: Operator delete (p);

}

// ... rest of class b unchanged

}

// ... Class a and main unchanged

Then recompile and run. The output is:

B :: Operator New (int)

1 b :: b Enter

1 b :: b exit

B :: Operator New (int)

2 b :: b enter

2 b :: b exit

B :: Operator New (int)

3 b :: b Enter

Throw

B :: Operator Delete (int)

2 b :: ~ b

B :: Operator Delete

1 b :: ~ b

B :: Operator Delete

Catch

That is very similar to Example 8, each Operator New matches an Operator Delete.

A possible strange place: All B objects are allocated through the Placement Operator New, but not all through the Placement Operator delete release. Remember, the Placement Operator Delete is only called to automatically destroy the partially constructed object when the PLCaEment Operator New fails. A fully constructed object will be manually destroyed through the DELETE statement, while the DELETE statement calls non-placement operator delete. (WQ Note: There is no way to call the Placement DELETE statement, only call the PLCaEment Operator Delete function, see 9.2.)

1.5 light cloudy arrow

In the ninth section, I will show how PfaceMent delete is smart (far exceeds the present), but there is a small concealment and simplification. And demonstrate a new mechanism to better accommodate anomalies in the constructor (such as A :: A).

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

New Post(0)