1、枚举
可以为一个或多个枚举成员提供初始值,用来初始化枚举成员的值必须是一个常量表达式,常量表达式是编译器在编译时就能够计算出结果的整型表达式。整型字面值常量是常量表达式。
例如,可以定义下列枚举类型:
// shape is 1, sphere is 2, cylinder is 3, polygon is 4
enum Forms {shape = 1, sphere, cylinder, polygon};
枚举成员值可以是不唯一的。
// point2d is 2, point2w is 3, point3d is 3, point3w is 4
enum Points { point2d = 2, point2w,
point3d = 3, point3w };
不能改变枚举成员的值。枚举成员本身就是一个常量表达式,所以也可用于需要常量表达式的任何地方。
2、C风格字符串
C 风格字符串既不能确切地归结为 C 语言的类型,也不能归结为 C++ 语言的类型,而是以空字符 null 结束的字符数组:
char ca1[] = {'C', '+', '+'}; // no null, not C-style string
char ca2[] = {'C', '+', '+', '\0'}; // explicit null
char ca3[] = "C++"; // null terminator added automatically
const char *cp = "C++"; // null terminator added automatically
char *cp1 = ca1; // points to first element of a array, but not C-style string
char *cp2 = ca2; // points to first element of a null-terminated char array
PS:标准库string 类型和字符串字面值为了与 C 语言兼容,字符串字面值与标准库 string 类型不是同一种类型。
3、指针和 typedef
typedef 中使用指针往往会带来意外的结果。下面是一个几乎所有人刚开始时都会答错的问题。假设给出以下语句:
typedef string *pstring;
const pstring cstr;
注意上例中,const修饰的是pstring本身,所以这是一个指向string的const指针,而不是一个指向const string的指针。
4、类型转换
4.1 何时发生隐式类型转换
编译器在必要时将类型转换规则应用到内置类型和类类型的对象上。在下列情况下,将发生隐式类型转换:
- 在混合类型的表达式中,其操作数被转换为相同的类型:
int ival;
double dval;
ival >= dval ;// ival converted to double
- 用作条件的表达式被转换为 bool 类型:
int ival;
if (ival) // ival converted to bool
while (cin) // cin converted to bool
- 用一表达式初始化某个变量,或将一表达式赋值给某个变量,则该表达式被转换为该变量的类型:
int ival = 3.14; // 3.14 converted to int
int *ip;
ip = 0; // the int 0 converted to a null pointer of type int *
另外,在函数调用中也可能发生隐式类型转换,我们将在第七章学习这方面的内容。
4.2 算术转换
最简单转换为整型提升:对于所有比 int 小的整型,包括 char、signed char、unsigned char、short 和 unsigned short,如果该类型的所有可能的值都能包容在 int 内,它们就会被提升为 int 型,否则,它们将被提升为unsigned int。如果将 bool 值提升为 int ,则 false 转换为 0,而 true 则转换为 1。
有符号与无符号类型之间的转换,若表达式中使用了无符号( unsigned )数值,所定义的转换规则需保护操作数的精度,unsigned 操作数的转换依赖于机器中整型的相对大小,因此,这类转换本质上依赖于机器。
包含 short 和 int 类型的表达式, short 类型的值转换为 int 。如果int 型足够表示所有unsigned short 型的值,则将 unsigned short 转换为int,否则,将两个操作数均转换为unsigned int
在 32 位的机器上,long 和 int 型通常用一个字长表示,因此当表达式包含 unsigned int 和 long 两种类型,其操作数都应转换为 unsigned long 型。
4.3 其他隐式转换
1.指针转换
在使用数组时,大多数情况下数组都会自动转换为指向第一个元素的指针:
int ia[10]; // array of 10 ints
int* ip = ia; // convert ia to pointer to first element
不将数组转换为指针的例外情况有:数组用作取地址(&)操作符的操作数或 sizeof 操作符的操作数时,或用数组对数组的引用进行初始化时,不会将数组转换为指针。我们将在第 7.2.4 节学习如何定义指向数组的引用(或指针)。
C++ 还提供了另外两种指针转换:指向任意数据类型的指针都可转换为void* 类型;整型数值常量 0 可转换为任意指针类型。
2.转换为bool 类型
算术值和指针值都可以转换为 bool 类型。如果指针或算术值为 0,则其bool 值为 false ,而其他值则为 true:
if (cp) /* ... */ // true if cp is not zero
while (*cp) /* ... */ // dereference cp and convert resulting char to bool
这里,if 语句将 cp 的非零值转换为 true。 while 语句则对 cp 进行解引用,操作结果产生一个char 型的值。空字符( null )具有 0 值,被转换为 false,而其他字符值则转换为 true。
4.4 显式转换
显式转换也称为强制类型转换(cast),包括以下列名字命名的强制类型转换操作符:static_cast、dynamic_cast、const_cast 和 reinterpret_cast。
http://www.360doc.com/content/15/1028/09/7510008_508889811.shtml
dynamic_cast :支持运行时识别指针或引用所指向的对象,主要用于上下行转换,转换的成功与否取决于要转换的指针或引用的实际指向,但保证程序不会出错,也就是成功失败都有相应的返回值。
const_cast :只能用于指针或引用,将转换掉表达式的 const 性质。假设有函数 string_copy,只有唯一的参数为 char* 类型,我们对该函数只读不写。在访问该函数时,最好的选择是修改它让它接受 const char* 类型的参数。string_copy 函数使用:
const char *pc_str;
char *pc = string_copy(const_cast<char*>(pc_str));
只有使用 const_cast 才能将 const 性质转换掉。在这种情况下,试图使用其他三种形式的强制转换都会导致编译时的错误。类似地,除了添加或删除const 特性,用 const_cast 符来执行其他任何类型转换,都会引起编译错误。
static_cast:编译器隐式执行的任何类型转换都可以由 static_cast 显式完成,但是这种转换不提供运行时识别指针或引用所指向的对象,一旦遇到不能转换将会发生错误,主要用于内置类型的转换等。
double d = 97.0;
// cast specified to indicate that the conversion is intentional
char ch = static_cast<char>(d);
当我们显式地提供强制类型转换时,警告信息就会被关闭。如果编译器不提供自动转换,使用 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);
可通过 static_cast 将存放在 void* 中的指针值强制转换为原来的指针类型,此时我们应确保保持指针值。也就是说,强制转换的结果应与原来的地址值相等。
reinterpret_cast: 通常为操作数的位模式提供较低层次的重新解释,依赖于机器。为了安全使用reinterpret_cast,要求完全理解所涉及的数据类型,以及编译器实现强制类型转换的细节。
例如,对于下面的强制转换:
int *ip;
char *pc = reinterpret_cast<char*>(ip);
程序员必须永远记得 pc 所指向的真实对象其实是 int 型,而并非字符数组。任何假设 pc 是普通字符指针的应用,都有可能带来有趣的运行时错误。
4.6.5 旧式强制类型转换
在引入命名的强制类型转换操作符之前,显式强制转换用圆括号将类型括起来实现:
char *pc = (char*) ip;
效果与使用 reinterpret_cast 符号相同,但这种强制转换的可视性比较差,难以跟踪错误的转换。
标准 C++ 为了加强类型转换的可视性,引入命名的强制转换操作符,为程序员在必须使用强制转换时提供了更好的工具。例如,非指针的 static_cast 和const_cast 要比 reinterpret_cast 更安全。结果使程序员可清楚地辨别代码中每个显式的强制转换潜在的风险级别。虽然标准 C++ 仍然支持旧式强制转换符号,但是我们建议,只有在 C 语言或标准 C++ 之前的编译器上编写代码时,才使用这种语法。
旧式强制转换符号有下列两种形式:
type (expr); // Function-style cast notation
(type) expr; // C-language-style cast notation
旧式强制转换依赖于所涉及的数据类型,具有与 const_cast、 static_cast和 reinterpret_cast 一样的行为。在合法使用 static_cast 或 const_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*
支持旧式强制转换符号是为了对“在标准 C++ 之前编写的程序”保持向后兼容性,并保持与 C 语言的兼容性。
PS:IO类型对象不可复制;