04.复合类型
目录介绍
- 4.1 复合类型
- 4.1.1 复合数据类型
- 4.2 字符串
- 4.2.1 字符串表示
- 4.2.2 C风格字符串
- 4.2.3 C++风格字符串
- 4.3 string类
- 4.3.1 string类
- 4.3.2 声明和初始化
- 4.3.3 常用操作
- 4.3.4 字符串转换
- 4.3.5 字符串输入
- 4.3.6 字符串输出
- 4.4 数组
- 4.4.1 数组基本概念
- 4.4.2 数组的声明
- 4.4.3 数组初始化
- 4.4.4 访问数组元素
- 4.4.5 多维数组
- 4.4.6 数组作为函数参数
- 4.4.7 动态数组
- 4.4.8 数组的总结
- 4.5 联合体
- 4.5.1 什么是联合体
- 4.5.2 联合体定义
- 4.5.3 联合体特点
- 4.5.4 联合体使用
- 4.5.5 联合体与结构体
- 4.5.6 匿名联合体
- 4.5.7 联合体应用场景
- 4.5.8 注意事项
- 4.6 枚举
- 4.6.1 无作用域枚举
- 4.6.2 有作用域枚举
- 4.6.3 枚举底层类型
- 4.6.4 枚举应用场景
- 4.6.5 枚举注意事项
- 4.8 容器
- 4.8.1 vector
- 4.8.2 list
- 4.8.4 map
- 4.8.4 unordered_map
- 4.8.5 array
- 4.9 练习题
- 4.9.1 数组冒泡排序
- 4.9.1 数组成绩统计
4.1 复合类型
4.1 复合数据类型
C++ 中的复合数据类型是由基本数据类型组合而成的更复杂的数据结构。它们允许将多个值组织在一起,以便更高效地管理和操作数据。
复合数据类型:
- array:数组类型,用于存储一组相同类型的元素。
- struct:结构体类型,用于定义自定义的复合数据类型,可以包含多个不同类型的成员变量。
- class:类类型,类似于结构体,但可以包含成员函数和访问控制。
4.2 字符串
4.2.1 字符串表示
字符串 是一种用于存储和操作文本数据的数据类型。C++ 提供了两种主要的字符串表示方式:
- C 风格字符串:基于字符数组的字符串。
std::string类:C++ 标准库提供的字符串类,功能更强大、更安全。
4.2.2 C风格字符串
C风格字符串: char 变量名[] = "字符串值"
示例:
int main() {
char str1[] = "hello world";
cout << str1 << endl;
return 0;
}
注意:C风格的字符串要用双引号括起来
4.2.3 C++风格字符串
C++风格字符串: string 变量名 = "字符串值"
示例:
int main() {
string str = "hello world";
cout << str << endl;
return 0;
}
注意:C++风格字符串,需要加入头文件==#include<string>==
4.3 string类
4.3.1 string类
std::string 是 C++ 标准库提供的字符串类,功能更强大、更安全。
4.3.2 声明和初始化
string str = "Hello";
string str2("World");
4.3.3 常用操作
std::string 提供了丰富的成员函数和操作符,如 length、append、substr 等。
#include <iostream>
#include <string>
using namespace std;
int main() {
string str1 = "Hello";
string str2 = "World";
// 获取字符串长度
cout << "Length of str1: " << str1.length() << endl;
// 连接字符串
string result = str1 + " " + str2;
cout << "Concatenated string: " << result << endl;
// 提取子字符串
string substr = result.substr(6, 5);
cout << "Substring: " << substr << endl;
// 查找子字符串
size_t pos = result.find("World");
if (pos != string::npos) {
cout << "Found 'World' at position: " << pos << endl;
}
return 0;
}
输出:
Length of str1: 5
Concatenated string: Hello World
Substring: World
Found 'World' at position: 6
4.3.4 字符串转换
1.std::string 转 C 风格字符串 使用 c_str() 方法。
string str = "Hello";
const char *cstr = str.c_str();
cout << "C-style string: " << cstr << endl;
2.C 风格字符串转 std::string 直接赋值即可。
const char *cstr = "Hello";
string str = cstr;
cout << "std::string: " << str << endl;
4.3.5 字符串输入
使用 cin 或 getline。
int main() {
string str;
cout << "Enter a string: ";
getline(cin, str); // 读取一行输入
cout << "You entered: " << str << endl;
return 0;
}
4.3.6 字符串输出
使用 cout。
int main() {
string str = "Hello World";
cout << "String: " << str << endl;
return 0;
}
4.4 数组
4.4.1 数组基本概念
在 C++ 中,数组 是一种用于存储相同类型元素的连续内存数据结构。数组的大小在声明时固定,不能动态改变。
数组是一组相同类型元素的集合,这些元素在内存中连续存储。数组的每个元素可以通过索引访问。
4.4.2 数组的声明
数据类型 数组名[数组大小];
数据类型:数组中元素的类型(如int、double等)。数组名:数组的名称。数组大小:数组中元素的数量,必须是一个常量表达式。
示例
int arr[5]; // 声明一个包含 5 个整数的数组
4.4.3 数组初始化
数组可以在声明时初始化,也可以后续赋值。
1.声明时初始化
int arr1[5] = {1, 2, 3, 4, 5}; // 完全初始化
int arr2[5] = {1, 2}; // 部分初始化,未初始化的元素为 0
int arr3[] = {1, 2, 3, 4, 5}; // 自动推断数组大小
2.后续赋值
int arr[5];
arr[0] = 10;
arr[1] = 20;
4.4.4 访问数组元素
数组元素通过索引访问,索引从 0 开始。
示例
#include <iostream>
using namespace std;
int main() {
int arr[5] = {10, 20, 30, 40, 50};
cout << "First element: " << arr[0] << endl;
cout << "Third element: " << arr[2] << endl;
return 0;
}
4.4.5 多维数组
数据类型 数组名[行数][列数];
案例如下:
#include <iostream>
using namespace std;
int main() {
int arr[2][3] = {{1, 2, 3}, {4, 5, 6}};
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 3; j++) {
cout << "arr[" << i << "][" << j << "] = " << arr[i][j] << endl;
}
}
return 0;
}
4.4.6 数组作为函数参数
数组可以作为函数参数传递,通常以指针的形式传递。示例
#include <iostream>
using namespace std;
void printArray(int arr[], int size) {
for (int i = 0; i < size; i++) {
cout << arr[i] << " ";
}
cout << endl;
}
int main() {
int arr[5] = {1, 2, 3, 4, 5};
printArray(arr, 5); // 传递数组和大小
return 0;
}
4.4.7 动态数组
C++ 使用 new 和 delete 运算符动态分配和释放数组。
动态分配数组
int *arr = new int[5]; // 动态分配一个包含 5 个整数的数组
for (int i = 0; i < 5; i++) {
arr[i] = i + 1;
}
释放数组
delete[] arr; // 释放动态数组
4.4.8 数组的总结
- 数组是存储相同类型元素的连续内存数据结构。
- 数组的大小在声明时固定,不能动态改变。
- 数组元素通过索引访问,索引从
0开始。 - 数组可以作为函数参数传递,通常以指针的形式传递。
- 动态数组使用
new和delete进行内存管理。 - 标准库中的
std::array和std::vector比原生数组更安全、更灵活。
4.5 联合体
4.5.1 什么是联合体
在 C++ 中,联合体(Union) 是一种特殊的数据类型,它允许在相同的内存位置存储不同的数据类型。
联合体的所有成员共享同一块内存空间,因此联合体的大小等于其最大成员的大小。
联合体的主要用途是节省内存,尤其是在需要存储多种类型的数据但同一时间只使用其中一种的情况下。
4.5.2 联合体定义
联合体的定义语法与结构体类似,使用关键字 union。语法
union 联合体名 {
数据类型 成员1;
数据类型 成员2;
// ...
};
示例
union MyUnion {
int i;
float f;
char c;
};
4.5.3 联合体特点
- 共享内存: 联合体的所有成员共享同一块内存空间,修改一个成员会影响其他成员的值。
- 大小等于最大成员: 联合体的大小等于其最大成员的大小。
- 同一时间只能使用一个成员: 联合体在同一时间只能存储一个成员的值。
4.5.4 联合体使用
#include <iostream>
using namespace std;
union MyUnion {
int i;
float f;
char c;
};
int main() {
MyUnion u;
u.i = 10;
cout << "u.i = " << u.i << endl; // 10
u.f = 3.14;
cout << "u.f = " << u.f << endl; // 3.14
cout << "u.i = " << u.i << endl; // 不确定的值(内存被覆盖)
u.c = 'A';
cout << "u.c = " << u.c << endl; // A
cout << "u.i = " << u.i << endl; // 不确定的值(内存被覆盖)
return 0;
}
4.5.5 联合体与结构体
| 特性 | 联合体(Union) | 结构体(Struct) |
|---|---|---|
| 内存分配 | 所有成员共享同一块内存 | 每个成员有自己的内存空间 |
| 大小 | 等于最大成员的大小 | 等于所有成员大小之和(考虑对齐) |
| 使用场景 | 同一时间只使用一个成员 | 同时使用多个成员 |
4.5.6 匿名联合体
匿名联合体没有名称,可以直接访问其成员。示例
#include <iostream>
using namespace std;
struct MyStruct {
int type;
union {
int i;
float f;
char c;
};
};
int main() {
MyStruct s;
s.type = 1;
s.i = 10;
cout << "s.i = " << s.i << endl;
s.type = 2;
s.f = 3.14;
cout << "s.f = " << s.f << endl;
return 0;
}
4.5.7 联合体应用场景
- 节省内存: 当需要存储多种类型的数据但同一时间只使用其中一种时,可以使用联合体。
- 类型转换: 联合体可以用于将一种类型的数据解释为另一种类型。
- 硬件编程: 在嵌入式系统中,联合体常用于访问硬件寄存器或处理二进制数据。
4.5.8 注意事项
- 成员覆盖: 修改一个成员的值会覆盖其他成员的值,因此需要小心使用。
- 初始化: 联合体只能初始化第一个成员。
- 类型安全: 联合体不提供类型安全检查,容易导致错误。
4.6 枚举
枚举(Enumeration) 是一种用户定义的数据类型,用于表示一组命名的整数常量。枚举可以提高代码的可读性和可维护性,因为它允许使用有意义的名称代替硬编码的整数值。
C++ 支持两种枚举类型:
- 无作用域枚举(C-style enum)
- 有作用域枚举(C++11 引入的
enum class)
4.6.1 无作用域枚举
无作用域枚举是 C 语言风格的枚举,枚举常量在全局作用域中可见。语法
enum 枚举名 {
枚举常量1,
枚举常量2,
// ...
};
示例
#include <iostream>
using namespace std;
enum Color {
Red,
Green,
Blue
};
int main() {
Color c = Red;
cout << "c = " << c << endl; // 0
if (c == Red) {
cout << "The color is Red!" << endl;
}
return 0;
}
显式指定值
enum Color {
Red = 1,
Green = 2,
Blue = 4
};
4.6.2 有作用域枚举
C++11 引入了有作用域枚举(enum class),枚举常量在枚举的作用域内可见,避免了命名冲突。
语法
enum class 枚举名 {
枚举常量1,
枚举常量2,
// ...
};
示例
#include <iostream>
using namespace std;
enum class Color {
Red,
Green,
Blue
};
int main() {
Color c = Color::Red;
cout << "c = " << static_cast<int>(c) << endl; // 0
if (c == Color::Red) {
cout << "The color is Red!" << endl;
}
return 0;
}
4.6.3 枚举底层类型
默认情况下,枚举的底层类型是 int,但可以显式指定其他整数类型。示例
#include <iostream>
using namespace std;
enum class Color : char {
Red = 'R',
Green = 'G',
Blue = 'B'
};
int main() {
Color c = Color::Red;
cout << "c = " << static_cast<char>(c) << endl; // R
return 0;
}
4.6.4 枚举应用场景
- 状态表示:使用枚举表示程序的状态或模式。
enum class State { Idle, Running, Paused, Stopped }; - 选项标志:使用枚举表示选项或标志。
enum class Options { None = 0, Read = 1, Write = 2, Execute = 4 }; - 提高可读性:使用枚举代替硬编码的整数值,提高代码的可读性。
4.6.5 枚举注意事项
- 命名冲突: 无作用域枚举的常量在全局作用域中可见,可能导致命名冲突。
- 类型安全: 有作用域枚举更安全,不会隐式转换为整数。
- 底层类型: 可以显式指定枚举的底层类型以节省内存。
4.8 容器
4.8.1 vector
动态数组,支持随机访问。
#include <iostream>
#include <vector>
int main() {
std::vector<int> vec = {1, 2, 3, 4, 5};
vec.push_back(6); // 添加元素
for (int i : vec) {
std::cout << i << " "; // 输出: 1 2 3 4 5 6
}
return 0;
}
4.8.2 list
双向链表,支持高效插入和删除。
#include <iostream>
#include <list>
int main() {
std::list<int> lst = {1, 2, 3, 4, 5};
lst.push_front(0); // 在头部添加元素
for (int i : lst) {
std::cout << i << " "; // 输出: 0 1 2 3 4 5
}
return 0;
}
4.8.4 map
键值对容器,基于红黑树实现,按键排序。
std::map 的特点
- 键值对存储: 每个元素是一个
pair<const Key, Value>,其中Key是键,Value是值。 - 自动排序: 元素按照键的顺序自动排序(默认是升序)。
- 唯一键: 每个键在
std::map中只能出现一次。 - 高效查找: 基于红黑树实现,查找、插入和删除的时间复杂度为
O(log n)。
插入元素:使用 insert 方法或 [] 操作符插入元素。
mapName.insert(std::make_pair(key, value)); // 使用 insert
mapName[key] = value; // 使用 [] 操作符
访问元素:使用 [] 操作符或 at 方法访问元素。
ValueType value = mapName[key]; // 使用 [] 操作符
ValueType value = mapName.at(key); // 使用 at 方法
删除元素:使用 erase 方法删除元素。
mapName.erase(key); // 删除指定键的元素
查找元素:使用 find 方法查找元素。
auto it = mapName.find(key);
if (it != mapName.end()) {
// 找到元素
} else {
// 未找到元素
}
基本操作
#include <iostream>
#include <map>
using namespace std;
int main() {
// 定义 map
map<string, int> ageMap;
// 插入元素
ageMap["Alice"] = 25;
ageMap["Bob"] = 30;
ageMap.insert(make_pair("Charlie", 35));
// 访问元素
cout << "Alice's age: " << ageMap["Alice"] << endl;
cout << "Bob's age: " << ageMap.at("Bob") << endl;
// 查找元素
auto it = ageMap.find("Charlie");
if (it != ageMap.end()) {
cout << "Charlie's age: " << it->second << endl;
} else {
cout << "Charlie not found" << endl;
}
// 删除元素
ageMap.erase("Bob");
// 遍历 map
for (const auto& pair : ageMap) {
cout << "Key: " << pair.first << ", Value: " << pair.second << endl;
}
return 0;
}
4.8.4 unordered_map
基于哈希表的键值对容器,查找效率高。
#include <iostream>
#include <unordered_map>
int main() {
std::unordered_map<std::string, int> um;
um["Alice"] = 25;
um["Bob"] = 30;
for (const auto &pair : um) {
std::cout << pair.first << ": " << pair.second << std::endl;
}
return 0;
}
4.8.5 array
#include <iostream>
#include <array>
using namespace std;
int main() {
array<int, 5> arr = {1, 2, 3, 4, 5};
for (int i = 0; i < arr.size(); i++) {
cout << arr[i] << " ";
}
return 0;
}
4.9 练习题
4.9.1 数组冒泡排序
冒泡排序案例,作用: 最常用的排序算法,对数组内元素进行排序
- 比较相邻的元素。如果第一个比第二个大,就交换他们两个。
- 对每一对相邻元素做同样的工作,执行完毕后,找到第一个最大值。
- 重复以上的步骤,每次比较次数-1,直到不需要比较
示例: 将数组 { 4,2,8,0,5,7,1,3,9 } 进行升序排序
int main() {
int arr[9] = {4, 2, 8, 0, 5, 7, 1, 3, 9};
for (int i = 0; i < 9 - 1; i++) {
for (int j = 0; j < 9 - 1 - i; j++) {
if (arr[j] > arr[j + 1]) {
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
for (int i = 0; i < 9; i++) {
cout << arr[i] << endl;
}
return 0;
}
4.9.2 数组成绩统计
案例描述:有三名同学(张三,李四,王五),在一次考试中的成绩分别如下表,请分别输出三名同学的总成绩
| 语文 | 数学 | 英语 | |
|---|---|---|---|
| 张三 | 100 | 100 | 100 |
| 李四 | 90 | 50 | 100 |
| 王五 | 60 | 70 | 80 |
参考答案:
int main() {
int scores[3][3] =
{
{100, 100, 100},
{90, 50, 100},
{60, 70, 80},
};
string names[3] = {"张三", "李四", "王五"};
for (int i = 0; i < 3; i++) {
int sum = 0;
for (int j = 0; j < 3; j++) {
sum += scores[i][j];
}
cout << names[i] << "同学总成绩为: " << sum << endl;
}
return 0;
}