Abnormal processing in C and C ++ 9

zhaozj2021-02-16  42

1. Placement New and Placement Delete, and the abnormality thrown by the constructor

When the call is called to clean the partial configuration, the first VOID * parameter of the Operator Delete is the address of the object (just returned by the corresponding Operator New). All additional placement parameters of Operator Delete match the values ​​of the corresponding parameters transmitted to the Operator New.

In the code, statement

P = NEW (N1, N2, N3) T (C1, C2, C3);

The effect is

P = Operator New (SizeOf (T), N1, N2, N3);

T (p, c1, c2, c3);

If the T (P, C1, C2, C3) constructor throws an exception, the program is secretly called

Operator delete (p, n1, n2, n3);

Principle: When a part of the object is released, the Operator Delete knows the context from the original NEW statement.

1.1 parameters for Placement Operator Delete

To prove this, enhance our example to track the corresponding parameter values:

// EXAMPLE 11

#include

#include

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;

}

//

// Non-Placement

//

Void * Operator new (size_t const n)

{

Void * const p = :: Operator new (n);

Std :: cout << "B :: operator new (" << n <<

") => << P << std :: endl;

Return P;

}

Void Operator Delete (Void * Const P)

{

Std :: cout << "B :: Operator delete (" << p <<

")" << std :: endl;

:: Operator delete (p);

}

//

// Placement

//

Void * Operator New (size_t const n, int const i)

{

Void * const p = :: Operator new (n);

Std :: cout << "B :: operator new (" << n <<

"," << i << ") =>" << p << std :: endl;

Return P;

Void Operator Delete (Void * Const P, Int Const i)

{

Std :: cout << "B :: Operator delete (" << p <<

"," << i << ")" << std :: endl;

:: Operator delete (p);

}

Private:

INT const id_;

}

Class A

{

PUBLIC:

A (): B1 (New (11) B (1)), B2 (New (22) B (2)), B3 (New (33) 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;

}

Compile and run with Visual C 6. The output on my machine is:

B :: Operator New (4, 11) => 007E0490

1 b :: b Enter

1 b :: b exit

B :: Operator New (4, 22) => 007e0030

2 b :: b enter

2 b :: b exit

B :: Operator New (4, 33) => 007E0220

3 b :: b Enter

Throw

B :: Operator Delete (007E0220, 33)

2 b :: ~ b

B :: Operator Delete (007e0030)

1 b :: ~ b

B :: Operator Delete (007E0490)

Catch

Pay attention to these numbers:

L 4 is the number of bytes of each assigned B object. This value is very different from different C achievements.

L such as 007E0490 is the address of the object returned by the Operator New, as a member function transmitted to T as the THIS pointer, and passed to Operator Delete as a Void * type pointer. The value you see is almost certainly different from me.

L 11, 22, and 33 are the additional placement parameters that were originally transmitted to the Operator New, and passed to the corresponding Placement Operator Delete during partially construct.

1.2 Handmade Call Operator Delete

All of these Operator New and Operator delete are very convenient, but it only occurs in part. For the usual complete configuration, Operator delete is not automatically called, but by indirect call by clear DELETE statement:

P = new (1) b (2); // Calls Operator new (size_t, int)

// ...

Delete P; // Calls Operator delete (void *)

Such order results are calling Placement Operator New and Non-Placement Operator delete, even if you have a corresponding (Placement) Operator Delete available. Although you are expected, you can't use this method to force the compiler to call Placement Operator Delete:

Delete (1) p; // error

And must write the delete statement to do things:

P-> ~ b (); // Call * P's Destructor

B :: Operator Delete (p, 1); // Call Placement

// Operator delete (void *, int)

To keep the behavior when you call Operator Delete is exactly the same, you must save the parameters passing through the New statement to the Operator New and handles them to the Operator Delete.

P = New (N1, N2, N3) B;

// ...

P-> ~ b ();

B :: Operator delete (p, n1, n2, n3);

1.3 Other non-Placement delete

Throughout this topic, I said that Operator New and Operator Delete are classified as follows:

Function pair

l void * Operator new (size_t)

l void Operator delete (void *)

It is a non-placement allocation and release function.

All functions of the following form

l void * Operator new (size_t, p1, ..., pn)

l void Operator Delete (void *, p1, ..., pn)

It is the Placement assignment and release function.

I said this is because it is simple, but I must now admit that Xiaolan:

Void Operator Delete (void *, size_t)

It can also be a non-placement release function that matches

Void * Operator new (size_t)

Although it has an extra parameter. As you guess, the SIZE_T parameter of Operator delete is the value that is transmitted to the SIZE_T of the Operator New. Unlike other additional parameters, it is an object that provides a fully constructed.

In our example, add this size_t parameter to a non-placement operator delete:

// EXAMPLE 12

// ... preamble unchanged

Class B

{

Void Operator delete (void * const p, size_t const n)

{

Std :: cout << "B :: Operator delete (" << p <<

"," << n << ")" << std :: endl;

:: Operator delete (p);

}

// ... rest of class b unchanged

}

// ... Class a and main unchanged

The resultS:

B :: Operator New (4, 11) => 007E0490

1 b :: b Enter

1 b :: b exit

B :: Operator New (4, 22) => 007e0030

2 b :: b enter

2 b :: b exit

B :: Operator New (4, 33) => 007E0220

3 b :: b Entertainment

B :: Operator Delete (007E0220, 33)

2 b :: ~ b

B :: Operator Delete (007e0030, 4)

1 b :: ~ b

B :: Operator Delete (007E0490, 4)

Catch

Note that for a fully constructed object, additional parameter 4 is supplied to Operator Delete.

1.4 obvious contradiction

You may be strange: The C standard allows non-Placement Operator delete to automatically know the size of an object, but denying the Placement Operator Delete can have the same capabilities. To keep them consistent, a Placement assignment function

Void * Operator New (size_t, p1, p2, p3)

Should be matched to such a Placement release function

Void Operator Delete (Void *, Size_T, P1, P2, P3)

But the fact is not this, these two functions do not match. Why is the language be designed like this? I guess two reasons: efficiency and clear.

In most cases, Operator Delete does not need to know the size of an object; forced functions to accept size parameters at any time. Also, if the standard allows the SIZE_T parameter to be optional, such a vague will result in:

Void Operator delete (void *, size_t, int)

There is a different meaning in different environments, decide which one will match:

Void * Operator New (size_t, int)

still is

Void * Operator new (size_t, size_t, int)

If you throw an exception by the following statement:

P = new (1) t; // Calls operator new (size_t, int)

The size_t parameter of Operator delete will be SizeOf (T); but if it is called

P = New (1, 2) t; // Calls Operator New (size_t, size_t, int)

The size_t parameter of Operator delete will be the first parameter value of the New statement (here 1). So, Operator Delete will not know how to explain its size_t value.

I estimate that you may want to know if there is a function of Placement

Void Operator Delete (void *, size_t)

At the same time, it matches as a Placement function.

Void * Operator new (size_t, size_t)

If it is allowed, Operator Delete will encounter the same problem. If it is not allowed, the C standard will require an exception to its rules.

I didn't find such an exception to the rule. I have tried several compilers, - incruding Edg's Front end, My Expert Witness on Such Matters - and considers:

Void Operator Delete (void *, size_t)

In fact simultaneously as a Placement release function and a non-placement release function. This is an important reminder.

If you doubt me, remove the Placement Operator Delete of Example 12.

// esample 13

// ... preamble unchanged

Class B

{

// void Operator Delete (Void * Const P, Int Const i)

// {

// std :: cout << "B :: Operator delete (" << p << // "," << i << ")" << std :: end1

// :: Operator delete (p);>

//}

// ... rest of class b unchanged

}

// ... Class a and main unchanged

Now, there is an operator delete that matches two Operator New. Its output results are still the same. (WQ Note: Conclusion is correct, but different compilers have a significant difference in Example 12 to Example 14, very interesting!)

1.5 end

Two final points:

l Through I :: Operator New and B :: Operator Delete discussion, I always declare the function as non-Static. Usually this claim means that there is a THIS pointer exists, but these functions are like they do not have this pointer. In fact, in these functions to try to quote this, you will find the code can't compile. Not like other member functions, Operator New and Operator delete are always static, even if you don't use Static keywords.

l No matter where I mention Operator New and Operator Delete, you can replace it with Operator New [] and Operator Delete []. The same mode, the same rules, and the same observations. (Although Operator New [] and Operator Delete [], the compiler will still allow you to define your own array version.)

l

I think this ends I have explained the role of PLCEMENT New and Delete and their anomalies thrown when the constructor is thrown. Next time, I will introduce you a different skill to tolerate the exception thrown by the constructor.

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

New Post(0)