Team LiB
Previous Section Next Section

5.12. Type Conversions

5.12. 类型转换

The type of the operand(s) determine whether an expression is legal and, if the expression is legal, determines the meaning of the expression. However, in C++ some types are related to one another. When two types are related, we can use an object or value of one type where an operand of the related type is expected. Two types are related if there is a conversion between them.

表达式是否合法取决于操作数的类型,而且合法的表达式其含义也由其操作数类型决定。但是,在 C++ 中,某些类型之间存在相关的依赖关系。若两种类型相关,则可在需要某种类型的操作数位置上,使用该类型的相关类型对象或值。如果两个类型之间可以相互转换,则称这两个类型相关。

As an example, consider

考虑下列例子:

     int ival = 0;
     ival = 3.541 + 3; // typically compiles with a warning

which assigns 6 to ival.

ival 的值为 6。

The operands to the addition operator are values of two different types: 3.541 is a literal of type double, and 3 is a literal of type int. Rather than attempt to add values of the two different types, C++ defines a set of conversions to transform the operands to a common type before performing the arithmetic. These conversions are carried out automatically by the compiler without programmer interventionand sometimes without programmer knowledge. For that reason, they are referred to as implicit type conversions.

首先做加法操作,其操作数是两个不同类型的值:3.541double 型的字面值常量,而 3 则是 int 型的字面值常量。C++ 并不是把两个不同类型的值直接加在一起,而是提供了一组转换规则,以便在执行算术操作之前,将两个操作数转换为同一种数据类型。这些转换规则由编译器自动执行,无需程序员介入——有时甚至不需要程序员了解。因此,它们也被称为隐式类型转换

The built-in conversions among the arithmetic types are defined to preserve precision, if possible. Most often, if an expression has both integral and floating-point values, the integer is converted to floating-point. In this addition, the integer value 3 is converted to double. Floating-point addition is performed and the result, 6.541, is of type double.

C++ 定义了算术类型之间的内置转换以尽可能防止精度损失。通常,如果表达式的操作数分别为整型和浮点型,则整型的操作数被转换为浮点型。本例中,整数3被转换为 double 类型,然后执行浮点类型的加法操作,得 double 类型的结果 6.541

The next step is to assign that double value to ival, which is an int. In the case of assignment, the type of the left-hand operand dominates, because it is not possible to change the type of the object on the left-hand side. When the left- and right-hand types of an assignment differ, the right-hand side is converted to the type of the left-hand side. Here the double is converted to int. Converting a double to an int TRuncates the value; the decimal portion is discarded. 6.541 becomes 6, which is the value assigned to ival. Because the conversion of a double to int may result in a loss of precision, most compilers issue a warning. For example, the compiler we used to check the examples in this book warns us:

下一步是将 double 类型的值赋给 int 型变量 ival。在赋值操作中,因为不可能更改左操作数对象的类型,因此左操作数的类型占主导地位。如果赋值操作的左右操作数类型不相同,则右操作数会被转换为左边的类型。本例中,double 型的加法结果转换为 int 型。doubleint 的转换自动按截尾形式进行,小数部分被舍弃。于是 6.541 变成 6,然后赋给 ival。因为从 doubleint 的转换会导致精度损失,因此大多数编译器会给出警告。例如,本书所用的测试例程的编译器给出如下警告:

     warning: assignment to 'int' from 'double'

To understand implicit conversions, we need to know when they occur and what conversions are possible.

为了理解隐式类型转换,我们需要知道它们在什么时候发生,以及可能出现什么类型的转换。

5.12.1. When Implicit Type Conversions Occur

5.12.1. 何时发生隐式类型转换

The compiler applies conversions for both built-in and class type objects as necessary. Implicit type conversions take place in the following situations:

编译器在必要时将类型转换规则应用到内置类型和类类型的对象上。在下列情况下,将发生隐式类型转换:

  • In expressions with operands of mixed types, the types are converted to a common type:

    在混合类型的表达式中,其操作数被转换为相同的类型:

         int ival;
         double dval;
         ival >= dval // ival converted to double
    
  • An expression used as a condition is converted to bool:

    用作条件的表达式被转换为 bool 类型:

         int ival;
         if (ival)   // ival converted to bool
         while (cin) // cin converted to bool
    

    Conditions occur as the first operand of the conditional (?:) operator and as the operand(s) to the logical NOT (!), logical AND (&&), and logical OR (||) operators. Conditions also appear in the if, while, for, and do while statements. (We cover the do while in Chapter 6)

    条件操作符(?:)中的第一个操作数以及逻辑非(!)、逻辑与(&&)和逻辑或(||)的操作数都是条件表达式。出现在 ifwhilefordo while 语句中的同样也是条件表达式(其中 do while将在第六章中学习)。

  • An expression used to initialize or assign to a variable is converted to the type of the variable:

    用一表达式初始化某个变量,或将一表达式赋值给某个变量,则该表达式被转换为该变量的类型:

         int ival = 3.14; // 3.14 converted to int
         int *ip;
         ip = 0; // the int 0 converted to a null pointer of type int *
    

In addition, as we'll see in Chapter 7, implicit conversions also occur during function calls.

另外,在函数调用中也可能发生隐式类型转换,我们将在第七章学习这方面的内容。

5.12.2. The Arithmetic Conversions

5.12.2. 算术转换

The language defines a set of conversions among the built-in types. Among these, the most common are the arithmetic conversions, which ensure that the two operands of a binary operator, such as an arithmetic or logical operator, are converted to a common type before the operator is evaluated. That common type is also the result type of the expression.

C++ 语言为内置类型提供了一组转换规则,其中最常用的是算术转换。算术转换保证在执行操作之前,将二元操作符(如算术或逻辑操作符)的两个操作数转换为同一类型,并使表达式的值也具有相同的类型。

The rules define a hierarchy of type conversions in which operands are converted to the widest type in the expression. The conversion rules are defined so as to preserve the precision of the values involved in a multi-type expression. For example, if one operand is of type long double, then the other is converted to type long double regardless of what the second type is.

算术转换规则定义了一个类型转换层次,该层次规定了操作数应按什么次序转换为表达式中最宽的类型。在包含多种类型的表达式中,转换规则要确保计算值的精度。例如,如果一个操作数的类型是 long double,则无论另一个操作数是什么类型,都将被转换为 long double

The simplest kinds of conversion are integral promotions. Each of the integral types that are smaller than int char, signed char, unsigned char, short, and unsigned shortis promoted to int if all possible values of that type fit in an int. Otherwise, the value is promoted to unsigned int. When bool values are promoted to int, a false value promotes to zero and true to one.

最简单的转换为整型提升:对于所有比 int 小的整型,包括 charsigned charunsigned charshortunsigned short,如果该类型的所有可能的值都能包容在 int 内,它们就会被提升为 int 型,否则,它们将被提升为 unsigned int。如果将 bool 值提升为 int ,则 false 转换为 0,而 true 则转换为 1。

Conversions between Signed and Unsigned Types
有符号与无符号类型之间的转换

When an unsigned value is involved in an expression, the conversion rules are defined to preserve the value of the operands. Conversions involving unsigned operands depend on the relative sizes of the integral types on the machine. Hence, such conversions are inherently machine dependent.

若表达式中使用了无符号( unsigned )数值,所定义的转换规则需保护操作数的精度。unsigned 操作数的转换依赖于机器中整型的相对大小,因此,这类转换本质上依赖于机器。

In expressions involving shorts and ints, values of type short are converted to int. Expressions involving unsigned short are converted to int if the int type is large enough to represent all the values of an unsigned short. Otherwise, both operands are converted to unsigned int. For example, if shorts are a half word and ints a word, then any unsigned value will fit inside an int. On such a machine, unsigned shorts are converted to int.

包含 shortint 类型的表达式, short 类型的值转换为 int 。如果 int 型足够表示所有 unsigned short 型的值,则将 unsigned short 转换为 int,否则,将两个操作数均转换为 unsigned int 。例如,如果 short 用半字表示而 int 用一个字表示,则所有 unsigned 值都能包容在 int 内,在这种机器上, unsigned short 转换为 int

The same conversion happens among operands of type long and unsigned int. The unsigned int operand is converted to long if type long on the machine is large enough to represent all the values of the unsigned int. Otherwise, both operands are converted to unsigned long.

longunsigned int 的转换也是一样的。只要机器上的 long 型足够表示 unsigned int 型的所有值,就将 unsigned int 转换为 long 型,否则,将两个操作数均转换为 unsigned long

On a 32-bit machine, long and int are typically represented in a word. On such machines, expressions involving unsigned ints and longs are converted to unsigned long.

在 32 位的机器上,longint 型通常用一个字长表示,因此当表达式包含 unsigned intlong 两种类型,其操作数都应转换为 unsigned long 型。

Conversions for expressions involving signed and unsigned int can be surprising. In these expressions the signed value is converted to unsigned. For example, if we compare a plain int and an unsigned int, the int is first converted to unsigned. If the int happens to hold a negative value, the result will be converted as described in Section 2.1.1 (p. 36), with all the attendant problems discussed there.

对于包含 signedunsigned int 型的表达式,其转换可能出乎我们的意料。表达式中的 signed 型数值会被转换为 unsigned 型。例如,比较 int 型和 unsigned int 型的简单变量,系统首先将 int 型数值转换为 unsigned int 型,如果 int 型的值恰好为负数,其结果将以第 2.1.1 节介绍的方法转换,并带来该节描述的所有副作用。

Understanding the Arithmetic Conversions
理解算术转换

The best way to understand the arithmetic conversions is to study lots of examples. In most of the following examples, either the operands are converted to the largest type involved in the expression or, in the case of assignment expressions, the right-hand operand is converted to the type of the left-hand operand:

研究大量例题是帮助理解算术转换的最好方法。下面大部分例题中,要么是将操作数转换为表达式中的最大类型,要么是在赋值表达式中将右操作数转换为左操作数的类型。

     bool      flag;         char           cval;
     short     sval;         unsigned short usval;
     int       ival;         unsigned int   uival;
     long      lval;         unsigned long  ulval;
     float     fval;         double         dval;
     3.14159L + 'a'; // promote 'a' to int, then convert to long double
     dval + ival;    // ival converted to double
     dval + fval;    // fval converted to double
     ival = dval;    // dval converted (by truncation) to int
     flag = dval;    // if dval is 0, then flag is false, otherwise true
     cval + fval;    // cval promoted to int, that int converted to float
     sval + cval;    // sval and cval promoted to int
     cval + lval;    // cval converted to long
     ival + ulval;   // ival converted to unsigned long
     usval + ival;   // promotion depends on size of unsigned short and int
     uival + lval;   // conversion depends on size of unsigned int and long

In the first addition, the character constant lowercase 'a' has type char, which as we know from Section 2.1.1 (p. 34) is a numeric value. The numeric value that 'a' represents depends on the machine's character set. On our ASCII machine, 'a' represents the number 97. When we add 'a' to a long double, the char value is promoted to int and then that int value is converted to a long double. That converted value is added to the long double literal. The other interesting cases are the last two expressions involving unsigned values.

第一个加法操作的小写字母 'a' 是一个 char 类型的字符常量,正如我们在第 2.1.1 节介绍的,它是一个数值。字母 'a' 表示的数值取决于机器字符集。在 ASCII 机器中,字母 'a' 的值为 97。将 'a'long double 型数据相加时,char 型的值被提升为 int 型,然后将 int 型转换为 long double 型,转换后的值再与 long double 型字面值相加。另一个有趣的现象是最后两个表达式都包含 unsigned 数值。

5.12.3. Other Implicit Conversions

5.12.3. 其他隐式转换

Pointer Conversions
指针转换

In most cases when we use an array, the array is automatically converted to a pointer to the first element:

在使用数组时,大多数情况下数组都会自动转换为指向第一个元素的指针:

     int ia[10];    // array of 10 ints
     int* ip = ia;  // convert ia to pointer to first element

The exceptions when an array is not converted to a pointer are: as the operand of the address-of (&) operator or of sizeof, or when using the array to initialize a reference to the array. We'll see how to define a reference (or pointer) to an array in Section 7.2.4 (p. 240).

不将数组转换为指针的例外情况有:数组用作取地址(&)操作符的操作数或 sizeof 操作符的操作数时,或用数组对数组的引用进行初始化时,不会将数组转换为指针。我们将在第 7.2.4 节学习如何定义指向数组的引用(或指针)。

There are two other pointer conversions: A pointer to any data type can be converted to a void*, and a constant integral value of 0 can be converted to any pointer type.

C++ 还提供了另外两种指针转换:指向任意数据类型的指针都可转换为 void* 类型;整型数值常量 0 可转换为任意指针类型。

Conversions to bool
转换为 bool 类型

Arithmetic and pointer values can be converted to bool. If the pointer or arithmetic value is zero, then the bool is false; any other value converts to true:

算术值和指针值都可以转换为 bool 类型。如果指针或算术值为 0,则其 bool 值为 false ,而其他值则为 true

     if (cp) /* ... */     // true if cp is not zero
     while (*cp) /* ... */ // dereference cp and convert resulting char to bool

Here, the if converts any nonzero value of cp to TRue. The while dereferences cp, which yields a char. The null character has value zero and converts to false. All other char values convert to true.

这里,if 语句将 cp 的非零值转换为 truewhile 语句则对 cp 进行解引用,操作结果产生一个 char 型的值。空字符( null )具有 0 值,被转换为 false,而其他字符值则转换为 true

Arithmetic Type and bool Conversions
算术类型与 bool 类型的转换

Arithmetic objects can be converted to bool and bool objects can be converted to int. When an arithmetic type is converted to bool, zero converts as false and any other value converts as true. When a bool is converted to an arithmetic type, true becomes one and false becomes zero:

可将算术对象转换为 bool 类型,bool 对象也可转换为 int 型。将算术类型转换为 bool 型时,零转换为 false ,而其他值则转换为 true 。将 bool 对象转换为算术类型时,true 变成 1,而 false 则为 0:

     bool b = true;
     int ival = b;   // ival == 1
     double pi = 3.14;
     bool b2 = pi;   // b2 is true
     pi = false;     // pi == 0
Conversions and Enumeration Types
转换与枚举类型

Objects of an enumeration type (Section 2.7, p. 62) or an enumerator can be automatically converted to an integral type. As a result, they can be used where an integral value is requiredfor example, in an arithmetic expression:

C++ 自动将枚举类型(第 2.7 节)的对象或枚举成员( enumerator )转换为整型,其转换结果可用于任何要求使用整数值的地方。例如,用于算术表达式:

     // point2d is 2, point2w is 3, point3d is 3, point3w is 4
     enum Points { point2d = 2, point2w,
                   point3d = 3, point3w };
     const size_t array_size = 1024;
     // ok: pt2w promoted to int
     int chunk_size = array_size * pt2w;
     int array_3d = array_size * point3d;

The type to which an enum object or enumerator is promoted is machine-defined and depends on the value of the largest enumerator. Regardless of that value, an enum or enumerator is always promoted at least to int. If the largest enumerator does not fit in an int, then the promotion is to the smallest type larger than int (unsigned int, long or unsigned long) that can hold the enumerator value.

enum 对象或枚举成员提升为什么类型由机器定义,并且依赖于枚举成员的最大值。无论其最大值是什么, enum 对象或枚举成员至少提升为 int 型。如果 int 型无法表示枚举成员的最大值,则提升到能表示所有枚举成员值的、大于 int 型的最小类型( unsigned intlongunsigned long)。

Conversion to const
转换为 const 对象

A nonconst object can be converted to a const object, which happens when we use a nonconst object to initialize a reference to const object. We can also convert the address of a nonconst object (or convert a nonconst pointer) to a pointer to the related const type:

当使用非 const 对象初始化 const 对象的引用时,系统将非 const 对象转换为 const 对象。此外,还可以将非 const 对象的地址(或非 const 指针)转换为指向相关 const 类型的指针:

     int i;
     const int ci = 0;
     const int &j = i;   // ok: convert non-const to reference to const int
     const int *p = &ci; // ok: convert address of non-const to address of a const
Conversions Defined by the Library Types
由标准库类型定义的转换

Class types can define conversions that the compiler will apply automatically. Of the library types we've used so far, there is one important conversion that we have used. When we read from an istream as a condition

类类型可以定义由编译器自动执行的类型转换。迄今为止,我们使用过的标准库类型中,有一个重要的类型转换。从 istream 中读取数据,并将此表达式作为 while 循环条件:

     string s;
     while (cin >> s)

we are implicitly using a conversion defined by the IO library. In a condition such as this one, the expression cin >> s is evaluated, meaning cin is read. Whether the read succeeds or fails, the result of the expression is cin.

这里隐式使用了 IO 标准库定义的类型转换。在与此类似的条件中,求解表达式 cin >> s,即读 cin。无论读入是否成功,该表达式的结果都是 cin

The condition in the while expects a value of type bool, but it is given a value of type istream. That istream value is converted to bool. The effect of converting an istream to bool is to test the state of the stream. If the last attempt to read from cin succeeded, then the state of the stream will cause the conversion to bool to be truethe while test will succeed. If the last attempt failedsay because we hit end-of-filethen the conversion to bool will yield false and the while condition will fail.

while 循环条件应为 bool 类型的值,但此时给出的却是 istream 类类型的值,于是 istream 类型的值应转换为 bool 类型。将 istream 类型转换为 bool 类型意味着要检验流的状态。如果最后一次读 cin 的尝试是成功的,则流的状态将导致上述类型转换为 bool 类型后获得 true 值——while 循环条件成立。如果最后一次尝试失败,比如说已经读到文件尾了,此时将 istream 类型转换为 bool 类型后得 falsewhile 循环条件不成立。

Exercises Section 5.12.3

Exercise 5.31:

Given the variable definitions on page 180, explain what conversions take place when evaluating the following expressions:

记住,你可能需要考虑操作符的结合性,以便在表达式含有多个操作符的情况下确定答案。

     (a) if (fval)
     (b) dval = fval + ival;
     (c) dval + ival + cval;

Remember that you may need to consider associativity of the operators in order to determine the answer in the case of expressions involving more than one operator.

记住,你可能需要考虑操作符的结合性,以便 在表达式含有多个操作符的情况下确定答案。

5.12.4. Explicit Conversions

5.12.4. 显式转换

An explicit conversion is spoken of as a cast and is supported by the following set of named cast operators: static_cast, dynamic_cast, const_cast, and reinterpret_cast.

显式转换也称为强制类型转换(cast),包括以下列名字命名的强制类型转换操作符:static_castdynamic_castconst_castreinterpret_cast

Although necessary at times, casts are inherently dangerous constructs.

虽然有时候确实需要强制类型转换,但是它们本质上是非常危险的。

5.12.5. When Casts Might Be Useful

5.12.5. 何时需要强制类型转换

One reason to perform an explicit cast is to override the usual standard conversions. The following compound assignment

因为要覆盖通常的标准转换,所以需显式使用强制类型转换。下面的复合赋值:

     double dval;
     int ival;
     ival *= dval; // ival = ival * dval

converts ival to double in order to multiply it by dval. That double result is then truncated to int in order to assign it to ival. We can eliminate the unnecessary conversion of ival to double by explicitly casting dval to int:

为了与 dval 做乘法操作,需将 ival 转换为 double 型,然后将乘法操作的 double 型结果截尾为 int 型,再赋值给 ival。为了去掉将 ival 转换为 double 型这个不必要的转换,可通过如下强制将 dval 转换为 int 型:

     ival *= static_cast<int>(dval); // converts dval to int

Another reason for an explicit cast is to select a specific conversion when more than one conversion is possible. We will look at this case more closely in Chapter 14.

显式使用强制类型转换的另一个原因是:可能存在多种转换时,需要选择一种特定的类型转换。我们将在第 14 章中详细讨论这种情况。

5.12.6. Named Casts

5.12.6. 命名的强制类型转换

The general form for the named cast notation is the following:

命名的强制类型转换符号的一般形式如下:

     cast-name<type>(expression);

cast-name may be one of static_cast, const_cast, dynamic_cast, or reinterpret_cast. type is the target type of the conversion, and expression is the value to be cast. The type of cast determines the specific kind of conversion that is performed on the expression.

其中 cast-namestatic_castdynamic_castconst_castreinterpret_cast 之一,type 为转换的目标类型,而 expression 则是被强制转换的值。强制转换的类型指定了在 expression 上执行某种特定类型的转换。

dynamic_cast

A dynamic_cast supports the run-time identification of objects addressed either by a pointer or reference. We cover dynamic_cast in Section 18.2 (p. 772).

dynamic_cast 支持运行时识别指针或引用所指向的对象。对 dynamic_cast 的讨论将在第 18.2 节中进行。

const_cast

A const_cast, as its name implies, casts away the constness of its expression. For example, we might have a function named string_copy that we are certain reads, but does not write, its single parameter of type char*. If we have access to the code, the best alternative would be to correct it to take a const char*. If that is not possible, we could call string_copy on a const value using a const_cast:

const_cast ,顾名思义,将转换掉表达式的 const 性质。例如,假设有函数 string_copy,只有唯一的参数,为 char* 类型,我们对该函数只读不写。在访问该函数时,最好的选择是修改它让它接受 const char* 类型的参数。如果不行,可通过 const_cast 用一个 const 值调用 string_copy 函数:

     const char *pc_str;
     char *pc = string_copy(const_cast<char*>(pc_str));

Only a const_cast can be used to cast away constness. Using any of the other three forms of cast in this case would result in a compile-time error. Similarly, it is a compile-time error to use the const_cast notation to perform any type conversion other than adding or removing const.

只有使用 const_cast 才能将 const 性质转换掉。在这种情况下,试图使用其他三种形式的强制转换都会导致编译时的错误。类似地,除了添加或删除 const 特性,用 const_cast 符来执行其他任何类型转换,都会引起编译错误。

static_cast

Any type conversion that the compiler performs implicitly can be explicitly requested by using a static_cast:

编译器隐式执行的任何类型转换都可以由 static_cast 显式完成:

     double d = 97.0;
     // cast specified to indicate that the conversion is intentional
     char ch = static_cast<char>(d);

Such casts are useful when assigning a larger arithmetic type to a smaller type. The cast informs both the reader of the program and the compiler that we are aware of and are not concerned about the potential loss of precision. Compilers often generate a warning for assignments of a larger arithmetic type to a smaller type. When we provide the explicit cast, the warning message is turned off.

当需要将一个较大的算术类型赋值给较小的类型时,使用强制转换非常有用。此时,强制类型转换告诉程序的读者和编译器:我们知道并且不关心潜在的精度损失。对于从一个较大的算术类型到一个较小类型的赋值,编译器通常会产生警告。当我们显式地提供强制类型转换时,警告信息就会被关闭。

A static_cast is also useful to perform a conversion that the compiler will not generate automatically. For example, we can use a static_cast to retrieve a pointer value that was stored in a void* pointer (Section 4.2.2, p. 119):

如果编译器不提供自动转换,使用 static_cast 来执行类型转换也是很有用的。例如,下面的程序使用 static_cast 找回存放在 void* 指针中的值(第 4.2.2 节):

     void* p = &d; // ok: address of any data object can be stored in a void*
     // ok: converts void* back to the original pointer type
     double *dp = static_cast<double*>(p);

When we store a pointer in a void* and then use a static_cast to cast the pointer back to its original type, we are guaranteed that the pointer value is preserved. That is, the result of the cast will be equal to the original address value.

可通过 static_cast 将存放在 void* 中的指针值强制转换为原来的指针类型,此时我们应确保保持指针值。也就是说,强制转换的结果应与原来的地址值相等。

reinterpret_cast

A reinterpret_cast generally performs a low-level reinterpretation of the bit pattern of its operands.

reinterpret_cast 通常为操作数的位模式提供较低层次的重新解释。

A reinterpret_cast is inherently machine-dependent. Safely using reinterpret_cast requires completely understanding the types involved as well as the details of how the compiler implements the cast.

reinterpret_cast 本质上依赖于机器。为了安全地使用 reinterpret_cast,要求程序员完全理解所涉及的数据类型,以及编译器实现强制类型转换的细节。

As an example, in the following cast

例如,对于下面的强制转换:

     int *ip;
     char *pc = reinterpret_cast<char*>(ip);

the programmer must never forget that the actual object addressed by pc is an int, not a character array. Any use of pc that assumes it's an ordinary character pointer is likely to fail at run time in interesting ways. For example, using it to initialize a string object such as

程序员必须永远记得 pc 所指向的真实对象其实是 int 型,而并非字符数组。任何假设 pc 是普通字符指针的应用,都有可能带来有趣的运行时错误。例如,下面语句用 pc 来初始化一个 string 对象:

     string str(pc);

is likely to result in bizarre run-time behavior.

它可能会引起运行时的怪异行为。

The use of pc to initialize str is a good example of why explicit casts are dangerous. The problem is that types are changed, yet there are no warnings or errors from the compiler. When we initialized pc with the address of an int, there is no error or warning from the compiler because we explicitly said the conversion was okay. Any subsequent use of pc will assume that the value it holds is a char*. The compiler has no way of knowing that it actually holds a pointer to an int. Thus, the initialization of str with pc is absolutely correctalbeit in this case meaningless or worse! Tracking down the cause of this sort of problem can prove extremely difficult, especially if the cast of ip to pc occurs in a file separate from the one in which pc is used to initialize a string.

pc 初始化 str 这个例子很好地说明了显式强制转换是多么的危险。问题源于类型已经改变时编译器没有提供任何警告或错误提示。当我们用 int 型地址初始化 pc 时,由于显式地声明了这样的转换是正确的,因此编译器不提供任何错误或警告信息。后面对 pc 的使用都假设它存放的是 char* 型对象的地址,编译器确实无法知道 pc 实际上是指向 int 型对象的指针。因此用 pc 初始化 str 是完全正确的——虽然实际上是无意义的或是错误的。查找这类问题的原因相当困难,特别是如果 ippc 的强制转换和使用 pc 初始化 string 对象这两个应用发生在不同文件中的时候。

Advice: Avoid Casts

建议:避免使用强制类型转换

By using a cast, the programmer turns off or dampens normal type-checking (Section 2.3, p. 44). We strongly recommend that programmers avoid casts and believe that most well-formed C++ programs can be written without relying on casts.

强制类型转换关闭或挂起了正常的类型检查(第 2.3 节)。强烈建议程序员避免使用强制类型转换,不依赖强制类型转换也能写出很好的 C++ 程序。

This advice is particularly important regarding use of reinterpret_casts. Such casts are always hazardous. Similarly, use of const_cast almost always indicates a design flaw. Properly designed systems should not need to cast away const. The other casts, static_cast and dynamic_cast, have their uses but should be needed infrequently. Every time you write a cast, you should think hard about whether you can achieve the same result in a different way. If the cast is unavoidable, errors can be mitigated by limiting the scope in which the cast value is used and by documenting all assumptions about the types involved.

这个建议在如何看待 reinterpret_cast 的使用时非常重要。此类强制转换总是非常危险的。相似地,使用 const_cast 也总是预示着设计缺陷。设计合理的系统应不需要使用强制转换抛弃 const 特性。其他的强制转换,如 static_castdynamic_cast,各有各的用途,但都不应频繁使用。每次使用强制转换前,程序员应该仔细考虑是否还有其他不同的方法可以达到同一目的。如果非强制转换不可,则应限制强制转换值的作用域,并且记录所有假定涉及的类型,这样能减少错误发生的机会。

5.12.7. Old-Style Casts

5.12.7. 旧式强制类型转换

Prior to the introduction of named cast operators, an explicit cast was performed by enclosing a type in parentheses:

在引入命名的强制类型转换操作符之前,显式强制转换用圆括号将类型括起来实现:

     char *pc = (char*) ip;

The effect of this cast is the same as using the reinterpret_cast notation. However, the visibility of this cast is considerably less, making it even more difficult to track down the rogue cast.

效果与使用 reinterpret_cast 符号相同,但这种强制转换的可视性比较差,难以跟踪错误的转换。

Standard C++ introduced the named cast operators to make casts more visible and to give the programmer a more finely tuned tool to use when casts are necessary. For example, nonpointer static_casts and const_casts tend to be safer than reinterpret_casts. As a result, the programmer (as well as readers and tools operating on the program) can clearly identify the potential risk level of each explicit cast in code.

标准 C++ 为了加强类型转换的可视性,引入命名的强制转换操作符,为程序员在必须使用强制转换时提供了更好的工具。例如,非指针的 static_castconst_cast 要比 reinterpret_cast 更安全。结果使程序员(以及读者和操纵程序的工具)可清楚地辨别代码中每个显式的强制转换潜在的风险级别。

Although the old-style cast notation is supported by Standard C++, we recommend it be used only when writing code to be compiled either under the C language or pre-Standard C++.

虽然标准 C++ 仍然支持旧式强制转换符号,但是我们建议,只有在 C 语言或标准 C++ 之前的编译器上编写代码时,才使用这种语法。

The old-style cast notation takes one of the following two forms:

旧式强制转换符号有下列两种形式:

     type (expr); // Function-style cast notation
     (type) expr; // C-language-style cast notation

Depending on the types involved, an old-style cast has the same behavior as a const_cast, a static_cast, ora reinterpret_cast. When used where a static_cast or a const_cast would be legal, an old-style cast does the same conversion as the respective named cast. If neither is legal, then an old-style cast performs a reinterpret_cast. For example, we might rewrite the casts from the previous section less clearly using old-style notation:

旧式强制转换依赖于所涉及的数据类型,具有与 const_caststatic_castreinterpret_cast 一样的行为。在合法使用 static_castconst_cast 的地方,旧式强制转换提供了与各自对应的命名强制转换一样的功能。如果这两种强制转换均不合法,则旧式强制转换执行 reinterpret_cast 功能。例如,我们可用旧式符号重写上一节的强制转换:

     int ival; double dval;
     ival += int (dval); // static_cast: converts double to int
     const char* pc_str;
     string_copy((char*)pc_str); // const_cast: casts away const
     int *ip;
     char *pc = (char*)ip; // reinterpret_cast: treats int* as char*

The old-style cast notation remains supported for backward compatibility with programs written under pre-Standard C++ and to maintain compatibility with the C language.

支持旧式强制转换符号是为了对“在标准 C++ 之前编写的程序”保持向后兼容性,并保持与 C 语言的兼容性。

Exercises Section 5.12.7

Exercise 5.32:

Given the following set of definitions,

给定下列定义:

     char cval;  int ival;   unsigned int ui;
     float fval;             double dval;

identify the implicit type conversions, if any, taking place:

指出可能发生的(如果有的话)隐式类型转换:

     (a) cval = 'a' + 3;        (b) fval = ui - ival * 1.0;
     (c) dval = ui * fval;      (d) cval = ival + fval + dval;

Exercise 5.33:

Given the following set of definitions,

给定下列定义:

     int ival;                         double dval;
     const string *ps;    char *pc;    void *pv;

rewrite each of the following using a named cast notation:

用命名的强制类型转换符号重写下列语句:

     (a) pv = (void*)ps;     (b) ival = int(*pc);
     (c) pv = &dval;         (d) pc = (char*) pv;


Team LiB
Previous Section Next Section