Preliminary Analysis of C ++ Object - Oriented Characteristics PART3

zhaozj2021-02-16  42

Chapter 2 package

2.1 Package and Significance

In general, the package is in front of us in the world in a simple manner. Buy a electric refrigerator, I don't have to know the process of the compressor inside, don't have to know more refrigeration process, what I want, just give it power, set the temperature, the rest of the thing, will not return I managed.

In terms of object-oriented programming, the package is to hide the details of the implementation, so that the class users use the functions of the class by the public interface. In C , it is very dangerous if the user can be securely modified by excuse to securely modify the internal data of the class by excuses. ). The encapsulation of the interior method can enhance the reusability of the code. The user is varying the function of the access class, but the implementation details of the class member function change, but as long as the structure is constant, all the customer code of all the classes do not need to change.

Many people think that Class's package effect in C , can also be implemented in c in C. In fact, it is not, see the code:

/

// Source Code 2.1

#include

Struct S_Test

{

INT (* p_add) (s_test *);

INT I;

}

INT Add (S_Test * P) {Return (P-> i);}

Class C_Test

{

PUBLIC:

c_test () {i = 0;}

INT add () {return i;}

Void show () {cout << i << endl;

Private:

INT I;

}

Void main (void)

{

s_test s;

S.I = 0;

S.P_ADD = add;

sp_add (& s);

Cout << S.I << Endl;

C_Test C;

C.ADD ();

C.SHOW ();

}

// Define structural s_test // containing two members // p_add to point to the function of the function // i is shaped

/ / Define the function of the pointer to the structural S_TEST

/ / Define class c_test

// Constructive Function to Internal Data Members Initialization // Member Functions Add () Complete the 1 Operation // Member Function SHOW () to display the value of internal data member i

// internal data member

// Creating the data member I assignment // of Structures S // to initialize the P_ADD pointer to point to Int add (s_test * p)

/ / Call Add (S_Test * P) by pointing to a function of the function

/ / Output I value

// Create a class c_test's object C

// Call the Add () member function

// Use show () to output i value

By observing the above code, we can find that Struct can "pack" with the operation, but this packaging is loose and unsafe. First, the Struct does not access the control mechanism, and any code can operate its internal data i, secondly, the function indicated by the structural body pointer does not necessarily contact the structure itself, and any code can call the Add (S_Test * P) function. Struct is just a framework, all members of the structure are visible and accessible. Look at the class, separation of the excuses and internal data by using the Class and Private keywords. Access to internal data is implemented by member functions, such as initialization of INT i, changing the value of i, first is output, etc., all implementation package.

We can draw this conclusion

a. The role of the package is to provide interface B. Package can protect internal data

c. Encouffily of packaging can code

2.2. Implementation mechanism of package

2.2.1 Call Method for Class Member Function

In the first part of the article, we explore the memory pattern of C Class, then, between the package and its memory pattern, what kind of contact? What is the access to internal variables?

By observing Figure 1-2, we know that only data members are included in the instance of the class, and the member function of the class is excluded from the object package. The compiler distinguishes each member function through the "Class Name :: Function Name". Such a memory layout makes people think that Class is a method of processing a member function, similar to the function pointer of Struct in the last program. Let's first look at the classification method of the class to the calling method of its member function and the function pointer used in Struct, see the code segment:

Void main (void)

{

s_test s;

S.I = 0;

S.P_ADD = add;

sp_add (& s);

Cout << S.I << Endl;

C_Test C;

C.ADD ();

C.SHOW ();

} // Full code See Source Code 2.1

// When you call Add () through the function pointer, we pass the structure pointer to the function, // enable the add () function to access and operate data through this pointer.

// Call the class's member function, no need to transfer pointers

We know that c_test can be created many instances, but its member function has only one in memory, then this member function does not have a parameter incorporated, how to distinguish these C_Test's instance? When calling a class member function, don't you have a parameter incoming? Let's take a look at the compiler for the binary code generated by the C.ADD (); this, any hidden mechanism will be at a glance.

When C.ADD () is called in the main function, the system generates the following code, where

00401050 LEA ECX, [EBP-4]

00401053 Call @ ilt 5 (c_test :: add) (0040100A)

Call @ ilt 5 (c_test :: add) (0040100A) will control the steering address 0040100A, here is a jump instruction

0040100A JMP C_TEST :: Add (004010c0)

Address 004010c0 is the real entry address of the function c_test :: add, let's take a look at the code at 004010c0

004010c0

004010C1

004010C3

004010C6

004010C7

004010C8

004010C9

004010CA

004010CD 004010CD 004010CD 004010CD

004010d2

004010d7

004010d9

004010DA

004010dd

004010E0

004010E2

004010E5

004010E8

004010EA 004010EA 004010EA 004010EA

004010ed

004010EF 004010

004010f0

004010F1

004010f2

004010F4

004010F5

push

MOV

Sub

push

push

push

push

LEA

MOV

MOV

Rep Stos

POP

MOV

MOV

MOV

Add

MOV

MOV

MOV

MOV

POP

POP

POP

MOV

POP

RET

EBP

EBP, ESP

ESP, 44H

EBX

ESI

EDI

ECX

EDI, [EBP-44H]

ECX, 11h

Eax, 0ccccccch

DWORD PTR [EDI] ECX

DWORD PTR [EBP-4], ECX

EAX, DWORD PTR [EBP-4]

ECX, DWORD PTR [EAX]

ECX, 1

EDX, DWORD PTR [EBP-4]

DWORD PTR [EDX], ECX

EAX, DWORD PTR [EBP-4]

Eax, DWORD PTR [EAX]

EDI

ESI

EBX

ESP, EBP

EBP

The above code is the specific implementation code for c_test :: adde.

Through the analysis of the above assembly code, we can find that the add () function has a hidden parameter, which is a parameter to the class instance (ie, the address of the C) function, therefore, actually compiler The call to C.Add (); the call is converted to the following code:

Test :: Add ((test *) & c);

INT add () {return i;} function is actually

Int Add (Test *) & this)

{RETURN ((Test *) & this) .i;}

Here, hidden parameters (Test *) & this is what we often say. We will find that this approach is similar to the method of pointer to the function in the structure (S.P_ADD (& S);), but this step in C is implemented by the compiler, usually the user does not need to the THIS pointer. Operation. This is the benefit of the package, which is complex and easily erroneous to the compiler.

2.2.2 Performance of Package

The package is hidden in the implementation of the content and behavior of things, and the user does not need to know its internal implementation. But there must be lost, sometimes we can't guarantee the efficiency of the package. C does not have a performance loss in the realization of the package? If there is any words, how much?

Please see the following test programs and corresponding code annotations

//

// Source Code 2.2

// C Class package performance

Const Double Count = 1000000;

#include

#include

#include

Void showtime ()

Class test {

PUBLIC:

TEST () {i = 0;}

INT foo () {return 0;}

Double I;

}

INT foo () {return 0;}

Double i = 0;

Void main (void) {

Double J = 0;

Test T;

Showtime ();

For (j = 0; j

Showtime ();

For (j = 0; j

Showtime ();

For (j = 0; j

Showtime ();

For (j = 0; j

Showtime ();

i ;

T.I ;

Foo ();

T.foo ();

} // Test 800MHz CPU in Intel Celeon

// VC 6.0, Windows 2000 environment complete

/ / Define the number of cycles

/ / Output millisecond accuracy time function, implement code

// loop control variable

// Test class instance

// The following code is a performance test, output loop running time

// Time accurate to millisecond, i and T.i run time

// Time accurate to millisecond, foo () and t.foo () call run time the same // code test, output the actual assembly code for variables and functions

// 0040128E FLD QWORD PTR [i (0042f220)]

// 00401294 Fadd qword ptr [__real @ 8 @ 3FFFF8000000000000000 (0042B050)]

// 0040129A FSTP QWORD PTR [i (0042f220)]]

// 004012A0 FLD QWORD PTR [EBP-10H]

// 004012A3 fadd qword ptr [__Real @ 8 @ 3FFF8000000000000000 (0042B050)]

// 004012A9 FSTP QWORD PTR [EBP-10H]

// 004012ac call @ ilt 35 (foo) (00401028)

// Address 00401028 is a hopping instruction, pointing to the entrance to the foo () function

// 004012B1 LEA ECX, [EBP-10H]

// 004012B4 Call @ ilt 40 (Test :: foo) (0040102D)

/ / Address 0040102D is a hopping instruction, pointing to the entrance to the Test :: foo () function

From the above results we can know that the time to access the data and functions encapsulated in the class is the same as the function of accessing the uncapacitated data. From the above-mentioned disassembled code we can find that the program is exactly the same as the package data and unpacking data, the level of assembly code is exactly the same. Let's see the access to the function.

Call the assembly code generated by the foo ()

004012ac Call @ ilt 35 (foo) (00401028)

And the code generated by the call class member function is

004012B1 LEA ECX, [EBP-10H]

004012B4 Call @ ilt 40 (Test :: foo) (0040102D)

Does this instruction cause performance problems? The time test to tell us that the time of these two functions is the same, and for a function call process, the compiler produces at least hundreds of assembly code, so more instructions here are in consideration of performance issues When it is completely ignored. So what is the role of this instruction? In the call method of 2.2.1 member functions, I analyzed the role and implementation of the THIS pointer, this command, the specific details, please refer to Section 2.2.1.

Through the above analysis, we know that the package does not cause additional overhead in the ordinary member variables and ordinary member functions. Refer to the C Class Memory Pattern section, we can easily discover the package to the static member does not have any performance overhead. So, what about the virtual function? This is indeed a problem.

The system access virtual function is implemented through the pointer index virtual function table, and considering that the derived class has been rewritten on the virtual function, the virtual function table is actually binding in the runtime. The detailed analysis of the virtual function will be expanded in the polymorphism.

Depending on the authority test, C is more than 5% compared to the same C language than the performance loss caused by the package. 5% performance loss, in exchange for object-oriented programming, I think any programmer will make a wise choice.

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

New Post(0)