-- Knowledge about classes
We have already introduced the concepts of member variables and member functions of user-defined types and their meanings. This article continues to explain the remaining content of user-defined types and their meanings.
Permission
The provision of member functions improves the semantics of custom types from resources to functional resources. What is a functional resource? For example, to map a radio to a number, you need to map the operation to adjust the frequency of the radio to receive different radio stations; adjust the volume of the radio; turn on and off the radio to prevent power loss. For this reason, the radio should be mapped to a structure, similar to the following:
Struct radiogram
{
Double Frequency;/* Frequency */void turnfreq (double value); // change frequency
Float volume;/* volume */void turnvolume (float value); // change the volume
Float power;/* power */void turnonoff (bool bon); // Switch
Bool bpoweron; // whether to enable
};
The preceding radiogram: frequency, radiogram: volume and radiogram: power are defined as members of the structure radiogram, so their semantics isARadio frequency,ARadio volume andARadio power. The semantics of the other three member functions is also changed.ARadio frequency and changeARadio volume and turn on or offAPower Supply of the radio. Note that "A" indicates which radio is not known yet. Only when a specific radio on the left is combined with the member operator can we know which radio is used, this is also why they are called Offset types. This will be detailed in the next article.
Note: Why do we need to map the three operations to the member functions of the structure radiogram? Because the radio has such a function? So do we need to define a structure for selecting watermelon, Cutting watermelon, and eating watermelon, and then define three member functions for it ?? Isn't it ridiculous? The three operations of the former are for the member variables of the structure, while the latter is for the structure itself. Then it is changed to eating fast food, hamburger for fast food, fries for fast food, and cola for fast food. If the two eating and drinking operations turn into a member function of fast food, is it a function of fast food ?! This is actually a matter of programming ideas, but here is actually the so-called object-oriented programming thinking. Although it is a good idea, it is not necessarily appropriate. I will discuss it in detail in the next article.
The reason for the above is that the radio is a function, because in reality we cannot directly change the frequency of the radio, and we must change the receiving frequency by rotating the knob of the caster. Similarly, the volume adjustment is also achieved by adjusting the volume knob, and the power loss caused by the boot is not directly caused by us, but indirectly caused by listening to the radio station. Therefore, member variables such as radiogram: power, radiogram: frequency all have a special feature-the outside world, and things other than this radio cannot change them. Therefore, C ++ provides a syntax to implement this semantics. The format is as follows: <permission> :. The <permission> here is one of public, protected, and private, which are called public, protected, and private, respectively, as follows:
Class radiogram
{
Protected: Double m_frequency; float m_volume; float m_power;
PRIVATE: bool m_bpoweron;
Public: void turnfreq (double); void turnvolume (float); void turnonoff (bool );
};
It can be found that it is the same as the Definition Format of the previous label, but it is not a statement modifier, that is, it can be struct ABC {private :};. Here, you do not need to use private: Followed by a statement because it is not a statement modifier. The member variables or member functions generated by all declarations and definitions between them until the next such syntax carry the semantics it represents. For example, in the radiogram class above, the radiogram: m_frequency, radiogram: m_volume, and radiogram: m_power are protected member variables, and the radiogram: m_bpoweron is a private member variable, the remaining three member functions are public member functions. Note that the preceding syntax can be repeated, for example, struct ABC {public: long a; private: Float B; public: Char D ;};.
What does it mean? It is very simple. Public Members can access the outside, and protected members cannot access the outside, while private members and sub-classes cannot. Next to the subclass. First look at the public. For the above, the following error will be reported:
Radiogram A; A. m_frequency = 23.0; A. m_power = 1.0f; A. m_bpoweron = true;
The preceding three operations on a use protection or private members of a. the compiler reports an error because the two Members cannot be accessed by the outside world. A. turnfreq (10); there is no problem because the member function radiogram: turnfreq is a public member and can be accessed from outside. So what is the outside world? For a custom type, all the code that can be written outside the body of the member function of this custom type is called the outside world. Therefore, for the above radiogram, only the functions of the three member functions can access its member variables. That is, the following code will be correct.
Void radiogram: turnfreq (double value) {m_frequency + = value ;}
Because m_frequency is used in the radiogram: turnfreq function, it does not belong to the outside world.
Why? It represents the semantics at the beginning. First, defining a member as public or private has no effect on the final generated code. Then, as I said before, the adjustment of the receiving frequency is achieved by adjusting the capacity of the common capacitance in the radio. The capacity of this capacitor must be achieved by using components, after the receiving frequency is mapped to a number, the CPU can be modified because it is a number. If. m_frequency + = 10; modify the code to increase the reception frequency of the radio by 10 kHz, which is against our objective world, it is inconsistent with the previous semantics. Therefore, it is provided as a syntax and reviewed by the compiler, which allows us to compile code that is more in line with the semantics of the world we live in.
Note that you can Union ABC {long a; private: Short B ;};. Here ABC: A is not modified before. Is it public or protected? I believe that it should be public from the above examples, which is why I have been using struct and Union to define custom types. Otherwise, errors will be reported in previous examples. The previous article said that there is only a small difference between the structure and the class, that is, when the member is not modified, the member will be private rather than public for the class, that is, the following will be an error.
Class ABC {long a; private: Short B ;}; ABC A; A. A = 13;
ABC: A is regarded as private because of the previous class. From this point, we can see that the structure is used to map resources (resources that can be directly used), and classes are used to map functional resources. The next article will detail their differences in semantics.
Constructor and constructor
After learning about the above things, we obviously have the following questions:
Struct ABC {PRIVATE: long a, B;}; ABC A = {10, 20 };
Is the initialization value variable a correct? Of course, this is an error. Otherwise, this is a syntax Vulnerability (you can modify the unmodifiable members ). However, in some cases, Initialization is required to ensure some logical relationships. For this reason, C ++ proposes the concepts of constructor and constructor, which correspond to initialization and scanning. Before learning about this, let's take a look at what is an instance ).
An instance is an abstract concept that indicates an objective existence. It is closely related to the concept of "world" introduced in the next article. For example, "This is a table" and "this table", the former "table" is a type, and the latter "table" is an instance. If there are 10 sheep, it is said that there are 10 goat instances, and the sheep are only one type. We can simply think of an object in the objective world as an object. Humans classify various objects for convenience. Therefore, no example of a TV set is provided for TV sets, a TV is an example of a TV. Similarly, code writing of a program is of little significance. Only when it is executed, we call an instance of that program running. If the operating system is required to execute the program when it is not completed, the two instances of the program are being executed for the multitasking operating system, if you open two Word files at a time point, two word program instances are running.
In C ++, only a number can be operated, and a number is an instance (as can be seen in the following description). More generally, the memory address that identifies the record number is an instance, that is, the variable is an instance, and the corresponding type is the object type mentioned above. For example: Long A, * pA = & A, & RA = A;. Here two instances are generated, one is long, one instance is long * (note that no instance is generated Because Ra is long, but Ra is still an instance ). Similarly, for a custom type, such as radiogram AB, C [3];, it is said that four radiogram instances are generated.
When an instance of the custom type is generated, the corresponding constructor is called. When it is destroyed, the corresponding destructor is called. Who will call it? The compiler is responsible for helping us compile the necessary code to implement corresponding constructor and destructor calls. The format of the constructor prototype (that is, the type corresponding to the function name, such as float AB (double, char); the prototype is float (double, char) is: directly use the type name of the custom type as the function name. If there is no return value type, the parameter is left blank. For destructor, the name is preceded by the symbol "~", No return value type. No parameter is required. As follows:
Struct ABC {ABC (); ABC (Long, long );~ ABC (); bool do (long); long a, count; float * PF ;};
ABC: ABC () {A = 1; Count = 0; pF = 0 ;}
ABC: ABC (long tem1, long tem2) {A = tem1; Count = tem2; pF = new float [count];}
ABC ::~ ABC () {Delete [] PF ;}
Bool ABC: Do (long COU)
{
Float * P = new float [cou];
If (! P)
Return false;
Delete [] PF;
PF = P;
Count = cou;
Return true;
}
Extern ABC g_abc;
Void main () {abc a, & R = A;. do (10); {abc B (10, 30);} ABC * P = new ABC [10]; Delete [] P ;}
ABC G_A (10, 34), g_p = new ABC [5];
The structure ABC above defines two Constructor (note that there are two overloaded functions) and the names are both ABC: ABC (actually convert the compiler into different symbols for connection ). A destructor is also defined (note that only one destructor can be defined, because it cannot be reloaded without parameters), and its name is ABC ::~ ABC.
Let's look at the main function. We first define a variable through abc a; because we need to allocate a piece of memory on the stack, that is, we have created a number (the memory for creating numbers also leads to the creation of numbers, because the memory cannot contain numbers.) then, an ABC instance is created and ABC constructor is called. Because no parameter is provided here (described later), ABC: ABC () is called, and A. A is 1, A. Pf and A. Count are both 0. The variable R is defined, but because it is ABC &, the memory is not allocated on the stack, and the instance is not created and ABC: ABC is not called. Call a. Do, allocate a piece of memory, and put the first address in A. Pf.
Note that the above definition of variable B uses the previously mentioned function-based initialization method. It calls ABC's constructor ABC: ABC (Long, long) in the format of function call to initialize ABC's instance B. Therefore, B. A is 10, B. Count is 30, and B. PF is the first address of the memory block. However, you should note that this initialization method is different from the previously mentioned "{}" method. The former is initialized by calling the callback function, the latter is initiated by the compiler (by generating the necessary code ). Since no function is called, the speed is faster (the overhead of the function is described in C ++ from (15th ). Note that abc B cannot be abc B = {1, 0, 0}; because the structure ABC already defines two constructors, it can only be initialized using the function-based initialization method, you can no longer use the "{}" method for initialization.
The above B is in a pair of braces, recalling the scope of the variable mentioned above, so when the program runs to ABC * P = new ABC [10, variable B has disappeared (beyond its scope), that is, the allocated memory syntax has been released (actually, It is not released because it is on the stack ), then, call the destructor of ABC to release the memory allocated by B in ABC: ABC (Long, long) for scanning.
For memory allocated on the heap through new, because it is new ABC [10], 10 ABC instances will be created, and ABC: ABC () will be called once for each instance (), note that ABC: ABC (Long, long) cannot be called here, because the new operator allocates the memory space required by 10 instances at a time, c ++ does not provide syntax (for example, "{}") to initialize the 10 instances allocated at a time. Then, delete [] P; is called, which releases the allocated memory, that is, 10 instances are destroyed. Therefore, the destructor of ABC is called 10 times for 10 scans.
Note that the global variable g_abc is declared. Because the global variable g_abc is not defined and no memory is allocated, no instance is generated. Therefore, the ABC constructor is not called, whereas G_A is a global variable, c ++ ensures that the global variable constructor is called before the main function is executed, the destructor of all global variables are called after the main function is executed (this is implemented by the compiler, in C ++ from scratch (19th) ). Therefore, g_a.abc (10, 34) is called before a. ABC (), even if its position is behind the Definition Statement of. The initialization Number of the global variable g_p is calculated by the new operator. The result is allocated memory on the stack, and five ABC instances are generated and ABC: ABC () is called () five times, because it is allocated during g_p initialization, these five calls are also in. before ABC. G_p only records the first address, but to release these five instances, you must call Delete (not necessarily, or do not call Delete to release the memory returned by new, in C ++ from (19th), but it is not called above, so the destructor of the five instances will not be called until the end of the program, what will happen? We will discuss the so-called memory leakage in case of exceptions later.
Therefore, the structure means that a piece of memory has just been allocated and has not been initialized. This piece of memory is called raw data. As mentioned above, all numbers must be mapped to resources in the algorithm, the validity of the number exists. For example, the ing person's age cannot be a negative number because it is meaningless. Therefore, after obtaining the original data, you should first call the constructor to ensure that the corresponding instance has the correct meaning. The Destructor means to perform the scanning and tail operation, as shown above, some memory is dynamically allocated during the operation of an instance (that is, the period during which the code for operating the instance is executed, it should be ensured that it is correctly released. Or this instance has a relationship with other instances, to ensure that the relationship is removed (because the instance is about to be destroyed), such as using class ing for a node in the linked list, when this node is deleted, its relationship with other nodes should be removed from its destructor.
Derivation and inheritance
We have defined a radiogram-like ing radio. If you need to map a digital radio, it is the same as a radio, however, the automatic searching, storage, selection, and deletion functions are added. A type system is proposed here, that is, if an instance is a digital radio, it must also be an instance of the radio. For example, if both Apple and pear are fruits, then the instances of Apple and pear must also be fruits. Here we propose three types: fruit, apple, and pear. Here, fruit is the parent class (parent type) of apple, and Apple is the Child class (Child type) of fruit ). Similarly, fruit is also the parent class of pear, which is a subclass of fruit. This kind of system is very meaningful, because humans recognize the world in this way and it is very consistent with human thinking habits, therefore, C ++ proposes a special syntax to support this semantics.
When defining a custom type, add ":" next to the type name, public, protected, or private, and then write the type name of the parent class, finally, it is the type definition character "{}" and related writing, as follows:
Class digitalradiogram: Public radiogram
{
Protected: Double m_stations [10];
Public: void searchstation (); void savestation (unsigned long );
Void selectstation (unsigned long); void erasestation (unsigned long );
};
In the above example, radiogram is defined as the parent class of digitalradiogram. digitalradiogram is defined as a child class of radiogram. It is called radiogram to derive the class digitalradiogram. The class digitalradiogram inherits the class radiogram.
Five ing elements are generated above, which are the above four member functions and one member variable, but actually not only. Because it is derived from radiogram, seven mappings will be generated, that is, the seven members of the radiogram class, but the name will change to digitalradiogram: modifier instead of the original radiogram :: but the type does not change. For example, the name of a ing element is digitalradiogram: m_bpoweron, And the type is bool radiogram:. The ing offset value remains unchanged and is still 16. There is also the diging element digitalradiogram: turnfreq, whose type is void (radiogram:) (double). The ing address remains unchanged and is the address corresponding to radiogram: turnfreq. Therefore, you can:
Void digitalradiogram: savestation (unsigned long index)
{
If (index> = 10) return;
M_station [Index] = m_frequency; m_bpoweron = true;
}
Digitalradiogram A; A. turnfreq (10); A. savestation (3 );
Although digitalradiogram: turnfreq is not declared above, it can still be called because it is derived from radiogram. Note that because a. turnfreq (10); does not write the full name, it is actually a. digitalradiogram: turnfreq (10); because the numeric type on the left of the member operator is digitalradiogram. If digitalradiogram is not derived from radiogram, the seven mappings mentioned above are not generated. The result is a. turnfreq (10). An error is returned.
Note that m_frequency is directly written in the above savestation, which is equivalent to this-> m_frequency. Because this is digitalradiogram * (because it is in the function body of digitalradiogram: savestation ), therefore, it is actually this-> digitalradiogram: m_frequency. Therefore, if it is not derived from radiogram, the above error will be reported. And it is easy to know that void (radiogram: * p) (double) = digitalradiogram: turnfreq ;. Although it is digitalradiogram: turnfreq, its type is void (radiogram:) (double ).
Note that m_bpoweron is used in savestation, which is defined as a private member in radiogram. That is to say, the subclass has no access permission, and the savestation is a member function of its subclass. Therefore, the above error is reported, insufficient permissions.
What are the permissions of the seven ing elements generated by derivation above? First look at the derived code above:
Class digitalradiogram: Public radiogram {...};
Because public is used, it is called digitalradiogram to inherit from the radiogram public. If it is changed to protected, it is called protection inheritance. If it is private, it is private inheritance. What is the difference? The ing elements generated through public inheritance (seven ing elements derived from radiogram) do not change their respective permission attributes, that is, the preceding digitalradiogram :: m_frequency is still protected for the digitalradiogram class, while digitalradiogram: m_bpoweron is still private. Protection inheritance means that all public members become protected members, and others remain unchanged. That is, if the inheritance is protected, digitalradiogram: turnfreq is protected for digitalradiogram. Private inheritance converts all parent class members to private for sub-classes. Therefore, if the above is private inheritance, digitalradiogram: turnfreq is private for digitalradiogram.
As you can see above, it is very simple, that is, no matter what inheritance, it specifies a permission. In the parent class, any ing element higher than this permission, all permissions must be granted to the sub-class and then inherited to the sub-class. What does the above always mean by "for subclass? As follows:
Struct a {long a; protected: Long B; private: Long C ;};
Struct B: Protected A {void AB ();};
Struct C: Private B {void ABC ();};
Void B: AB () {B = 10; C = 10 ;}
Void C: ABC () {A = 10; B = 10; C = 10; AB ();}
A A; B; C; A. A = 10; B. A = 10; B. AB (); C. AB ();
The above definition of B is equivalent to struct B {protected: long a, B; private: Long C; public: void AB ();};.
The above C definition is equivalent to struct c {private: Long A, B, C; void AB (); Public: void ABC ();};
Therefore, B = 10 in B: AB; no problem, but C = 10; there is a problem, because the compiler shows that B: C is generated from the parent class, it is a private member for the parent class, so the subclass has no access permission and is incorrect. Next let's take a look at C: ABC, A = 10; and B = 10; it's okay, because they are all protection members for B, but C = 10; will be wrong, because C :: c. It is a private member of the parent class B and has no permissions and fails. Then AB ();, because C: AB is a public member for the parent class B, no problem.
Next is. A = 10; no problem; B. A = 10;, error, because B: A is a protection member of B; B. AB ();, no problem; C. AB ();, error, because C: AB is a private member of C. Note that public, protected, and private are not type modifiers, but provide some information in syntax, and the types of inherited members will not change, whether it protects inheritance or public inheritance, the role of permissions depends on the use of Members, and it has nothing to do with the type. What is the use of Members? As follows:
Long (A: * P) = & A: A; P = & A: B;
Void (B: * pb) () = B: AB; void (C: * PC) () = C: ABC; Pc = C: AB;
There is no problem with initialization of Variable P. Here we use a:. However, when P = & A: B; is used, because a: B is used, the compiler must check where the code is located and find that a belongs to the outside world. Therefore, an error is reported, insufficient permissions. There is no problem with the assignment of Pb values, but PC = C: AB; is incorrect. For B. A = 10;, the member operator uses B: A, a member of Class B, so the permission check is conducted here, and an error is reported when the permission is insufficient.
Well, why is it so complicated? What protection, private and public inheritance do you get? First of all, let's look at why we should provide inheritance, because we want to embody the type system in code, which means that if an instance is a subclass instance, it must also be a parent class instance, that is, you can operate on it according to the definition of the parent class. Although this can also be achieved through the previously mentioned conversion pointer type, the former can directly display the type inheritance semantics (that is, subclass derived from the parent class) from the code ), however, the latter can only describe the same instance with different types.
So why do we need to add permissions to inheritance? Indicates that this class does not want the outside world or its subclass to view it as its parent class. For example, a chicken can be eaten, but a chicken made of specimens cannot be eaten. Therefore, when inheriting the parent class "Chicken specimen", we should protect the inherited parent class "chicken" to indicate that the outside world is not allowed (but its derived class is allowed) to regard it as a chicken. It is no longer a chicken, but it actually changes from a chicken. Therefore, private and protection inheritance are very suitable for expressing the evolutionary relationship of animals. For example, humans evolved from monkeys, but not monkeys. Here, people should use private inheritance, because they do not want the external and human sub-classes-black, yellow, white, etc.-to regard the parent class "person" as a monkey. Public inheritance means that the external and child classes can regard the Child class instance as the parent class instance. As follows:
Struct a {long a, B ;};
Struct AB: Private A {long C; void ABCD ();};
Struct ABB: Public AB {void AAA ();};
Struct AC: Public A {long C; void ABCD ();};
Void ABC (A * A) {A-> A = 10; A-> B = 20 ;}
Void main () {AB B; ABC (& B); ac c; ABC (& C );}
Void AB: ABCD () {AB B; ABC (& B );}
Void AC: ABCD () {AB B; ABC (& B );}
Void ABB: AAA () {AB B; ABC (& B );}
The above class AC is a public inheritance, so its instance C will be implicitly converted by the compiler when executing ABC (& C);, which is a very strange feature, the next part of this article will describe. But class AB is private inheritance, so in ABC (& B);, the compiler will not perform implicit type conversion, and an error will be reported, the type does not match. For this case, only ABC (A *) & B); is required to display and convert the type.
Note the red letter above. Private inheritance indicates that neither the outside world nor its sub-classes can be viewed by the parent class. Therefore, in ABB: AAA, This is the sub-class of AB, so here ABC (& B); will report an error. In ac: ABCD, an error is reported for AB. In AB: ABCD, it is itself, that is, it is neither a subclass nor an external world, so ABC (& B); will be fine. If AB is replaced with protection inheritance, ABC (& B) in ABB: AAA will not be wrong.
For the semantics discussed in this article and the next article) it will specifically propose a concept to give a solution to guide how to design classes and various relationships. Due to space limitations, this article is divided into the next two articles, the rest of the content is described in the next article.