More Effective C ++ Terms 5

zhaozj2021-02-08  307

Terms 5: Care Define Type Conversion Functions

The C compiler can implicit conversion between the two data types, which inherits the C language conversion method, such as allowing the CHAR implicit to INT and implicitly converted from Short to Double. So when you pass a SHORT value to a function that is ready to accept the Double parameter value, you can still run successfully. Many of this terrible conversion may result in the loss of data, which still exists in C , including INT to SHORT conversion and Double to Char conversion.

You have no power to these types of conversions because they are the characteristics of the language itself. But when you add your own type, you can have more control because you can choose whether to provide a function to make the compiler for implicit type conversion.

There are two functions that allow compilers to perform these conversions: single-arguent constructors and implicit type conversion operators. Single parameter constructor refers to a constructor that can be called only by one parameter. This function can be only defined for a parameter, or although all parameters after a plurality of parameters but the first parameter will have a default. There are two examples below:

Class name {// for Names of things

PUBLIC:

Name (const string & s); // Convert String to

// Name

...

}

Class Rational {// A number of categories

PUBLIC:

Rational (int name = 0, // conversion INT to

INT Denominator = 1); // Have a number of categories

...

}

Implicit type conversion operator is just a strange member function: Operator keyword, followed by a type symbol. You don't have to define the return type of the function, because the return type is the name of this function. For example, in order to allow Rational (rational number) classes to be hidden to Double types (when mixing types with rational numbers, you can use it), you can declare the Rational Class:

Class russ {

PUBLIC:

...

Operator Double () const; // Convert Rational Class

}; // double type

In this case, this function will be automatically called:

Rational R (1, 2); // r value is 1/2

Double D = 0.5 * r; // Convert R to Double,

/ / Then make a multiplication

These instructions are just a review, I really want to say why you don't need to define all medium type conversion functions.

The fundamental problem is that when you do not need to use a conversion function, these functions can be called running. As a result, these incorrect procedures will make some annoying things, and you have difficulty judging the reason.

Let's first analyze the implicit type conversion operators, they are the easiest to handle. Suppose you have an Rational class as described above, you want the class to print the functionality of the rational object, as if it is a built-in type. Therefore, you may write this:

Rational R (1, 2);

Cout << r; // should print "1/2" and then assume you forgot to define Operator <<. You might want to print operations will fail because there is no suitable Operator << called. But you are wrong. When the compiler calls Operator <<, it will find that there is no such function exists, but it will try to find a suitable implicit type conversion order to make the function call normally. The rule definition of the type conversion order is complex, but in this case the compiler will find that they can call the Rational :: Operator Double function to convert R to Double type. Therefore, the result of the above code print is a floating point number, not an a number. This is simply a disaster, but it indicates the disadvantage of implicit type conversion: their existence will result in errors.

Solution is to replace the conversion operator with an equivalent function without using the syntax key. For example, in order to convert the Rational object to Double, use the askOUBLE function instead of the Operator Double function:

Class russ {

PUBLIC:

...

Double asdouble () const; // transition Rational

}; // Cheng Double

This member function can be explicitly called:

Rational R (1, 2);

Cout << r; // error! RATIONA object is not

// Operator <<

Cout << R.ASDOUBLE (); // correctly, print R with double type //

In most cases, this explicit conversion function is not convenient, but the case where the function is quietly called no longer happens, this loss is worth it. In general, the more experienced C programmers, the more you like to avoid the type of conversion operator. For example, people working in C standard libraries (see Terms 49 and 35) are the most experienced in this field. They add String types in the library function. The type of CHAR * that is implicitly converted from String into a C style. But define a member function c_str to complete this conversion, is this a coincidence? I don't look.

It is more difficult to eliminate implicit type conversion by single parameter constructing functions. And in many cases, the issues caused by these functions are considered to be implicit type conversion operators.

For example, an Array class template, these arrays require caller to determine the upper limit and lower limit of the boundary:

Template

Class array {

PUBLIC:

Array (int lowbound, int highbound);

Array (int size);

T & Operator [] (int index);

...

}

The first constructor allows the caller to determine the range of array indexes, such as from 10 to 20. It is a two-parameter constructor, so it cannot be used as a type conversion function. The second constructor allows the caller only defines the number of array elements (similar to the use of built-in arrays), but different is that it can be used as a type conversion function, which can lead to infinity pain.

For example, comparing Array objects, part of the code is as follows:

Bool Operator == (Const Array & lhs,

Const array & rhs; array a (10);

Array b (10);

...

For (int i = 0; i <10; i)

IF (a == b [i]) {//! "a" should be "a [i]"

Do Something for When

a [i] and b [i] are equal

}

Else {

Do Something for when.'re not;

}

We want to compare each element of A with each element of B, but when entering A, we accidentally forgot the array subscript. Of course we want the compiler to report a wide variety of warning information, but it doesn't. Because it uses the array parameter (for a) and int (for a) parameter call the operator == function, no Operator == function is these parameter types, our compiler pays attention To it can convert the INT type to the Array type by calling the Array , this constructor has only one int type parameter. Then the compiler is so compiled, the generated code is like this:

For (int i = 0; i <10; i)

IF (a == static_cast > (b [i])) ...

Each cycle is compared to a temporary array of a size B [i] (content is undefined). This is not only possible to operate in the right way, but also low efficiency. Because every cycle we must establish and release the Array object (see Terms 19).

By do not declare the operator (Operator), you can overcome the disadvantages of the implicit type conversion operator, but the single parameter constructor is not as simple. After all, you really want to provide the caller with a single parameter constructor. At the same time, you also want to prevent the compiler from adjusting this constructor without discriminating. Fortunately, there is a way to make your fish and bear's paw. In fact, two ways: First, it is easy, and the other is the method necessary to use when your compiler does not support easy methods.

Easy ways is to utilize the features of the latest compiler, the Explicit keyword. This feature specially introduced in order to solve the implicit type conversion, which is well understood. The constructor declares with an Explicit, if so, the compiler will reject the constructor to invoke the constructor for implicit type conversion. Explicit type conversion is still legal:

Template

Class array {

PUBLIC:

...

Explicit array (int size); // Note "Explicit"

...

}

Array a (10); / / correct, Explicit constructor

/ / Can use it normally when establishing an object

Array b (10); // is also correct

IF (a == b [i]) ... // error! No way

// implicit conversion

// int to array

IF (a == array (b [i])) ... // correct, explicitly from int to

// array conversion

// (but the logic of the code

// unreasonable) IF (a == static_cast > (b [i])) ...

/ / The same is true, the same

// unreasonable

IF (a == (array ) B [i]) ... // C style conversion is also correct,

/ / But logic

// is still unreasonable

In the example, STATIC_CAST (see Terms 2), the space between the two ">" characters cannot be missed, if such a write statement:

IF (a == static_cast > (b [i])) ...

This is a different meaningful statement. Because the C compiler explains ">>" as a symbol. There is no space between two ">", and the statement will generate syntax errors.

If your compiler does not support EXPLICI, you have to return a single parameter constructor that does not use the implicit type conversion function. (...)

I have said that the complex rules have decided that which imply-type conversion is legal, which one is illegal. There is no conversion in these rules to include a user-defined type (call single parameter constructor or implicit type conversion operator). You can use this rule to construct your class correctly, so that the object can be constructed normally, and go off the implicit type conversion you don't want.

Let's think of the array template, you need to use a shaped variable as a constructor parameter to determine an array size, but it must also prevent implicit type conversion from an integer type to a temporary array object. You have to achieve this, first to build a new class Arraysize. This object has only one purpose is to indicate the size of the array. You must modify the single-parameter constructor of Array, replace int instead of an Arraysize object. code show as below:

Template

Class array {

PUBLIC:

Class Arraysize {// This class is new

PUBLIC:

Arraysize (int Numelements): THSIZE (NumeLements) {}

INT size () const {return.}

Private:

INT THESIZE;

}

Array (int lowbound, int highbound);

Array (arraysize size); // pay attention to a new statement

...

}

Here, Arraysize is in Array, in order to emphasize that it is always used with Array. You must also declare Arraysize as public, in order to make anyone can use it.

Think about what kind of thing will happen when defining the Array object through a single parameter constructor.

Array a (10);

Your compiler requires the constructor in Array with the int parameter, but there is no such constructor. The compiler realizes that it can be converted from the int parameter into a temporary Arraysize object, and the Arraysize object is only required by the Array constructor, so the compiler is converted. The function call (and the object established) will be successful.

In fact, you still can construct an Array object at peace of mind, but do this can avoid type conversion. Consider the following code:

Bool Operator == (Const Array & lhs,

Const array & rhs;

Array a (10);

Array b (10); ...

For (int i = 0; i <10; i)

IF (a == b [i]) ... // 呦! "a" should be "a [i]";

// is now an error.

In order to call the Operator == function, the compiler requires the Array object in the "==" right side, but there is no single parameter constructor that is int. Moreover, the compiler cannot convert int to a temporary Arraysize object and then build a must-bete object through this temporary object, because this will call two user-defined types, one from int to Arraysize, one From Arraysize to Array . This conversion order is prohibited, so the compiler will definitely generate an error when trying to compare.

The use of the ARRAYSIZE class is like a purposeful helper, which is a more general technology application example. Classs similar to Arraysize are often referred to as Proxy Classes because each of these objects is to support other objects. The Arraysize object is actually an alternative to an integer type, which is used to determine an array size when establishing an Array object. Proxy objects can help you better control software in certain aspects, otherwise you can't control these behaviors, such as in the above case, this behavior refers to implicit type conversion, so it deserves to learn and use. You may ask how to learn it? One way is to turn to clause 33; it is specifically discussed Proxy classes.

Before you jump to the terms 33, take a closer look at the contents of this Territor. Let the compiler for implicit type conversion caused by greater than the benefits it brings, so unless you do need it, don't define type conversion functions.

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

New Post(0)