面向对象编程(OOP)的本质是设计并扩展自己的数据类型。
设计自己的数据类型就是让类型与数据匹配。如果正确做到了这一点,将会发现以后使用数据时会容易得多。然而,在创建自己的类型之前,必须了解并理解C++内置的类型,因为这些类型是创建自己类型的基本组件。
内置的C++类型分两组:基本类型和复合类型。
本章将介绍基本类型,即整数和浮点数。似乎只有两种类型,但C++知道,没有任何一种整型和浮点型能够满足所有的编程要求,因此对于这两种数据,它提供了多种变体。
当然,程序还需要一种标识存储的数据的方法,本章将介绍这样一种方法—使用变量;然后介绍如何在C++中进行算术运算;最后,介绍C++如何将值从一种类型转换为另一种类型。
一、 简单变量
程序通常都需要存储信息—如某只股票当前的价格、杭州市8月份的平均湿度。为把信息存储在计算机中,程序必须记录3个基本属性:
-
信息将存储在哪里
-
要存储什么值
-
存储何种类型的信息
到目前为止,本书的示例采取的策略都是声明一个变量。声明中使用的类型描述了信息的类型和通过符号来表示其值的变量名。例如,使用了下面的语句:
int braincount;
braincount = 5;
这些语句告诉程序,它正在存储整数,并使用名称braincount来表示该整数的值(这里为5)。
实际上,程序将找到一块能够存储整数的内存,将该内存单元标记为braincount,并将5复制到该内存单元中;然后,您可在程序中使用braincount来访问该内存单元。这些语句没有告诉您,这个值将存储在内存的什么位置,但程序确实记录了这种信息。
实际上,可以使用&运算符来检索braincount的内存地址。
1. 变量名
C++提倡使用有一定含义的变量名。必须遵循几种简单的C++命名规则:
-
在名称中只能使用字母字符、数字和下划线(_)
-
名称的第一个字符不能是数字
-
区分大写字符与小写字符
-
不能将C++关键字用作名称
-
以两个下划线或下划线和大写字母打头的名称被保留给实现(编译器及其使用的资源)使用。以一个下划线开头的名称被保留给实现,用作全局标识符。
-
C++对于名称的长度没有限制,名称中所有的字符都有意义,但有些平台有长度限制
倒数第二点与前面几点有些不同,因为使用像_time_stop或_Donut这样的名称不会导致编译器错误,而会导致行为的不确定性。
换句话说,不知道结果将是什么。不出现编译器错误的原因是,这样的名称不是非法的,但要留给实现使用。
全局名称指的是名称被声明的位置。
最后一点使得C++与ANSI C(C99标准)有所区别,后者只保证名称中的前63个字符有意义(在ANSI C中,前63个字符相同的名称被认为是相同的,即使第64个字符不同)。
下面是一些有效和无效的C++名称:
如果想用两个或更多的单词组成一个名称,通常的做法是用下划线字符将单词分开,如my_onions;或者从第二个单词开始将每个单词的第一个字母大写,如myEyeTooth。
2. 整型
整数就是没有小数部分的数字,如2、98、-5286和0。
整数有很多,如果将无限大的整数看作很大,则不可能用有限的计算机内存来表示所有的整数。
因此,语言只能表示所有整数的一个子集。
不同C++整型使用不同的内存量来存储整数。使用的内存量越大,可以表示的整数值范围也越大。
另外,有的类型(符号类型)可表示正值和负值,而有的类型(无符号类型)不能表示负值。
术语宽度(width)用于描述存储整数时使用的内存量。使用的内存越多,则越宽。
C++的基本整型(按宽度递增的顺序排列)分别是char、short、int、long和C++11新增的long long,其中每种类型都有符号版本和无符号版本,因此总共有10种类型可供选择。
3. 整型short、int、long和long long
计算机内存由一些叫做位(bit)的单元组成。
C++的short、int、long和long long类型通过使用不同数目的位来存储值,最多能够表示4种不同的整数宽度。
C++提供了一种灵活的标准,它确保了最小长度(从C语言借鉴而来),如下所示:
-
short至少16位
-
int至少与short一样长
-
long至少32位,且至少与int一样长
-
long long至少64位,且至少与long一样长
当前很多系统都使用最小长度,即short为16位,long为32位。这仍然为int提供了多种选择,其宽度可以是16位、24位或32位,同时又符合标准;甚至可以是64位,因为long和long long至少长64位。
可以像使用int一样,使用这些类型名来声明变量:
实际上,short是short int的简称,而long是long int的简称,但是程序设计者们几乎都不使用比较长的形式。
这4种类型(int、short、long和long long)都是符号类型,这意味着每种类型的取值范围中,负值和正值几乎相同。例如,16位的int的取值范围为-32768到+32767。
要知道系统中整数的最大长度,可以在程序中使用C++工具来检查类型的长度。
首先,sizeof运算符返回类型或变量的长度,单位为字节(运算符是内置的语言元素,对一个或多个数据进行运算,并生成一个值。例如,加号运算符+将两个值相加)。
前面说过,“字节”的含义依赖于实现,因此在一个系统中,两字节的int可能是16位,而在另一个系统中可能是32位。其次,头文件climits(在老式实现中为limits.h)中包含了关于整型限制的信息。具体地说,它定义了表示各种限制的符号名称。
我们看一下程序:
#include <iostream>
#include <climits>
int main() {
using namespace std;
int n_int = INT_MAX;
short n_short = SHRT_MAX;
long n_long = LONG_MAX;
long long n_long_long = LLONG_MAX;
cout << "int is " << sizeof(int) << " bytes." << endl;
cout << "short is " << sizeof(short) << " bytes." << endl;
cout << "long is " << sizeof(long) << " bytes." << endl;
cout << "long long is " << sizeof(long long) << " bytes." << endl;
cout << endl;
cout << "max-values : " << endl;
cout << "int : " << n_int << endl;
cout << "short : " << n_short << endl;
cout << "long : " << n_long << endl;
cout << "long long : " << n_long_long << endl;
cout << "min-values : " << INT_MIN << endl;
cout << "bites per byte : " << CHAR_BIT << endl;
return 0;
}
输出结果为:
int is 4 bytes.
short is 2 bytes.
long is 4 bytes.
long long is 8 bytes.
max-values :
int : 2147483647
short : 32767
long : 2147483647
long long : 9223372036854775807
min-values : -2147483648
bites per byte : 8
D:\C++Project\Project3\x64\Debug\Project3.exe (进程 28512)已退出,代码为 0。
要在调试停止时自动关闭控制台,请启用“工具”->“选项”->“调试”->“调试停止时自动关闭控制台”。
按任意键关闭此窗口. . .
这些输出来自运行64位Windows 11的系统。
我们来看一下该程序的主要编程特性:
1. 运算符sizeof和头文件limits
sizeof
运算符指出,在使用8位字节的系统中,int的长度为4个字节。
可对类型名或变量名使用sizeof运算符。对类型名(如int)使用sizeof运算符时,应将名称放在括号中;但对变量名(如n_short)使用该运算符,括号是可选的:
cout << "int is " << sizeof(int) << " bytes." << endl;
头文件climits
定义了符号常量来表示类型的限制。
如前所述,INT_MAX
表示类型int能够存储的最大值,对于Windows 11系统,为2147483647。编译器厂商提供了climits
文件,该文件指出了其编译器中的值。例如,在使用16位int的老系统中,climits文件将INT_MAX定义为32 767。
climits中的符号常量:
符号常量—预处理器方式
climits文件中包含与下面类似的语句行:
#define INT_MAX 32767
在C++编译过程中,首先将源代码传递给预处理器。
在这里,#define和#include一样,也是一个预处理器编译指令。该编译指令告诉预处理器:在程序中查找INT_MAX,并将所有的INT_MAX都替换为32767。因此#define编译指令的工作方式与文本编辑器或字处理器中的全局搜索并替换命令相似。修改后的程序将在完成这些替换后被编译。预处理器查找独立的标记(单独的单词),跳过嵌入的单词。也就是说,预处理器不会将PINT_MAXTM替换为P32767IM。也可以使用#define来定义自己的符号常量(参见程序清单3.2)。然而,#define编译指令是C语言遗留下来的。C++有一种更好的创建符号常量的方法(使用关键字const,将在后面的一节讨论),所以不会经常使用#define。然而,有些头文件,尤其是那些被设计成可用于C和C++中的头文件,必须使用#define。
2. 初始化
初始化将赋值与声明合并在一起。例如,下面的语句声明了变量n_int,并将int的最大取值赋给它:
int n_int = INT_MAX;
也可以使用字面值常量来初始化。可以将变量初始化为另一个变量,条件是后者已经定义过。
甚至可以使用表达式来初始化变量,条件是当程序执行到该声明时,表达式中所有的值都是已知的:
#include <iostream>
using namespace std;
int main() {
int uncles = 5;
int aunts = uncles;
int cha = aunts + uncles + 2;
cout << cha << endl;
return 0;
}
输出结果为:
12
D:\C++Project\Project3\x64\Debug\Project3.exe (进程 23368)已退出,代码为 0。
要在调试停止时自动关闭控制台,请启用“工具”->“选项”->“调试”->“调试停止时自动关闭控制台”。
按任意键关闭此窗口. . .
如果不对函数内部定义的变量进行初始化,该变量的值将是不确定的。这意味着该变量的值将是它被创建之前,相应内存单元保存的值。
如果知道变量的初始值应该是什么,则应对它进行初始化。将变量声明和赋值分开,可能会带来瞬间悬而未决的问题:
short year;
year = 2022;
然而,在声明变量时对它进行初始化,可避免以后忘记给它赋值的情况发生。
3. C++11初始化方式
还有另一种初始化方式,这种方式用于数组和结构,但在C++98中,也可用于单值变量:
int a = {24};
将大括号初始化器用于单值变量的情形还不多,但C++11标准使得这种情形更多了。
首先,采用这种方式时,可以使用等号(=),也可以不使用:
#include <iostream>
using namespace std;
int main() {
int a = { 5 };
cout << a << endl;
return 0;
}
输出结果为5。
其次,大括号内可以不包含任何东西。在这种情况下,变量将被初始化为零:
#include <iostream>
using namespace std;
int main() {
int a = { };
cout << a << endl;
return 0;
}
输出结果为:
0
D:\C++Project\Project3\x64\Debug\Project3.exe (进程 18704)已退出,代码为 0。
要在调试停止时自动关闭控制台,请启用“工具”->“选项”->“调试”->“调试停止时自动关闭控制台”。
按任意键关闭此窗口. . .
为何需要更多的初始化方法?有充分的理由吗?原因是让新手更容易学习C++,这可能有些奇怪。以前,C++使用不同的方式来初始化不同的类型:初始化类变量的方式不同于初始化常规结构的方式,而初始化常规结构的方式又不同于初始化简单变量的方式;通过使用C++新增的大括号初始化器,初始化常规变量的方式与初始化类变量的方式更像。
C++11使得可将大括号初始化器用于任何类型(可以使用等号,也可以不使用),这是一种通用的初始化语法。以后,教材可能介绍使用大括号进行初始化的方式,并出于向后兼容的考虑,顺便提及其他初始化方式。