C ++ from scratch (11) Next - Class related knowledge

zhaozj2021-02-16  83

C from scratch (11) Next - Class-related knowledge Due to space limit, this article is "C from scratch (11)", discussing polymorphism and some remaining issues. The meaning of the virtual meaning this article has introduced the meaning of the virtual, which is to be indirect, and for example, the channel of the television is to make people get the TV station frequency, so it is virtual since this sense because it may operate Failure - A channel has not been adjusted to cause a piece of snow. And the indirect advantage is to use only a piece of code (press 5), there may be different results each time it executes it (today 5 channels are set to the central 5, tomorrow can be set to the central 2) Furthermore, the procedure (according to 5 channels) is flexible. Note that it can be flexible because it must be indirectly achieved by "a means", such as each channel records a frequency. But this is not enough, there must be "another code" to change the result of the means (the frequency of channel records), such as tape. First look at the virtual inheritance. It indirectly obtains the location of the parent class instance in an instance of the subclass, through the virtual class table (this is a means "), then it must be able to have" another code "to change the value of the virtual class table to express Its flexibility. First, you can write this code yourself, but you will ask a clear compiler to place the virtual class table, and different compilers have different implementation methods, then the code compatibility written is very poor. C of course gives "another code", that is, when a class is multiple times in the same class inheritance system, the value of the false class table is changed to make the parent class instance of each sub-class. One. This function is very poor, just to save memory. Such as: struct a {long a;}; struct b: virtual public a {long b;}; struct c: virtual public a {long c;}; structure d: public b, public c {long d;}; here There are two virtual class tables, inherited from B and C, in which the compiler writes the necessary code to correct the two virtual class tables to correct D, The examples obtained by the virtual class tables that are inherited by C are the same. Look at the virtual function. Its address is available indirect, implemented through virtual functions (this is a means "), and then you must change the content of the virtual function table. Also, if it is rewritten, the compatibility of the code is poor, and C also gives "another code", as above, by filling out the virtual function table in the derived constructor, writing according to the current derived class Virtual function table. It must populate a virtual function table to the current derived class, type, name, and the function of the function that is defined as a virtual function as much as possible as much as possible. Such as: struct a {Virtual Void ABC (), BCD (FLOAT), ABC (FLOAT);}; Struct B: Public a {Virtual Void ABC ();}; struct c: public b {void abc (float), BCD (Float); Virtual float ccc (double);}; struct d: public c {void abc (), abc (float), bcd (float);}; in A:: A, two A :: ABC Fill in the virtual function table of a A :: BCD.

In B:: B, b :: ABC and inheritance B :: BCD and B :: ABC are filled in the virtual function table of B. In C :: C, fill C :: ABC, C :: BCD and inheritance C :: ABC into the virtual function table of C, and add an element: C :: CCC. In D:: D, two D:: ABCs and a D:: BCD and inherited D :: CCC are filled into D's virtual function table. The D here is in turn inherit from A, B, C, and does not generate two virtual functions because of multiple inheritance, only one virtual function table. Although the member functions in D do not use virtual modification, their address is still filled in D's virtual function table, because virtual just means indirect access to its address when using that member function, and is not filled in the virtual function table relationship. Why should the TV use channels to indirectly get the frequency of TV stations? Because the frequency of the TV station is not easy, and if you know a frequency, slowly adjust the capacitance value of the constant capacitor to reach the frequency efficiency low. For 10 sets of concerted circuits, the capacitance value of each set is no longer moved, and the fast conversion frequency is achieved by switching different hemourism circuits. This indirect can also improve efficiency. Also, the 5 channel is originally the central government. Later, it was tired to change it into a central 2, and the same action (according to 5 channel) will produce different results, "according to 5 channels" this program is very flexible. From above, at least it is possible: indirectly for simplifying operations, improving efficiency and flexibility. The three uses mentioned here are based on such an idea - with "one means" to achieve the purpose, with "another code" to implement the usage of the above. The virtual success and virtual function provided by C , as long as the virtual success, the "one means" is completed. To achieve "another code", it can be seen from the above description that needs to be achieved by derived means. In derived class definitions and virtual functions in the parent class, the same function can change the virtual function table, and only the type of inheritance of the derived class can change the type of emotion inheritance can change the virtual class table, and it is only all Point to the same instance of the same class being inherited, the modification of the virtual function table is convenient and flexible, so the virtual inherit is not used, and the virtual function is often used. The virtual use is the way to generate the "virtual" in C needs to be derived, and the derived is the type of generator, so "virtual" is generally mapped into indirect instead of the above channel (a group of equal communication circuits) Indirect to achieve. Note "Simplified Operation" is actually referring to the complex operation of the function to simplify the writing of the code, and use the address of the function name to indirectly perform the corresponding code. For the virtual function, it is a modified form to show a variety of execution results. "Improvement efficiency" is an algorithm, that is, the channel is achieved by repeating the ten sets of all-hunting circuits, and the authenticity of the empty space change is not the type of indirect. Therefore, "virtual" in C can only increase the flexibility and simplification of the code (for the three indirect benefits mentioned above). For example, the animal will call, different animals are different, and the sounds of the sound are different. This is the type of "a means" (called) to express different effects (the name of the cat and dog), and This requires "another code" to achieve, which is achieved by derived. That is, from class Animal derived class CAT and class DOG, the corresponding GNAR member function is achieved by the "Call (GNAR)" is declared as the virtual function in Animal, and then the corresponding GNAR member functions are achieved in CAT and DOG.

If you have achieved different effects in the calls of Animal :: GNAR, as follows: Cat Cat1, Cat2; Dog; Animal * PA [] = {& Cat1, & Dog, & Cat2}; for (unsigned long i = 0; i < SizeOf (PA); i ) PA [I] -> gNAR (); The above container Pa records a reference for a series of Animal instances (with reference, refer to "C from zero (eight)"), its semantics It is this 3 animals. As for anything, it is not known. (Just like this TV has 10 channels, as for each station, I don't know), then ask these three animals to be called once. (Call animal :: gnar), the result will send a cat called, dog call and cat. This is the addition flexibility before, also known as polymorphism, refers to the same Animal :: GNAR call, but exhibits different forms. The above for cycle is no longer written, it is "a means", and wants to change its performance effect, use "another code", which is to derive different derived classes and derive the instance of the derived class. The reference is placed in the array PA. Therefore, a member function of a class is declared as a virtual function, indicating that the corresponding function of the resource mapped in this class should be a method of use, not an implementation. As "called" above, it means that it is necessary to "call" without giving parameters, nor has returned values, and call directly. Therefore, consider the previous radio and digital radio, there is a function of which the corresponding function should be declared as a virtual function, to indicate that it is to be turned on, give the frequency increment or reduction, and digital adjustment The implementation of the Taiwan and ordinary tower is obvious, but no matter whether it is. It means that people who use radios do not care about how to achieve it, only care about how to tease. Therefore, the definition of the virtual function indicates that the function is not important. It is important to declare the function. The virtual function is only realized in the derived class, and the parent class gives a definition of virtual functions. Therefore, C gives a special syntax to allow the definition of the virtual function, the format is simple, and "= 0" is added after the declaration statement of the virtual function, it is called a pure virtual function. As follows: Class Food; Class Animal {Public: Virtual Void GNAR () = 0, Eat (Food &) = 0;}; Class Cat: Public Animal {Public: Void GNAR (), Eat (Food &);}; Class Dog: Public animal {void gNAR (), Eat (Food &);}; void cat :: gNAR ()} void cat :: eat (food &) {} void :: gnar ()} void :: Eat (Food & ) {} Void main () {cat Cat; Dog Dog; Animal Ani;} On the statement, "= 0" is written behind the statement to indicate that the elements it map is not defined. What is the difference between this and not writing "= 0"? Directly declare Animal :: GNAR can also be defined. Note the above Animal Ani; will report an error, because in Animal :: Animal, you need to populate the virtual function table of Animal, and it needs an address of an iMal :: GNAR.

If it is an ordinary statement, it will not be reported here because the compiler will consider Animal :: GNAR definitions in other files, and the latter connector will process. But here "= 0" is used to inform the compiler that it is not defined, so the above code will fail, and the compiler has determined that there is no definition of animal :: gNAR. But if you add animal :: gNAR, what is the definition of Animal :: GNAR? Animal Ani; still error, because the compiler has already determined that there is no definition of animal :: gNAR, and the function table does not view the generation of the Animal instance, so it is useless to give animal :: gNAR. However, the map element Animal :: GNAR is now filled in the number, so there is no problem when cat.animal :: gNAR (); If you do not give the definition of Animal :: GNAR, Cat.animal :: GNAR (); there is no problem, but it will be reported when the connection is connected. Note the above dog :: gNAR is private, and Animal :: gnar is public, resulting in dog.gnar (); will report error, while dog.animal :: gNAR (); no error (because it is virtual function Still calling DOG :: GNAR), that is, the so-called public, etc., is not related to the type, just a syntax. There is also a Class Food; don't use it whether it is a statement or defined, just look at it provides information, there is only one - a type name of the name is Food, which is a type of custom type. When the statement is animal :: Eat, the compiler also only knows that Food is a type name instead of the programmer, because it is not used here, because there is no use of Food. The above Animal is referred to as a pure virtual basis. The base class is the top level of the class inheritance system; the virtual base class is the base class with a pure virtual member function; the pure virtual basis is no member variable and the non-purely members function, only the base class of the purely members function. The above Animal defines a rule, also known as an agreement or an interface. That is, an animal can be GNAR, and an instance of a FOOD must be given when EAT, and EAT must give an example, indicating that animals can eat food. That is, animal is an instruction manual, indicating the function of animals, and its examples have no meaning, and it does not generate an instance because of the use of pure virtual functions. What if the Gner and Eat above are not a pure virtual function? Then they must be defined, and the animals are no longer an abstract concept, but there can be examples, it can have such animals, it is an animal, but it is not any specific animal (neither cat also Not a dog). Obviously, such semantics and pure virtual bases are far from the difference. So what? Members of the class that are virtual inheritance will be indirect, which is its "one means", which means that the members of the class that are virtual inherit will operate different memory due to different offset values. However, the modification of the virtual class table is limited to the repeated appearance, then modify into indirect operation of the same instance, so since the fundamental virtual inherit is to solve the problem of two hunger, it is only the meaning of itself. Implementation of an algorithm. This leads to the design of marine life and dull milk, it is impossible to determine whether to have a false father, and whether there is a situation like a whale in the derived class. If there is, it will fall back to marine life and The milk mold is designed to inherit from animals, which is not a good phenomenon.

Static (static) said in "C from scratch (5)", static is that there is no change every time, and the dynamic is possible to change every run. C gives Static keywords, like the PUBLIC, VIRTUAL above, just a symptom identifier, not type modifier. It can act in front of the member to indicate that this member is constant for each instance, as follows: Struct a {static long a; long b; static void abc ();}; long a :: a; void a :: abc () {a = 10; b = 0;}; void main () {a a; aa = 10; AB = 32;} The above A:: A is the static member variable of structure A, A :: ABC is a static member function of A. What changes? The above mapping element A :: A will no longer be long A :: but LONG. The same type A :: ABC also becomes Void () instead of VOID (A ::) (). First, members should be static, that is, the member variable is the same as the address of the memory identified by each instance, and the member function is constant for modifying the memory of each this parameter. of. The above turns A::: A and A :: ABC into normal types, rather than offset type, eliminates the dependence of the instance of A, which in turn implements the static above. Since the elimination of examples on the example, that is, the member function removes the THIS parameters, the member variable maps the exact memory address and no longer offset, so Struct A {static long a;}; just a variable A:: a Declaration, its name is A :: A, type is long, the address of the map is not given, that is, it is not defined, so it must be defined in the global space (ie, not in any function), thereby there is long. A :: A ;. Similarly A :: ABC type is void (), is removed from the parameters, thereby in A :: b = 10; equivalent to A :: b = 10; found A :: B is an offset type, The THIS parameter is required, it is equivalent to this-> a :: b = 10; Results A :: ABC does not have this parameter, error. For A = 10;, it is equivalent to A :: A = 10;, there is no problem with this variable. Note the above A.a = 10; equivalent to A.A :: A = 10;, then A:: A is not an offset type, isn't there any error here? This type of type C allows this type of mismatched, "a." Is equal to no, because this is a static member we want to behave. That is, A A, B; A.A = 10; B.A = 20; After execution, A.A is 20, because no matter which example, the memory identified by the same address to the operation of the member A:: A. The memory identified by the same address. What is the meaning? The difference between them and ordinary variables is that the name is defined by A ::, which in turn can exhibit their dedicated to class A. For example, the height and width of the door of the house are set, there are two houses to make a company, the height and width of their doors, so the height and width of the door should be the house of the company Static members are recorded in the actual height and width, but they do not need to change due to the different examples. In addition to members, C also provides static partial variables.

The local variable is the variable in the function, which is enclosed in a pair of "{}", which is limited to the variables of the field. For functions, each call function is called, because the local variables in the function are allocated in the stack, these variables are actually some relative values, and each call function, may result in actual corresponding due to the reasons of the stack. The address is different. As follows: void abc () {long a = 0; A ;} VOID BCD () {long d = 0; ABC ();} void main () {abc (); bcd ();} The above main adjustment ABC The address corresponding to the generated local variable A is not the same as the address generated by the BCD, and the address generated in the BCD is not the same, the principle is described in "C from zero (fifteenth)". Therefore, the static partial variable indicates that the address of the variable is not changed whether it is to call it. As follows: void ABC () {static long a = 0; A ;} void bcd () {long d = 0; D ; ABC ();} void main () {abc (); bcd ();} The above variables A The address is a fixed value, not the original relative value. This is the same as the address of the variable A obtained from the MAIN and adjusting the ABC from the BCD. The above is equivalent to: long g_abc_a = 0; void abc () {g_abc_a ;} void bcd () {long d = 0; D ; ABC ();} void main () {abc (); bcd ();} therefore The initialization of the static local variable A in the above ABC actually did it before it was executed, rather than the first time called ABC, and then the above code was executed, the value of ABC was 2, Because of the two calls of ABC. Its meaning? Indicates that this variable is only used in this function, and its lifetime needs to exceed the function of the function. It does not provide any semantics (because "the" available "is used in this function" can be done using local variables), just when some algorithms need to use global variables, and this algorithm is mapped to a function The use of static variables has a good naming effect - both the survival of global variables and the semantics of local variables. The Inline (embedded) function call is less efficient, and the parameters need to be stored according to the call rule before calling, and then the memory of the storage parameters is passed, but also the address when the call is called to ensure that the function can return to the call (about details) Discuss in "C from scratch (fifteen)"), but it can reduce the length of the code, especially the function body is relatively large, and the code is called in the code, which can greatly reduce the length of the code ( It seems to cycle 10 times, if you do not write a loop statement, you need to copy the code in the cycle body 10). However, it may also be poured, and the number of call times is small and the function is small. The reason why the map is also mapped to the sense of semantics. At this time, it may be more focused on execution efficiency rather than the code length, and the Inline keyword is provided for this C . When the function is defined, write inline in front of the definition statement, indicating that when this function is called, the call is not written, pass the code, and the function body of this function is expanded at the call. It seems that the previously said the code in the cyclic body is two times.

This will not be used to work, and the execution efficiency of the code will be improved, but the length of the final generated code may become due to excessive deployment. As follows: void abcd (); void main () {abcd ();} inline void abcd () {long a = 0; A ;} The above ABCD is the inline function. Note that the declaration of ABCD does not write Inline, because inline is not a type modifier, it just tells the compiler to record some information more when generating this function, and then the connector is disconnected according to this information. Note is "as appropriate", that is, the compiler may be sufficiently intelligent to find that there is too much call to the corresponding function when it is connected, and it is not suitable for deployment. In this regard, different compilers give different processing methods. For VC, it provides a keyword __forceinline to indicate that the corresponding function must be expanded, and do not need to be used to call. As mentioned earlier, the compiler will look into the inline function for the function definition written in the type definition. Once the Inline function is turned, it is not necessary to give a definition of the function due to multiple intermediate files. I don't know which definition should be used, because all the functions of these functions no longer need the address of the function, the function will Directly start there. The height and width of the door of a house in a company should be a static member variable, but it is obvious that the height and width of the door will change during the entire period of the instance of the house. C proposes this type of modifier. It is modified to indicate that the number of address types modified by the type cannot be used to write, that is, the number of address types If the const type will only be read, it cannot be modified. Such as: const line a = 10, b = 20; A ; A = 4; (Note You cannot cosnt long a; because the subsequent code cannot modify A, but the value of A cannot be changed, then a is meaningless) . Here A ; and A = 4; all will be an error, because the type of A is COSNT LONG, which represents the value of the memory corresponding to the address of A. A ; and A = 4; do you want to change this value. Since constling is a type, there is also a const line *, indicating a pointer type constling, so it matches the type, there is: const line * p = & b; p = & a; * p = 10 ;. Here P = & a; in accordance with the type matched, P is a constant long-type pointer, without any problems. However, * p = 10; will report an error, because * P converts P's digital to the address type, it will become the address type of the constant long type, so it is written to the operation error. Note: constling * const p = & a; p = & a; * p = 10; , The value indicating that the memory corresponding to the address of the P is not modified, so the latter P = & a; will errors, violate the meaning of const. The same * p = 10; also errors.

However: long a = 3, * const p = & a; p = & a; * p = 10; the type of P = 10 is long * const, a constant of the long type, so it must be initialized. Subsequent P = & A; will report an error, because P is long * const, but * p = 10; there is no problem, because there is no problem after turning long *. So there are also: const line a = 0; const line * const * pp = & p; as long as you follow from left to right modified order, all const modifications are due to the content operator "*" The conversion becomes the type in the left side of the pointer type modifier "*", so the type of PP is constling * const, * P is const line. It should be noted that C also allows you: Struct A {long a, b; void ABC () const;}; void A :: abc () const {a = 10; b = 10;} Top A :: ABC Type For VOID (a ::) () const, it is equivalent to: Void A_ABC (const A * this) {this-> a = 10; this-> b = 10;} therefore above A = 10; and b = 10 Will report an error because the THIS type is const a *. The meaning of the above means that the value of the member variable cannot be modified in the function A: ABC, because the parameters of each THIS become const a *, but can modify the values ​​of the static member variables of the class, such as Struct a {static long C; long A, B; Void ABC () const;} long a :: c; void A :: ABC () const {a = b = 10; c = 20;} Is equivalent to: void a_abc (const a * this) {THIS -> a = this-> b = 10; a :: c = 20;}. Therefore, you can still modify the value of A :: C. has no meaning? For the space, please refer to another article "Semantic needs" for the semantics of Const. Friend (Frieist) The signal has the function of transmitting electric waves. The trip has the function of receiving radio waves, and three classes of the signal, the consignor, and the radio wave, first send the traffic to the electric wave, Some of the members variables of the electric waves can be modified, but these members of the wave should be protected, otherwise it will receive or modify the information carried by the electric wave. Similarly, the consignment needs to receive radio waves, and some of the electric waves can be accessed, which is troublesome. If two public member functions are defined in radio waves, let the sender and the consider can access the members of Protected by protected. This is also a problem that many people have committed, and since the traffic can modify the members of the electric wave through the public member function, can the stone can modify the electric wave with that member function? This is equal to the original, there is no door, but there is no lock. In order to eliminate this problem, C proposes the concept of friends.

When defining a custom type, a custom type or a function is declared in the type definition "{}", in the front of the statement or definition statement, such as: Class Receiver; Class Sender; Class Wave {Private: Long B, C; Friend Class Receiver; Friend Class Sender;}; Declaring WAVE's two friend categories to indicate that Receiver and Sender have WAVE qualifications, as follows: Class a {private : long a;}; class wave: public a {...}; void receiver :: abc () {Wave WAV; wav.a = 10; WAV.B = 10; WAV.A:: a = 10;} Receiver is a friend of Wave, so you can directly access Wave :: A, Wave :: b, but wav.a: a = 10; will report error, because A :: A is A Private members, Wave does not have permission to ask it, and Receiver's permissions are equivalent to Wave, so they are not enough. Similarly, you can also have a friend function, that is, give a declaration or definition of a function. Plus Friend before the statement, as follows: Class Receiver {public: void abc ();}; class a {private: long a; friend void receiver :: abc ();}; This, the Receiver :: ABC is used as a friend function of A, which is all permissions with class A in Receiver :: ABC. Be careful to give the idea of ​​the information, it is also possible to: class a {private: long a; friend void receiver :: abc () {long a = 0;}}; here define the function received :: ABC, due to It is defined in the type definition, and it has been said before, and the Receiver :: ABC will be modified as the Inline function. So what is the meaning of friends? A operation requires a member of the protected in two resources simultaneously, and this action should be mapped as a friend function. If you need to use two resources and chapters of the file, the function of the stamp map should be a function of the file and chapter. The name space indicates a static member variable, and its semantics are an instance dedicated to a class and independent of class. It is the key to the global variable. The name is more than a qualifier (ie "::", which means the dependency ), Such as a :: A is a static member variable of A, then A :: A. This name can show A from A. Therefore, in order to express this dependency, it is necessary to define the variable as a static member variable. Consider a situation, mapping the mining. However, in terrestrial mining and under the seabed mining, what should I do? Mapping two functions, named MiningOnland and MiningOnSeabed, respectively. Ok, then you need to map on land exploration and in submarine exploration, what should I do? Map is Prospectonland and ProSpectonseabed.

If you need to map in terrestrial drilling and drilling in the sea floor, what should I do in the land blasting and blast in the sea? Obviously, here is the name of the name, and the use of static member functions is more unreasonable, and the namespaces are provided for this C , the format is Namespace {}. Where is the name of the defined namespace, and is a plurality of declarations or definition statements. As follows: Namespace online {void mining (); void proid art (); void artesianwell () {void mining (); void proid prospect (); void proid artesianwell () {}} void online :: mining ()} Long a = 0; A ;} void online :: proid () {long a = 0; a ;} void overseabed :: mining () {long a = 0; a ;} void overseabed :: proid () {long a = 0; A ;} Top 6 elements are defined, each type of Void (). Note The definition of Onland :: Artesianwell and OnSeabed :: Artesianwell is written directly in "{}" and will be the inline function. Such a definition of the six variables have a qualifier, which can reflect the dependence from the name, the semantic performance is better than the original, and ONSEABED :: ProCT is represented in the seabed exploration. Note that you can also follow: namespace a {long b = 0; long a = 0; namespace b {long b = 0; float a = 0.0f}} Namespace C {struct ABC {Long a, b, c, d; void abcd () {a = b = c = d = 12;}} ab;} Namespace D {void ABC (); void ABC () {long a = 0; A ;} extern float bd;} is available in the namespace Any statement or definition statement can also be used to modify a custom structure, so it can be C :: ABC A; A.Abcd (); It should be noted that C also allows the namespace alias, such as: Namespace AB = C; AB :: ABC A; A.Abcd () ;. Here, the name space C will have a name AB, just like the TypeDef previously mentioned. It should also be noted that the definition of custom types is very similar, such as Struct a {long a;}; will generate a :: A, and the names of the mapping element, the name of the mapping element, but should understand The structure A is not a name space, namely Namespace ABC = a; will fail. The namespace seem to be all members of all members of a static member's custom structure.

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

New Post(0)