C ++ starts from scratch (10) - What is class

zhaozj2021-02-16  100

C starts from zero (10) - What is the pre-class for the structure only defines a memory layout, and it is also possible to write Class, which is the type of custom type (Class), which type before it is defined. There is no difference between it (only a little difference, the next description), and the reason why it is necessary to provide a Class, which is actually because C is expanded from C, which is a very important concept of C you propose. This is just to keep Struct this keyword in order to be compatible with C language. However, the small differences mentioned in front brackets are also sufficient to see the different semantics of the C designer as the structure and class definitions, the following description. Temporary can first think that the more structured long-range progress is more than the concept of member functions (although the structure can be a member function), let's see a semantic demand before understanding the member function. Operation and resource program is mainly composed of operations and resource resources, the executor of the operation is CPU, which is normal, but sometimes there is some need, need to perform another resource (temporarily called operation For example, in the game, there is often what you want to map the monsters to attack the player. The reason is required, generally because this operation also needs to modify the operator or use some information recorded by the operator to complete the operation, such as the monster's attack power to determine the status after the player is attacked. This semantic is manifested as some functions of the operator. In order to achieve the above semantics, such as the original map, first mapping monsters and players are structures, as follows: struct monster {float life; float attact; float defend;}; struct player {float life; float attack; float defend; }; The above attack operation can be mapped to Void MonsterattackPlayer (Monster & Mon, Player & PLA). Note that the operator is expected to express the operator through the function name, but the previous article is named SLN, which belongs to the river scheme, belongs to the original inversion, because this semantic should be manifested by the type, not a function name. To this end, C provides the concept of member functions. The member function is before, the declaration statement writes a function in the type definition will define the member function, as follows: struct abc {long a; void ab (long);}; above defines a mapping element - the first one Variables ABC :: A, type long abc ::; and declare a mapping element - the second function ABC:: AB, the type Void (ABC ::) (long). Type modifier ABC :: In this, the function ABC:: AB is modified, indicating that it is a function type of offset type, ie a relative value. However, since it is a function, meaning, and variables, it is still mapped by the address (the address of the code), but because it is an offset type, it is relative, that is, it is incomplete, so it cannot be applied to it Operators, such as: abc :: ab (10) ;. Here will be wrong, because ABC:: Ab is relative, its opposite thing is not as a memory address as a member variable, but a structural pointer type parameter, the parameter name must be this, which is forcibly defined, followed.

Note Because its name is abc :: ab, and the above is only a declaration, it is necessary to define it, still the definition of the previous function, as follows: void abc :: ab (long d) {this-> a = d The name of the above function should be written as ABC:: AB, but the previously said member variables cannot directly write long ABC :: A;, it will not directly write the definition statement of the function (at least the function name ABC). :: AB does not meet the identifier rule), but must define the custom type first by type definition "{}" first defined, then write, which will be described in detail when the declaration will be explained later. Note that this keyword is used above, which is ABC *, automatically generated by the compiler, that is, the above function definitions actually equivalent to Void ABC :: ab (abc * this, long d) {this-> a = D; }. The reason why the THIS parameters is omitted and by the compiler are to reflect the semantics mentioned earlier on the code (ie, the meaning of members), which is why ABC :: ab is the offset type of the function type, It is relative to this THIS parameter, as is relative, as follows: ABC A, B, C; A.Abc ::Ab (10); B.Abc: :Ab (12); C.AB (14); The above uses the member operator to call ABC:: AB, pay attention to the execution, the values ​​of AA, BA and CA are 10, 12, respectively, ie, call ABC:: Ab, but through member operators, three THIS parameters The value is not the same, and the member variables of three ABC variables are modified. Note The above is written on A.Abc: :Ab (10); like a member variable, because the left and right types must correspond, it can also be A.ab (10) ;. It should also be noted that when defining abc :: ab, writing this-> = d in the function body;, in the same, because the type must correspond to the corresponding relationship, the THIS must be a pointer to the corresponding type, so I can omit this- > The writing, there is a void abc :: ab (long d) {a = d;}. Note that the action of the member operator is no longer returned to the number of the corresponding member variable type, but returns a function type number, but the difference is that this function type is not expressed in syntax, ie C and No keyword or type modifier is provided to express this returned type (the VC is provided inside the __thiscall this type modifier to represent, but it is still not available when writing the code, just the internal use of the compiler). That is, when the member operator is connected to the number of the offset type of the function type, the number of a function type is returned (indicating that it can be applied to the operator), and the type of function is given in the offset type. The type, but this type cannot be manifested. That is, A.ab will return a number, this number is a function type, which is Void (__thiscall abc ::) (long) in the VC, but this type is illegal in C . C does not provide keywords like __thiscall to modify the type, because this type is required to encounter a function operator and member operator, such as A.ab (10); to turn the member operator left The address is passed as a first parameter that is called, and then the remaining parameters given in the function operator are again transmitted.

That is, this type is for the particular situation of the function operator and member operator, providing some information to the compiler to generate the correct code, instead of modifying the number (modified number requires all situations). That is, the type is used to modify the number, and this type cannot modify the number, so C does not provide a keyword similar to __thiscall. As before, since ABC :: Ab is mapped by ABC :: Ab, it is not an offset value, so it can be abc :: ab; but not ABC :: A;, because the latter is a deviation value. It is easy to know according to the type of type, and you can have: void (abc :: * p) = abc :: ab; or void (abc :: * p) (long) = & attc :: ab; there is : Void (abc :: ** pp) (long) = & p; (c. ** pp) (10.0f) ;. The reason why parenthesis is because the priority of the function operator is higher than "*". Recovering the previous section said that the conversion of the pointer type is only the type change, the value is unchanged (the case where the following description numerical change), so there can be the following code, this code is meaningless, here is only a deepening to the member function . Struct abc {long a; void ab (long);}; void abc :: ab (long d) {this-> a = d;} struct AB {Short A, B; Void Abcd (Short Tem1, Short Tem2); Void abc (long Tem);}; void ab :: abcd (short tem1, short tem2) {a = tem1; b = tem2; B = SHORT (TEM - TEM / 10);} void main () {ABC A, B, C; AB D; (c. * (void (abc :: *) (long) & ab :: abc) (43 ); (B. * (Void (abc :: *) (0xAbcdef12); (d. * (Void (ab :: *) (short, short) ABC:: AB) (0xAbcd, 0xef12);} After the execution, CA is 0x00270004, Ba is 0x0000EF12, DA is 0xAbcd, DB is 0xFFFF. For the function call of C, since the address of the AB :: ABC map is directly converted to the type, the program will jump to a = short (TEM / 10) at AB:: ABC; start execution, and parameter TEM mapping It is the first address of the memory of the memory, and then use the long type to get the TEM 43, and then execute. Note B = Short (TEM - TEM / 10); actually this-> b = short (TEM - TEM / 10); and the value of this is C correspondence, but it is considered to be AB * type (because In the function of the function AB :: ABC), it can be this-> b. There is no B in the ABC structure), and the offset of B is 2, so the result 39 is stored in C after execution. The memory corresponding to the address is added, and the 16-bit binary number of bits obtained by the SHORT type is explained.

For A = Short (TEM / 10); also doing the same thing, so the value of C.A is 0x0027004 (decimal 39 is converted to hexadecimal 0x27). Similarly, for the call to B, the program will jump to the AB :: ABCD, but when the generated B call code, record the parameter 0XABCDEF12 in the memory of the parameter type LONG, then jump to AB :: Abcd . However, when compiling AB :: ABCD, the parameters of the parameter TEM1 and TEM2 corresponding to the parameters are mapped, so it is easy to think that the value of TEM1 will be 0xef12, and the value of TEM2 is 0xAbcd, but it is not true. How to pass the previously said function call rules determine, the specific implementation details of the function call are described in "C from zero (fifteen)", which only knows that the member function mapping is still the address, and its type decision How to use it, later explain. The statement of the statement has explained what is the meaning of declaration, where this new definition syntax is defined by the definition rule of the member function, must re-consider the meaning of the statement. Pay attention to a point, put a function definition to the main function definition, you can no longer have the function; if you define a variable, you don't have to declare that variable. This means that the definition statement has a declaration function, but the definition statement of the above member function does not have a declaration function, following the understanding of the true meaning. The statement is a statement that requires a compiler to generate mapping elements. The so-called mapping elements are the variables and functions described above, and only 3 columns (or 3 fields): type column, namebar, and address bar (the member variable type of the offset value). That is, the compiler whenever you see a statement statement, generate a mapping element, and empty the corresponding address bar, then leave some information to tell the connector - this .Obj file (the file generated after the compiler compiles the source file For the VC is .Obj file) require some symbols, find these symbols, modify it and improve this .Obj file, last connection. Recall the meaning of the symbols before, it is a string for communication between the compiler and the connector. Note that the symbol is not type because the connector is only responsible for finding symbols and perfect (because some mapping elements are still empty) intermediate files (for VC is .Obj files), there is no syntax analysis, there is no type. The definition is requested to require the compiler to populate the address bar where the previous declaration is not written. That is to say, the address corresponding to a variable is only known when it is defined. Therefore, the actual allocation of memory on the stack is done by the definition of the variable, so the variable that is declared does not allocate memory. However, it should be aware that the definition is to generate the address required for the mapping element, so the definition will explain the address of which map element it generates, and if the compiler is mapped within this time (ie, the compiler is inside the compiler before There is no mapping element in the variable table, function table, etc.) of the map element, that is, there is no statement of the corresponding elements, then the compiler will report an error. But only one variable or function definition statement is written in front, it does not report it normally? It is actually very simple, just think of declarations and definitions as a statement, just the information provided to the compiler. Such as: void abc (float); and Void ABC (FLOAT) {}, the compiler is similar to them. The former gives the type and type name of the function, so the compiler only fills in the names and types of the mapping elements. Due to only one ";", this function map is not given, so the compiler cannot fill in the address bar.

The latter gives the function name, the type, and the mapping code (empty composite statement), so the compiler gets all the information to be filled in and then fills the information of the three columns. The result shows the definition statement. The function of the declaration. For variables, such as long a; Also, the type and name are given here, so the compiler fills in the type and name. However, the variable corresponds to the first address of a piece of memory on the stack. This first address cannot be exhibited from the code (the previous function is in the address of the code corresponding to the corresponding function by writing the composite statement after the function declaration), and The inside of the compiler must be obtained by calculation, so the definition of writing calculations above the above, and the declaration of the required variable needs to be plus extern. That is, all the information that causes the compiler to perform internal calculations to draw the corresponding address to fill in all the information of the mapping element. It is inevitable that there is a mysterious deficiency, which is all because of the appearance of the custom type. Consider the definition of member variables, such as Struct ABC {Long A, B; Double C;}; above - Long ABC ::, long abc :: and double abc ::; given the name - ABC :: A, abc :: b and abc :: c; addresses (ie offset) - 0, 4 and 8, because it is a structural custom type, so that each member variable can be obtained. Offset. Three information is obtained, that is, all information of the mapping element can be filld, so the above can be calculated as a definition statement. For member functions, as follows: struct abc {void ab (float);}; the type -void (abc ::) (FLOAT) is given, given the name - ABC :: Ab. However, since the address is not given, all information of the mapping element cannot be filled in, so the above is the statement of the member function ABC :: ab. According to the previous statement, just give an address, without having to deal with it is defined or declared, so it can be said: struct abc {void ab (float) {}}; the type and name give Out of the address, therefore will fully fill in all the information of the mapping element, is defined. The above usage has its particularity, which will be described later. Note that if this is later written later, the definition statement is written later, that is, the error: struct abc {void ab (float)}}}}}}}}}}}}}}}}}} will be an error, reasons Very simple, because the latter only defines, it only provides information about ABC:: AB, but the address bar in the mapping element has been filled, so the compiler will say repeatly. Look at the definition of the member function separately, it gives the type VOID (abc ::) (FLOAT), gives the name ABC:: AB, but also gives the address, but why it only gives this information. ? First, the name ABC :: ab is not in accordance with the identifier rule, and the type modifier ABC :: must pass the type definition "{}" to add, which has been described earlier. Therefore, the information given above is: given an address, this address is the address of the type VOID (ABC ::) (FLOAT), the name of the mapping element of ABC :: ab.

As a result, the compiler finds such a mapping element, if there is, fill in the corresponding address bar, otherwise, it is wrong, that is, write a void abc :: ab (float) {} is wrong, and you must first pass the type definition "{}" Declares the corresponding mapping element. This is also the definition of the previously said that only fills the address bar and does not generate mapping elements. The declaration of the declaration is obvious, meaningful mapping (name pair address) is it, but what is the use of statement? It just generates a type of name, why not have the type of name? It just tells the compiler Do not make an error saying the variable or function is undefined? Everything has its meaning, first look at the code below. Extern "C" long abc (long a, long b); void main () {long c = ABC (10, 20);} It is assumed that the above code is written in A.CPP, compiling the generation file A.Obj, no problem. However, according to the previous description, the error will be incorrect because the symbol _ABC can not be found. Because the address bar corresponding to the name _ABC is still empty. Then add a new source file B.CPP to the project where A.CPP is located in the VC, and write code. EXTERN "C" float abc (float a) {return a;} Compiled and connected, now there is no problem, but I believe that you have seen the problem - the type of function ABC declares and the type does not match, but it is successful. ? Note that there is no type on the connection, there is no type, and only the symbol. The above uses "c" so that the A.obj request _ABC symbol, and B.CPP provides _ABC symbols, and the remaining is just the connector to put the address corresponding to the _abc in the _abc to improve A .Obj, last connecting A.Obj and B.Obj. So what is the result of the implementation of the function, this is described in "C from zero (fifteen)" in "C ), and here is that the compiler can still generate code even if there is no address. Function operator function - function call. The reason why it is because it must be given the type and name at the same time, because the type tells the compiler, how to generate a code when a operator involves a mapping element, how to generate a code to implement the function of this operator. That is, the digital multiplication of the two char types and the code generated by the two long types of digital multiplication; the function call code and VOID ABC (FLOAT) are different from the function call code for long ABC (long); That is, the digital type of the operator will result in different code generated by the compiler. So why put the definition of ABC in B.CPP? Because the compilation between the respective source files is independent, if it is placed in A.cpp, the compiler will find that there is already such a mapping element, but the type does not match, and it will be reported. In the B.CPP, make the connector to improve A.Obj, there is no type of presence, only the symbol. Continue below. Struct ABC {Long A, B; Void Ab (Long Tem1, Long Tem2); Void Abcd ();}; void main () {abc a; a.ab (10, 20);} By the above statement, here There is no definition of ABC :: AB, but it is still compiled, there is no problem. Still assume that the above code is in A.CPP, then add B.CPP, and write the following code.

Struct ABC {Float B, A; Void Ab (Long Tem1, Long Tem2); long abcd (float);}; void abc :: ab (long Tem1, long tem2) {a = tem1; b = tem2;} The function abc :: ab, pay attention to the previously said, because the function definition here is just defined, so you must write the type definition "{}" in front of it to allow the compiler to generate a mapping element. But more should be noted here to change the position of the member variable, which B is mapped 0 and A map is 4, and the type A, B is replaced by the type of float, more and A.CPP. . But there is no problem, the compilation connection is successful, A.ab (10, 20); after execution AA is 0x41a00000, AB is 0x41200000, and * (float *) & a.a is 20, * (flaot *) & a.b is 10 . Why? Because the compiler follows the type matches only in the currently compiled source file, compiling the map elements generated by other source files when compiling another source file. Therefore, the statement is bound to the type and name, and the name represents the number of address types of the type it associated, and the compilation of all operations in the subsequent code will be affected by the type of this number. That is, the statement is to tell the compiler how to generate code, which is not just a speech or function statement, but it is indispensable. It should also be noted that the ABC :: ABCD member functions in the above two files are different, and there is no definition of ABC :: ABCD in the entire project (ie, A.cpp and B.cpp), but still compile the connection. Because the declaration is not telling the compiler, how to generate a code. The header file has been described, if there is a custom type ABC, use it in A.CPP, B.CPP, and C.CPP, must be used in A.CPP, B.CPP, and C.CPP, each use ABC before redefining this custom type with the type definition "{}". If it is not careful as the definition written in A.CPP and B.CPP, it will generate an error that is difficult to find. To this end, C provides a pre-compiled instruction to help. The precompiled instruction is the instruction executed before compiling, which is explained by the precompiled translator. The pre-compiler is another program, in general, compiler vendors merged them into the C compiler and only one program. Here, the pre-compilation instruction contains the instruction-# include, which is a #include . It should be noted that the pre-compilation instruction must be occupied separately, and the is a file name enclosed in double quotes or sharp possessions, such as: #include "abc.c", # include "c: /abc.dsw" Or #include . Its role is very simple, is explained in an ANSI format or MBCS format in an ANSI format or MBCS format (About these two formats ") in an ANSI format or MBCS format (About these two formats")) Replace the location of #include in the position, such as the content of the file ABC.

Struct ABC {Long a, b; Void Ab (long Tem1, Long Tem2);};, the front A.CPP can be changed to: #include "abc" void main () {Abc A; a.ab (10, 20 );} And B.CPP can be changed to: #include "abc" void abc :: ab (long Tem1, long tem2) {a = tem1; b = tem2;} At this time, there will be no appearance in B The definition of the custom type ABC is wrong and the result of the definition of the defined type ABC (AA is 0x41a00000, AB is 0x41200000), and AA is 10, AB is 20. Note that the double quotes are used here, which means that when the enclosed only one file name or relative path does not give a full path, if the above ABC, first search the source file compiled at this time. The directory, then search the compiler, self-specific, including the directory (such as: C: / Program Files / Microsoft Visual Studio .NET 2003 / VC7 / Include, etc.), which generally put the header file of the SDK comes with the compiler ( Regarding SDK, it will be described in "C from zero (18)"), if you still have not found, an error (note, all the compilers provide some options to make the specified specified in addition to the above directory Directory, different compiler settings are different, here they don't have a table). If it is enclosed with a sharp bracket, the directory where the compiler is self-qualified first, the reply source is located. Why is it different? Just in order to prevent the file names from which you start, you have conflicted with the files of the compiler's contained directory, because once the file is found, it will no longer be searched. Therefore, in a general C code, if you want to use a custom type, the definition of the custom type is installed in two files. For the above structure ABC, two files should be generated, which is ABC. H and abc.cpp, where ABC.H is called the header file, and abc.cpp is called a source file. The header file is declared, and the source file is amplified by definition, the content of ABC.H is the same as the previous ABC, and the content of ABC.cpp is the same as B.CPP. Then, whenever the structure ABC is used in a source file in a source file, the beginning of that source file contains ABC.H, which is equivalent to bringing all relevant statements of the structure ABC into the compilation of the file, such as front. A.CPP is contained in the beginning to declare structure ABC. Why do you still have an abc.cpp? If the definition statement of ABC:: Ab is also placed in ABC.H, A.cpp To use ABC, C.CPP also uses ABC, so A.cpp contains abc.h, due to Abc :: ab of Abc :: ab Definition, generate a symbol? AB @ ABC @@ qaexjj @ z (for VC); the same C.CPP compiles to generate this symbol, then connect, the connector cannot determine which one is used due to two identical symbols. Infault.

Therefore, it is specifically defined, put the definition of the function ABC :: ab in Abc.obj, which will not be reported when only one symbol is generated. Note the struct abc {void ab (float) {}} above. If this is placed in ABC.H, since the definition of the function ABC:: Ab is given in the type definition, two identical symbols will appear, and the connection fails. In order to avoid this problem, C specifies the function defined by direct writing function definitions in the type definition in the type definition, for the space, the next introduction. Members The meaning of the member function is indicated by grammar. If it is very faint, don't tighten, it doesn't matter if it doesn't mean it, it doesn't mean it. The programmer is important is the ability to use the language of language rather than the level of understanding (though The latter is also very important). The semantics of the member will be described below. This article puts forward a semantic-some resource features, and the C custom type plus member operators "." And "->", it is easy to show it from the code. Symphony - dependency relationship. For example: A.B, C.D represents the B and C of A. Some resources have functions to be mapped to C , and this resource should be mapped into a custom type, and its features are mapped to the member function of this custom type, such as the most beginning mentioned monsters and players , the following: struct Player {float Life; float Attack; float Defend;}; struct Monster {float Life; float Attack; float Defend; void AttackPlayer (Player & pla);}; Player player; Monster a; a.AttackPlayer (player ); The above semantics is very obvious, the operation of the code is the monster A attack player Player, and Player.life represents the player's PLAYER's health. Suppose the definition of MONSTER :: AttackPlayer as follows: Void Monster :: attackplayer (Player & Pla) {pla.life - = attack - pla.defend;} The above semantics is very obvious: a monster attack player method is the player who will be attacked. The life value minus its own attack power to reduce the value of the defenders of the player. The semantics are very clear, the code is readily readable. And the original way of writing: Void MonsterattackPlayer (Monster & Mon, Player & Pla) {pla.life - = Mon.attack - Pla.defend;} Semantics of code performance: Monster attack player is an operation, this operation requires two resources, It is monster type and player type. This semantic did not show the idea of ​​our original intended performance, but another explanation of the attack function of the monster (with this point, it will be more suitable for "C from scratch (12)"), which is more suitable for performance Cash register. For example, the cashier is implemented by the collection, and the customer bought something at the counter, and the salesperson opened the document, and the customer will pay the bill to pay the bill.

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

New Post(0)