C ++ beginner solution (4) - constructor (on)

zhaozj2021-02-11  252

Constructor (on)

In order to facilitate the meaning and usage of the constructor, it is assumed that we are working on a certain or certain Class design. Here we want to design this two Class, one is a description of the Class, which is Complex., Although there is also a plurality of classes in the standard library, but it is assumed that we have to design one by the special needs, it seems that it is like this. :

Class Complex

{

PUBLIC:

... // Others

Private:

Double _x; // real part

Double _y; // 虚部

}

In addition, we have to design a class, which is a senior array for storing INT data. We know that the array function in C has a single function, there is no problem, not safe enough, so we have to make a higher level design. It is probably equivalent to vector in the standard library. Due to the dynamic allocation of memory, we need a pointer variable to store the memory block address allocated, maybe we also need a total of the number of current array elements. Variables, so it looks like this:

Class IntaRray

{

PUBLIC:

... // Others

Private:

INT * _P;

INT _SIZE;

}

OK, we now start to see what kind of constructor can provide us with these two classes.

First, initialization, and various ways to initialize

Introducing a constructor is a natural purpose to initialize it when the object is constructed. There is no initialized object, and its value will be arbitrary. If it is not appropriate to use it, it may cause a certain risk. For example, if you do not provide initialization, the object is in the configuration, the value of _P and _size is generally uncertain. If it accidentally, it is unclear to read and write operations, it is almost certain that it will be able to access it. The area where the end is not accessed, the ending is a memory protection error, or the program crashes. For multiple classes, the situation is better: an unsteadic complex of an initial value will not cause the above horror errors; but we still have to have an automatic initialization mechanism. Of course, the constructor can achieve this for us.

For Intarray, you can set the _P as an empty pointer during initialization, set _size to 0 (hereinafter, the definition is omitted) ::

INTARRAY :: Intarray ()

{

_P = 0;

_size = 0;

}

For complex classes, it simply sets the initial real and virtual unit to 0:

Complex :: Complex ()

{

_X = 0.0;

_y = 0.0;

}

Now our two CLASS provide an initialization feature that is convenient and secure. But further, we sometimes want to initialize the object in a specific, specified manner, for example, we may want to construct a plural variable of 3 2i, if the constructor only provides simple construction, we must not Do not write:

Complex C;

C.SETX (3.0); // Set the real part

C.SETY (2.0); // Setumen

Oh, of course, this doesn't seem too much problem, at least it works normally. Just we note that in the first line of the code, define the plural object C, and this will call the constructor, it will set _x, _y to 0, then we have to manually turn _x, _y to change to 3 and 2, how many stupid, isn't it? Even if the loss of efficiency is not considered, it looks quite unsightly. Fortunately, we can use the parametric constructor to achieve this: Complex :: Complex (double x, double y)

{

_X = x;

_y = y;

}

Since we need to provide the default initialization mode and the specified initialization, we should use the overload to retain the original constructor. We can use the default initialization method as the previous declaration C, or use the specified initial value, like this:

Complex D (3.0, 2.0);

The constructor of the second version will be called, and a plurality of multiple objects d is 3 2i is constructed.

Above we achieved two different initialization methods with two overloaded constructor, it was found that they were actually very similar, namely, the _x, _y assigned, the difference is only the value assigned by the default. Still specify. For this feature, it is not difficult to think of this is the feature of the default parameter function, so that we can combine the above two constructors into a constructor with the default parameters:

Complex :: Complex (Double X = 0.0, Double Y = 0.0)

{

_X = x;

_y = y;

}

In addition to the better to have a better sense, we also "By", you can use a real number to initialize the plural:

Complex C (3.14);

Since the imaginary department is not clear, it will be defaulted to 0, which is in line with the actual intention; so that it exempts us to write a constructor that is specifically used to initialize the plurality of reality parameters.

Of course, it is not all cases, and the functionality like this is beneficial: it is certainly possible to bring confusion or unrealistic initialization. At this time, we must specifically limit the initialization pathway by overloading.

Similarly, for INTARRAY, by overloading we can implement various initialization methods, for example, by specifying _size to allocate memory, further, further, then specify an initial value, give each of the arrays Elements as an initial value; we can also initialize IntArray, and so on in an ordinary C integer array

Second, Class with "Special" constructor

No default constructed Class

For the previous versions of the Complex class, we all provide its "default construct" way, that is, we can write such as:

Complex C;

The statement does not have to add a pair of parentheses containing constructor after the object name. Because when we don't provide any constructor for Complex, or only a constructor, such as a COMPLEX () constructor, this is the only way we declare the Complex object, to our use of overload, and even unify them This way of writing is still meaningful when a constructor provides the default parameter. But if we just provide a constructor, and it needs to specify the parameters, and completely default parameters are not allowed, such as

Complex (Double X, Double Y);

This will mean that the function call like Complex () is meaningful, that is, we can't write again.

Complex C; // There is no function such as Complex () for call, compiling, can only write similar to such a statement.

Complex C (0.0, 0.0); // No problem, it calls Complex (Double X, Double Y)

Oh, it seems that the problem is not too big, we only write more things when you declare the object. But let us transfer attention to the array of C : If we need to declare a Complex array, what should I do? Obviously Complex will still ask us to provide an initial value for each object, but for arrays, we cannot write

Complex a [20] (0.0, 0.0); // Ha, error

Similarly, the dynamic allocation array does not work:

Complex * p = new complex [20] (0.0, 0.0); // Haha, still not

We can only recognize that "there is no direct method, you can use an array that do not provide the default constructed mode." Oh, you may have to make this statement seems to be in hints or some hidden methods to implement the Complex array. That reminds me of thinking, oh, maybe you can implement it with the malloc () memory allocation function in C, because it is just simply molbling, and does not call the constructor, so compiling will not have problems. However, it is a way to use malloc () to allocate memory for C objects, and thus is not recommended. If you really feel that you need to use an array, then provide a default constructor for your class.

In addition, if there is a class that needs to include the Complex object as a member, how should you specify the initialization parameters? E.g:

Class A

{

PUBLIC:

... // Others

Private:

... // Others

Complex C; // This is not allowed to write COMPLEX C (0.0, 0.0);

}

Here, C provides an initialization method, ie "member initialization list". After the parameter table defined by the constructor, it is separated by a colon with the parameter table. If there are multiple members to initialize in the parameter table, they are separated between comma. For Class A, in order to initialize member C, we can define the constructor such:

A :: A (... / * parameter table * /): c (0.0, 0.0) / / here initialization C

{

... // Other operations

}

Of course, the above (0.0, 0.0) can also be changed to a parameter containing other parameters, variables, and the like to achieve designation of the C value.

In addition to user-defined Class objects, the built-in basic type in C can also complete initialization in the member initialization list, such as the front COMPLEX constructor can also be written.

Complex :: Complex (Double X = 0.0, Double Y = 0.0): _X (x), _y (y)

{

}

So, it and

Complex :: Complex (Double X = 0.0, Double Y = 0.0)

{

_X = x;

_y = y;

}

what differences are there? There is no difference between the answer. Some people, such as me, prefer to get the initialization list of basic built-in types, and others tend to put the Class objects and put the basic internal transtribution types in the function body. You can choose from it.

If the constructor contains both member initialization lists, it also contains statements in the function (including initialization statements and non-initialization statements), then the compiler guarantees the initialization of the initialization list to perform the statement of the function body. However, for the initialization of members in the member initialization list, special attention is required: the order is not determined by the order of writing, but is determined by the order of the members in the Class declaration. If considering the previous IntArray, suppose we want to provide a constructor initialized by an array size, it may be like this: Class IntaRray

{

PUBLIC:

INTARRAY (int size);

... // Others

Private:

INT * _P;

INT _SIZE;

}

// Simple, suppose the size value provided by the user is more than 0 to ensure that new int [size] has always meaningful

INTARRAY :: INTARRAY (int size): _size (size), _P (new int [_size])

{

... // Others

}

Here, a quite hidden error: On the surface, we will initialize _size first in the initialization list, and then transmit _size as the size of the dynamic array to the New Expression. However, the initialization of the objects in the initialization is actually based on the order of the variable, and we first declare _p, and then declare _size in the declaration section of Intarray, and then initialize the _P when the program is running. At this time, the _size value used by the New expression may be unsure, which may be a very large number, or very small or even negative values, which seriously violates our intention.

Avoiding the above mistakes can of course have many ways, such as directly, you can exchange _P and _size's declaration order, but this is not a way to advocate, after all, we will subconsciously think that the order of the statement will not change The semantics of the program, so it is possible to declare the order in the future, and this will cause errors and difficult to capture. A better solution is to change _size to size, which should minimize the dependence between member variables at the initialization phase.

The above discusses a class complex that does not contain the default constructed form, and how to initialize it when another class will make COMPLEX as a member variable, let's take a look at the inheritance: If there is a Class inherited from Complex, according to about The inheritance regulations, the compiler will call the base class after the constructor call of the derived class is completed, which is the constructor of Complex. The question is how should we pass the parameter to the base class constructor? The answer is similar: through the list of members. Here is an example:

Class Supercomplex: Public Complex

{

PUBLIC:

Supercomplex (... / * parameter table * /);

... // Others

Private:

... // Others

}

Supercomplex :: supercomlex (... / * parameter table * /): complex (0.0, 0.0)

{

... // Others

}

Obviously, the situation is similar to the situation contained in the previous, and the difference is that the previous object name is replaced by the base class name here, of course, you can also use other variables, such as the parameter value of the constructor of Supercomplex. Instead of constant parameters in COMPLEX (0.0, 0.0).

2. Private constructor

Usually we all put the statement of the constructor in the public segment, if we will happen what kind of consequences will happen in the private segment? Yes, I also know that this will make the constructor private, what does this mean?

We know, when we declare an object in the program, the compiler is called the constructor (if any), and this call will usually be external, that is, it does not belong to the call of the Class object itself, if constructor It is private, and this will result in compilation errors because it is not allowed to access private members outside the CLASS. You said: "Haha." We made a class that does not seem to generate objects. Of course, for the class itself, we can also use its Static public member, because they don't have to generate objects independently of the Class object. They can also be used. Well, it seems that we have found a reason for the existence for classes with private constructors. However, we should not be satisfied, because it should also be excavated.

First let's take a serious look at whether it is really unable to create a class object with a private constructor. "Hey, maybe it may not be." You may say this now. This is very good, let's take a look at why, it's right, because the constructor is privatated by the class, so we have to create an object, you must be able to access the private domain of Class; but this "we" can't do it. So, can anyone get it? Class members can do it; but how can we use its members before we construct its object? Hey, I just mentioned the Static public member, which exists independently of the Class object, of course, it is also public, "we" can be accessed. If the Class object is created in a Static function, it returns it in the form of a reference or pointer (cannot be returned in the form of a value, thinking about why), we have access to this object. Here is an example:

Class Wonderfulclass

{

PUBLIC:

Static Wonderfulclass * makeanObject ()

{

// Create a WonderFulClass object and return its pointer

Return (New Wonderfulclass);

}

Private:

Wonderfulclass () {}

}

int main ()

{

Wonderfulclass * p = wonderfulclass :: makeanobject ();

... // Use * P

Delete P; // Not Neccesary Here, But it's a good habit.

Return 0;

}

Well, this example uses a private constructor, but it works very well: makeanobject () as a static member function of Wonderfulclass, to create an object for us to do it: Create an object on the pile, even if makeanobject () exits, the object will not evaporate, of course, you should not forget to remove it.

Back to the previous idea: In addition to public Static members can help us access private domains, there is still other "things" that can be used?

Hey, you must think of using friends, it is completely correct. You can use the friend function or friend class of this class to create its object, which is not exemplified here.

We know that no one will be bored to set a class to private, then write one of the same MakeanObject () to let it experience the fantastic feeling. We also don't believe this is just a "By" "special" "useless" edge function caused by C design reasons. It should have practical use.

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

New Post(0)