第四章
4.1数组
声明的通用格式
typename arrayName[arrySize];
数组的初始化规则
int arr[4] = { 2, 3, 4, 5 };
int arr[4];
int arr[7] = { 2, 3, 4, 5 }; // 其他元素将设置为0
int arr[7] = { 0 }; // 所有元素都将初始化为0
int arr[] = { 2, 3, 4, 5 }; // 编译器自动计算数组长度
int nLength = sizeof(arr) / sizeof(int); // 求得数组长度
C++11数组初始化方法
int arr[4] { 2, 3, 4, 5 }; // 初始化数组时, 可以省略等号
int arr[4] {}; // 不包含任何东西, 将会全部被初始化为0
int arr[4] = {};
// 列表初始化禁止缩窄转换
模板类:
C++标准模板库STL提供了数组代替品——vector
C++11新增了模板类——array
第16章将会介绍
4.2字符串
1.一种来自C语言
2.一种来自string类库
1.C风格字符串
以空字符'\0'结尾, ASCII为0
cout 打印字符串时, 将会一直打印, 直到遇到空字符为止
可以使用字符串常量/字符串字面值初始化字符串, 隐式地包括了结尾的空字符
char bird[11] = "Mr. Cheeps";
char fish[] = "Bubbles";
C++输入工具通过键盘输入, 将字符串读到char数组中时, 将会自动加上'\0', 应确保数组足够大
拼接字符串常量
可以使用空格、制表符、换行符拼接
cout << "abc" "def" << endl;
在数组中使用字符串
字符串输入
cin 通过空格、制表符、换行符确定字符串结尾的位置
当输入比字符数组长时?
当想输入空格、换行、制表符时?
每次读取一行字符串输入
get()和getline()
这两个函数都读取一行输入, 直到遇到换行符。
区别:getline() 将会丢弃换行符, get()将会保留在输入队列中
相同:都不会将换行符放入数组中
问题:如果连续调用多次cin.get(), cin.get()将不能跨越换行符
可以写成
cin.get(arr, nLen).get(); // cin.get(arr, nLen) 将会返回一个cin对象, 这个对象接着调用get()
cin.getline(name, nLen).cin.getline(name, nLen); // 相当于连续调用两次cin.getline()
空行和其他问题
当get()读取空行时:将会设置失效位(failbit), 输入将被阻断
cin.clear(); // 恢复输入
当getline()读取空行时:下一条输入语句将会在读取结束的位置开始读取
字符串超出数组的长度时, cin.get()和cin.getlind()都将会把输入留在队列中, cin.getline() 会设置失效位, 关闭后面的输入
5、6、17章将会介绍这些属性
混合输入字符串和数字
使用cin.get()丢弃每次输入的换行符
(cin >> year).get();
4.3 string 类简介
头文件:, 命名空间:std
可以使用C风格字符串初始化string对象
可以使用cin从键盘输入存储到string对象中
可以使用cout显示string对象
可以使用数组表示法访问存储在string中的字符
string str1; // 未初始化长度为0
str1 = "panther"
cin >> str1; // 自动调整str1的长度
C++ 11字符串初始化
char first[] = { "Le Chapon Dodu" };
char second[] = { "The Elegant Plate" };
string third = { "The Bread Bowl" };
string fourth { "Hank's Fine Eats" };
赋值、拼接、附加
string对象可以相互赋值, 数组之间不可以
string str1;
string str2 = "panther";
str1 = str2;
支持的运算符
+、+=、=
string 类的其他操作
string 类的IO
string str1;
getline(cin, str1); // 读取一行
由于istream类早于string类, 没有考虑到string类, 所以不能使用cin.getline()
为何以下代码可行
cin >> str1;
使用了string类的一个友元函数
友元函数将在11章介绍
其他形式的字符串字面值
前缀
L:wchar_t
u:char16_t
U:char32_t
C++11:u8:Unicode编码方案UTF-8类型的字符串字面值
C++11:R:原始字符串, 使用"()"作为界定符
cout << R"(Jim "King" Tutt uses "\n" instead of endl.)" << endl;
输出:Jim "King" Tutt uses "\n" instead of endl.
自定义界定符
cout << R"+*(Jim "King" Tutt uses "\n" instead of endl.)+*" << endl;
输入原始字符串时, 回车不会移动到下一行, 会在原始字符串中添加回车符
4.4 结构简介
定义结构
创建结构类型的变量, C++允许省略关键字static
初始化
C++11结构初始化
inflatable duck {}; // 将会把每一个字节设置为0
不允许缩窄转换
结构体可以将string作为成员
其他结构属性
支持 =
支持成员变量之间的 =
可以声明没有名称的结构类型, 同时定义一个变量
struct
{
int x;
int y;
}position;
C++的结构也可以由成员函数, 将在11章介绍类时介绍
结构数组
与创建数组相同
结构中的位字段
可以设置每个成员占据的位数, 可以使用没有名称的字段提供间距, 每个成员都都被称之为位字段
可以创建指定字节的结构
可以使用整型和附录E的按位运算符代替这种方式
4.5 共用体
union是一种数据格式, 能储存不同的数据类型, 但同时只能存储其中的一中类型
共用体的长度是其最大成员的长度
用途, 当数据使用两种或更多的格式(但不会同时使用)时, 可以节省空间
匿名共用体没有名称, 其成员将成为位于相同地址处的变量
struct widget
{
char brand[20];
int type;
union
{
long id_num;
char id_char[20];
};
};
widget prize;
... ...
if (prize.type == 1)
cin >> prize.id_num;
else
cin >> prize.id_char;
常用于节省内存
常用于操作系统数据结构或硬件数据结构
4.6 枚举
enum:创建符号常量的方式, 可以代替const
允许定义新类型
enum spectrum { red, orange, yellow, green, blue, violet, indigo, ultraviolet };
默认情况下, 成员的值从0开始, 成员都是常量, 叫做枚举量
定义枚举类型变量时, 只能将枚举量赋值给变量
枚举量是整型, 可以被提升为int类型, 但是int类型不能转为enum类型
枚举经常用来定义相关符号常量, 而不是新类型。可以省略枚举类型的名称
设置枚举量的值
可以创建多个值相同的枚举量
枚举量的取值范围
可以通过计算范围, 将不是枚举量的值赋给枚举变量
C++11扩展了枚举, 增加了作用域内枚举, 第10章类作用域一节介绍
4.7 指针和自由存储空间
对变量应用 & 运算符, 即可得到变量的地址
对地址应用 * 运算符, 即可得到地址内存放的值
声明和初始化指针
int * ptr;
int * ptr1, ptr2; // 创建一个指针变量ptr1, 一个int变量ptr2
每个指针变量都需要一个 *
使用new分配内存
typeName * pointer_name = new typeName;
new分配的内存块的地址都被存储在栈(stack)的内存区域中
new从堆(heap)或自由存储区(free store)的内存区域分配内存
第9章将详细介绍
内存耗尽:第15章错误处理技术
使用delete释放内存
int * ps = new int;
... ...
delete ps;
使用new创建动态数组
动态联编:意味着数组是在程序运行时创建的。这种数组叫做动态数组
静态联编:在编译时给数组分配内存。
使用new创建动态数组
int * psome = new int [10];
... ...
delete [] psome;
通用格式
type_name * pointer_name = new type_name [num_elements];
使用动态数组
arrayname[i] 等价于 * (arrayname + i)
pointername[i] 等价于 * (pointername + i)
4.8 指针、数组和指针算术
C++将数组名解释为地址
数组名和指针的区别
数组名是常量
指针是变量
sizeof(数组名) = 数组的长度, 在这种情况下, C++不会将数组名解释为地址
sizeof(指针变量) = 指针的长度
数组的地址
对数组名取地址时, 数组名也不会被解释为地址
数组名被解释为第一个元素的地址, 对数组名取地址得到的是整个数组的地址
short tell[10];
cout << tell << endl;
cout << &tell << endl;
从数字上说, 这些地址相同, 但从概念上说, 他们是不同的
数组名是首元素的地址, 是一个2字节的内存
整个数组的地址是一个20字节的内存块的地址
short (*pas)[20] = &tell; // pas的类型为 short (*)[20]
指针和字符串
在C++中, 用引号括起来的字符串就像数组名一样
如果给cout提供字符串的地址, 它将从地址处开始打印字符, 直到遇到'\0'结束
同样, 如果给cout字符的地址, 它也将从地址处开始打印字符, 直到遇到'\0'结束
在cout和多数C++表达式中, char数组名、char指针、以及引号括起来的字符串常量, 都被解释为字符串第一个字符的地址
字符串字面值被视为常量
使用new创建动态结构
struct things
{
int good;
int bad;
};
things grubnose;
things * pointer_struct = &grubnose;
指针访问成员变量
pointer_struct->good;
(*pointer_struct).good;
关于C++如何处理内存的知识, 将在第9章做全面介绍
自动存储、静态存储和动态存储
C++由三种管理数据内存的方式:自动存储、静态存储、动态存储(自由存储空间或堆)
C++11:线程存储
1. 自动存储
在函数内部定义的变量使用自动存储空间, 被称为自动变量。
在函数调用时自动产生, 在函数结束时消亡, 仅在函数活动时存在。
自动变量是局部变量, 作用域为包含它的代码块。
自动变量通常储存在栈种。LILO
2. 静态存储
一种是在函数外面定义, 一种是使用关键字static。
存在程序的整个生命周期。
第9章详细介绍
3. 动态存储
new、delete
栈、堆和内存泄漏
C++智能指针将在16章介绍
4.9 类型组合
结构
结构指针
数组
结构数组
(结构指针)数组
……
4.10 数组的代替品
模板类 vector 和 array 是数组的替代品
模板类 vector
头文件: 命名空间:std
vector 内部使用 new 和 delete 管理内存, 这种工作是自动完成的
创建一个名为 vt 的 vector 对象, 他可以存储 n_elem 个类型为 typeName 的元素:
vector vt(n_elem);
n_elem 可以是整型常量, 也可以是整型变量
模板类 array (C++11)
头文件: 命名空间:std
vector 类的功能比数组强大, 但是效率稍低
array 对象的长度也是固定的, 也使用栈, 效率与数组相同, 但是更加安全、方便
array 对象可以相互赋值
array[InvalidCharacterError: "TYPENAME," did not match the Name production]
n_elem 不能是变量
比较 vector、array、 数组
使用at时, 将在运行期间捕获非法索引, 中断程序, 代价:使得运行时间更长
成员函数:begin(), end()将在16章讨论