Abnormalities in C and C ++ 5

zhaozj2021-02-16  47

1. Abnormalities in C New and Delete operations

Today, we start learning the exception handling of C NEW and DELETE operations. First, I will introduce the standard C runtime support for New and Delete operations. Then, the exception accompanying these supports.

1.1 New and Delete expressions

Write

B * p = new D;

Here, B and D are Class types, and there are constructors and destructor, the code actually generated by the compiler is about this:

B * P = Operator New (SizeOf (D));

D :: D (p);

The process is:

l NEW operation accepts the size of the D object (byte) as a parameter.

l NEW operation returns a size of a size sufficient to accommodate the memory of a D object.

The default constructor of L D is called. The THIS pointer that this constructor is incorporated is the memory address that is just returned.

l Results: * P is a completely constructed object, the static type is B, the dynamic type is D.

Similar, statement

Delete P;

Almost compiled

D :: ~ d (p);

Operator delete (p);

The destructor of D is called, the introduced THIS pointer is P; then the delete operation releases the assigned memory.

The New operation and delete operation is actually a function. If you don't provide your own version, the compiler uses the version of the standard C running library header file :

Void * Operator new (std :: size_t);

Void Operator delete (void *);

Unlike other standard runtime functions, they are not in the namespace STD.

Because the compiler implies these functions, it must know how to find them. If the compiler places them in a special space (such as namespace STD), you can't declare your alternative version. Therefore, the compiler searches outwards from the absolute name. If you don't have a version of your own, the compiler will eventually find the global version of the declaration in .

This header contains 8 new / delete functions:

//

// new and delete

//

Void * Operator new (std :: size_t);

Void delete (void *);

//

// array new and delete

//

Void * operator new [] (std :: size_t);

Void delete [] (void *);

//

// Placement New and Delete

//

Void * Operator new (std :: size_t, void *);

Void Operator delete [] (void *, void *);

//

// Placement Array New and Delete

//

Void * operator new [] (std :: size_t, void *);

Void Operator delete [] (void *, void *);

The first two I have already introduced. Next, two allocation and release array objects, and the last four do not assign and release anything!

1.2 array new and array delete

NEW [] Operation is implied in such an expression:

B * p = new d [n];

The implementation of the compiler is:

B * P = Operator new [] (SIZEOF (D) * n _V);

For (std :: size_t _i (0); _i

D :: D (& P [_i]); the previous example assigns and constructs a single D object, this example assigns and constructs an array with N D objects. Note that the byte size transmitted to the New [] is SizeOf (d) * n _v, the total size of all objects plus _v. Here, _v is an additional overhead when array assignment.

If you think,

Delete [] P;

Real as:

For (std :: size_t _i (_n_of (p)); _i> 0; --_ i)

D :: ~ d (& P [i-1]);

Operator delete [] (p);

Here, _n_of (p) is an imaginary word, which relies on your compiler to implement the system when detecting the number of elements in * p.

Unlike P = New D [N] (which explicitly explains * p contains N elements), delete [] P does not express the number of * p elements in the compile period. Your program must calculate the number of elements during the running period. The C standard does not force the implementation of the implementation system, and the compiler I have ever seen has two implementations:

l Save the number of elements in the byte in front of * P. Its storage space comes from new [] operation _v byte extra overhead.

l Maintain a private N to P mapping table by standard run library.

1.3 Placement New and Placement Delete

Keyword New can accept parameters:

P = New (Arg1, Arg2, Arg3) D;

(C standard said such expression is "New with Placement" or "Placement New", I will immediately explain the reason.) These parameters will be implied to the New operation function:

P = Operator New (SizeOf (D), Arg1, Arg2, Arg3);

Note that the first parameter is still the number of bytes to generate an object, and other parameters are always following it.

The standard runtime defines a special overload version of a new operation, which accepts an extra parameter:

Void * Operator new (std :: size_t, void *);

This form of NEW operation is implied in the following statement:

P = New (addr) d;

Here, AddR is the address of some data area, and the type is compatible with Void *.

Addr is transmitted to this special new operation, which returns the address of the memory that will be constructed, but does not need to apply for memory in the free memory area, but do not need to apply for memory directly:

Void * Operator New (std :: size_t, void * addr)

{

Return addr;

}

This return value is then passed to the THIS pointer for the D :: D as constructor.

In this way, expressions

P = New (addr) d;

A D object is constructed on the memory referred to in ADDR and P is assigned to the value of ADDR. This method allows you to specify the location of the newly generated object, so it is called "Placement New".

The additional parameter form of this New is initially designed to control the location of the object, but the C Standard Commission recognizes that such a transmission can be used for any use but not only the location of the control object. Unfortunately, the term "Placement" has been developed according to the initial purpose and is suitable for the additional parameters of all NEW operations, even if they do not attempt to control the location of the object.

So, each of the following expressions is an example of Placement New:

NEW (AddR) D; // Calls Operator New (std :: size_t, void *)

NEW (AddR, 3) D; // Calls Operator New (std :: size_t, void *, int) new (3) d; // calls operator new (std :: size_t, int)

Even if only the first form is generally used as a control object position.

1.4 Placement Delete

Now, as long as the Placement delete is useful. I will definitely tell the reasons, it may be in the next two.

The Placement New operation and the Placement delete operation must appear. Generally, every one

Void * Operator New (std :: size_t, p1, p2, p3, ..., pn);

All correspond

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

According to this principle, the standard runtime is defined.

Void Operator Delete (void *, void *);

Take the Placement New operation I just mentioned.

1.5 array new and array delete

Based on the symmetry, the standard running library also declares that the Placement New [] operation and the Placement Delete [] operation:

Void * operator new [] (std :: size_t, void *);

Void Operator delete [] (void *, void *);

As you expected: Placement New [] operation returns the incoming address, and the behavior of the Placement delete [] operation is almost the same as the Placement delete operation behavior I have not described.

1.6 abnormal

Now we combine these new / delete and unusual. Consider this statement again:

B * p = new D;

What happens when it calls the New operation without assigning enough memory?

In the dark age of C (1994 and previous), the NEW operation will return NULL for most compilers. This was once a reasonable extension of the Malloc function of C. Fortunately, we are now living in the bright era, the compiler is strong, and the class is designed very beautiful, and the New operation of the compiler will throw it.

In front, I showed the declaration of 8 functions that appeared in . At that time, I did some small hands and feet; here is their full form:

Namespace STD

{

Class Bad_alloc

{

// ...

}

}

//

// new and delete

//

Void * Operator new (std :: size_t) throw (std :: bad_alloc);

Void Operator Delete (Void *) throw ();

//

// array new and delete

//

Void * Operator new [] (std :: size_t) throw (std :: bad_alloc);

Void Operator delete [] (void *) throw ();

//

// Placement New and Delete

//

Void * Operator new (std :: size_t, void *) throw ();

Void Operator Delete (Void *, Void *) throw ();

//

// Placement Array New and Delete

//

void * operator new [] (std :: size_t, void *) throw ();

Void Operator delete [] (void *, void *) throw ();

In these NEW operators, only non-Placement form will be throwing (std :: bad_alloc). This exception means that the internal deposit is exhausted, or other memory allocation fails. You may be strange why the Placement form does not leveled; but remember that these functions do not assign any memory at all, so they do not have a problem that can be reported.

There is no delete operation to throw it. This is not surprising, because DELETE does not assign new memory, just returns the old memory.

1.7 abnormal elimination

Compared to the form of New Operations, also declares that there is no need for overloaded versions:

Namespace STD

{

Struct nothrow_t

{

// ...

}

EXTERN Const Nothrow_t NothRow;

}

//

// new and delete

//

Void * Operator new (std :: size_t, std :: nothrow_t constatabase);

Void Operator Delete (void *, std :: nothrow_t constatabase);

//

// array new and delete

//

Void * operator new [] (std :: size_t, std :: nothrow_t const ");

Void Operator Delete [] (Void *, std :: nothrow_t constatabase);

These functions are also considered to be a New operation and the delete operation of the placement because they also receive additional parameters. Unlike the previous control target allocation location, these only let you distinguish the neutral new New and the never throwing New.

#include

#include

Using namespace std;

int main ()

{

INT * P;

//

// 'new' That Can Throw

//

Try

{

P = new int;

}

Catch (Bad_alloc &)

{

Cout << "'New' Threw An Exception";

}

//

// 'new' That Can't throw

//

Try

{

P = new (nothrow) int;

}

Catch (Bad_alloc &)

{

COUT << "This Line Should Never Appear";

}

//

Return 0;

}

Pay attention to the important differences between the two New Expressions:

P = new int;

Throw std :: bad_alloc while allocation fails,

P = new (nothrow) int;

Do not leveled when allocation fails, returns NULL (just like the NEW of Malloc and C Dark New Year).

If you don't like the syntax of Nothrow, or your compiler does not support, you can achieve the same effect:

#include

//

// function template emulating 'new (std :: nothrow)'

//

Template

T * new_nothrow () throw ()

{

T * P;

Try

{

P = new T;

}

Catch (std :: bad_alloc)

{

P = null;}

Return P;

}

//

// eXample usage

//

int main ()

{

INT * p = new_nothrow (); // Equivalent to 'new (nothrow) int

Return 0;

}

This template function has a potential abnormal security vulnerability with its significant New (Nothrow) expression. Now, I will leave it as the course to find it. (I am afraid that there is nothing to use: I am related to Placement delete.)

1.8 small knot

New and delete are monsters. Together with TypeID, they are only the keywords that will call the function in the standard run library in C . Even if the program does not explicitly call or define any functions outside MAIN, the New and Delete statements will cause the program to call the run. As I demonstrate here, call the run library will often throw or have an exception.

The code and annotations in this article are used for my explanation of C standards. Unfortunately, as I said before, Microsoft's Visual C often does not follow C standards. In the next article, I will reveal that the Visual C runtime supports NEW and DELETE support from the C standard. I will pay special attention to departing from the support of the abnormalities and will show them.

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

New Post(0)