C++ 入门完全指南:从零开始掌握现代C++编程
#C++#编程入门#教程#现代C++
C++ 入门完全指南:从零开始掌握现代C++编程
欢迎来到C++的世界!作为一门强大而高效的编程语言,C++在系统编程、游戏开发、高性能计算等领域占据着重要地位。本教程将带你从零开始,逐步掌握现代C++编程的核心技能。
1. 为什么选择C++?
在开始学习之前,让我们了解一下C++的优势:
- 高性能:C++是编译型语言,执行效率极高
- 面向对象:支持完整的面向对象编程特性
- 底层控制:可以直接操作内存,适用于系统级编程
- 丰富的生态系统:拥有大量的库和框架
- 现代特性:C++11/14/17/20引入了许多现代化特性
2. 开发环境搭建
2.1 编译器安装
Windows用户:
hljs bash
# 推荐安装 MinGW-w64 或 Visual Studio
# 下载地址:https://www.mingw-w64.org/
macOS用户:
hljs bash
# 安装 Xcode Command Line Tools
xcode-select --install
# 或使用 Homebrew
brew install gcc
Linux用户:
hljs bash
# Ubuntu/Debian
sudo apt update
sudo apt install build-essential
# CentOS/RHEL
sudo yum groupinstall "Development Tools"
2.2 第一个C++程序
让我们从经典的"Hello, World!"开始:
hljs cpp
#include <iostream>
int main() {
std::cout << "Hello, World!" << std::endl;
return 0;
}
编译和运行:
hljs bash
g++ -o hello hello.cpp
./hello
3. 基础语法
3.1 变量和数据类型
hljs cpp
#include <iostream>
#include <string>
int main() {
// 基本数据类型
int age = 25; // 整数
double height = 175.5; // 双精度浮点数
float weight = 70.5f; // 单精度浮点数
char grade = 'A'; // 字符
bool isStudent = true; // 布尔值
std::string name = "张三"; // 字符串
// 自动类型推断(C++11)
auto score = 95.5; // 自动推断为double
auto level = 5; // 自动推断为int
// 输出变量
std::cout << "姓名: " << name << std::endl;
std::cout << "年龄: " << age << std::endl;
std::cout << "身高: " << height << "cm" << std::endl;
return 0;
}
3.2 常量
hljs cpp
#include <iostream>
int main() {
// const关键字定义常量
const double PI = 3.14159;
const int MAX_STUDENTS = 100;
// constexpr(C++11)编译时常量
constexpr int SECONDS_PER_MINUTE = 60;
std::cout << "圆周率: " << PI << std::endl;
std::cout << "每分钟秒数: " << SECONDS_PER_MINUTE << std::endl;
return 0;
}
4. 运算符
4.1 算术运算符
hljs cpp
#include <iostream>
int main() {
int a = 10, b = 3;
std::cout << "加法: " << a + b << std::endl; // 13
std::cout << "减法: " << a - b << std::endl; // 7
std::cout << "乘法: " << a * b << std::endl; // 30
std::cout << "除法: " << a / b << std::endl; // 3(整数除法)
std::cout << "取余: " << a % b << std::endl; // 1
// 浮点数除法
double c = 10.0, d = 3.0;
std::cout << "浮点除法: " << c / d << std::endl; // 3.33333
return 0;
}
4.2 比较和逻辑运算符
hljs cpp
#include <iostream>
int main() {
int score = 85;
// 比较运算符
std::cout << "score > 60: " << (score > 60) << std::endl; // true (1)
std::cout << "score == 85: " << (score == 85) << std::endl; // true (1)
std::cout << "score != 90: " << (score != 90) << std::endl; // true (1)
// 逻辑运算符
bool isPass = score >= 60;
bool isExcellent = score >= 90;
std::cout << "及格且优秀: " << (isPass && isExcellent) << std::endl; // false (0)
std::cout << "及格或优秀: " << (isPass || isExcellent) << std::endl; // true (1)
std::cout << "不是优秀: " << (!isExcellent) << std::endl; // true (1)
return 0;
}
5. 控制流程
5.1 条件语句
hljs cpp
#include <iostream>
int main() {
int score;
std::cout << "请输入成绩: ";
std::cin >> score;
if (score >= 90) {
std::cout << "优秀!" << std::endl;
} else if (score >= 80) {
std::cout << "良好!" << std::endl;
} else if (score >= 60) {
std::cout << "及格!" << std::endl;
} else {
std::cout << "不及格!" << std::endl;
}
// 三元运算符
std::string result = (score >= 60) ? "通过" : "不通过";
std::cout << "考试结果: " << result << std::endl;
return 0;
}
5.2 循环语句
hljs cpp
#include <iostream>
int main() {
// for循环
std::cout << "for循环输出1-5:" << std::endl;
for (int i = 1; i <= 5; i++) {
std::cout << i << " ";
}
std::cout << std::endl;
// while循环
std::cout << "while循环倒计时:" << std::endl;
int count = 5;
while (count > 0) {
std::cout << count << " ";
count--;
}
std::cout << "发射!" << std::endl;
// do-while循环
std::cout << "do-while循环示例:" << std::endl;
int number;
do {
std::cout << "请输入一个正数(输入0退出): ";
std::cin >> number;
} while (number != 0);
// 范围for循环(C++11)
std::cout << "范围for循环:" << std::endl;
int numbers[] = {1, 2, 3, 4, 5};
for (int num : numbers) {
std::cout << num << " ";
}
std::cout << std::endl;
return 0;
}
6. 函数
6.1 基础函数
hljs cpp
#include <iostream>
#include <string>
// 函数声明
int add(int a, int b);
double calculateAverage(double arr[], int size);
void printMessage(const std::string& message);
int main() {
// 调用函数
int result = add(10, 20);
std::cout << "10 + 20 = " << result << std::endl;
double scores[] = {85.5, 90.0, 78.5, 92.0};
double avg = calculateAverage(scores, 4);
std::cout << "平均分: " << avg << std::endl;
printMessage("学习C++很有趣!");
return 0;
}
// 函数定义
int add(int a, int b) {
return a + b;
}
double calculateAverage(double arr[], int size) {
double sum = 0.0;
for (int i = 0; i < size; i++) {
sum += arr[i];
}
return sum / size;
}
void printMessage(const std::string& message) {
std::cout << "消息: " << message << std::endl;
}
6.2 函数重载
hljs cpp
#include <iostream>
// 函数重载 - 相同函数名,不同参数
int multiply(int a, int b) {
std::cout << "调用int版本" << std::endl;
return a * b;
}
double multiply(double a, double b) {
std::cout << "调用double版本" << std::endl;
return a * b;
}
int multiply(int a, int b, int c) {
std::cout << "调用三参数版本" << std::endl;
return a * b * c;
}
int main() {
std::cout << "5 * 3 = " << multiply(5, 3) << std::endl;
std::cout << "2.5 * 4.0 = " << multiply(2.5, 4.0) << std::endl;
std::cout << "2 * 3 * 4 = " << multiply(2, 3, 4) << std::endl;
return 0;
}
7. 数组和字符串
7.1 数组操作
hljs cpp
#include <iostream>
#include <array>
int main() {
// 传统数组
int scores[5] = {85, 90, 78, 92, 88};
// 访问数组元素
std::cout << "第一个成绩: " << scores[0] << std::endl;
std::cout << "第三个成绩: " << scores[2] << std::endl;
// 遍历数组
std::cout << "所有成绩: ";
for (int i = 0; i < 5; i++) {
std::cout << scores[i] << " ";
}
std::cout << std::endl;
// 现代C++数组(C++11)
std::array<int, 5> modernScores = {85, 90, 78, 92, 88};
// 范围for循环遍历
std::cout << "现代遍历方式: ";
for (int score : modernScores) {
std::cout << score << " ";
}
std::cout << std::endl;
return 0;
}
7.2 字符串操作
hljs cpp
#include <iostream>
#include <string>
#include <vector>
int main() {
// 字符串创建和初始化
std::string name = "张三";
std::string greeting = "Hello, " + name + "!";
std::cout << greeting << std::endl;
// 字符串属性
std::cout << "字符串长度: " << greeting.length() << std::endl;
std::cout << "字符串大小: " << greeting.size() << std::endl;
// 字符串操作
std::string text = "C++编程很有趣";
std::cout << "原字符串: " << text << std::endl;
// 查找子串
size_t pos = text.find("编程");
if (pos != std::string::npos) {
std::cout << "找到'编程'在位置: " << pos << std::endl;
}
// 替换
text.replace(pos, 4, "开发");
std::cout << "替换后: " << text << std::endl;
// 字符串分割(简单示例)
std::string data = "苹果,香蕉,橙子";
std::vector<std::string> fruits;
size_t start = 0;
size_t end = data.find(',');
while (end != std::string::npos) {
fruits.push_back(data.substr(start, end - start));
start = end + 1;
end = data.find(',', start);
}
fruits.push_back(data.substr(start));
std::cout << "水果列表: ";
for (const std::string& fruit : fruits) {
std::cout << fruit << " ";
}
std::cout << std::endl;
return 0;
}
8. 指针和引用
8.1 指针基础
hljs cpp
#include <iostream>
int main() {
int number = 42;
int* ptr = &number; // ptr指向number的地址
std::cout << "变量值: " << number << std::endl;
std::cout << "变量地址: " << &number << std::endl;
std::cout << "指针存储的地址: " << ptr << std::endl;
std::cout << "通过指针访问值: " << *ptr << std::endl;
std::cout << "指针本身的地址: " << &ptr << std::endl;
// 通过指针修改值
*ptr = 100;
std::cout << "修改后number的值: " << number << std::endl;
// 空指针
int* nullPtr = nullptr;
if (nullPtr == nullptr) {
std::cout << "这是一个空指针" << std::endl;
}
return 0;
}
8.2 引用
hljs cpp
#include <iostream>
// 使用引用作为参数
void swapValues(int& a, int& b) {
int temp = a;
a = b;
b = temp;
}
// 返回引用
int& getMax(int& a, int& b) {
return (a > b) ? a : b;
}
int main() {
int x = 10, y = 20;
std::cout << "交换前: x = " << x << ", y = " << y << std::endl;
swapValues(x, y);
std::cout << "交换后: x = " << x << ", y = " << y << std::endl;
// 引用示例
int num = 50;
int& ref = num; // ref是num的别名
ref = 60; // 修改ref就是修改num
std::cout << "num = " << num << ", ref = " << ref << std::endl;
// 返回引用示例
getMax(x, y) = 100; // 将较大的值设为100
std::cout << "操作后: x = " << x << ", y = " << y << std::endl;
return 0;
}
9. 面向对象编程
9.1 类和对象
hljs cpp
#include <iostream>
#include <string>
class Student {
private:
std::string name;
int age;
double score;
public:
// 构造函数
Student(const std::string& n, int a, double s)
: name(n), age(a), score(s) {
std::cout << "创建学生对象: " << name << std::endl;
}
// 默认构造函数
Student() : name("未知"), age(0), score(0.0) {}
// 析构函数
~Student() {
std::cout << "销毁学生对象: " << name << std::endl;
}
// 成员函数
void displayInfo() const {
std::cout << "姓名: " << name
<< ", 年龄: " << age
<< ", 成绩: " << score << std::endl;
}
// Getter和Setter
std::string getName() const { return name; }
void setName(const std::string& n) { name = n; }
int getAge() const { return age; }
void setAge(int a) { age = a; }
double getScore() const { return score; }
void setScore(double s) { score = s; }
// 静态成员函数
static std::string getSchoolName() {
return "示例中学";
}
};
int main() {
// 创建对象
Student student1("张三", 18, 95.5);
Student student2("李四", 17, 88.0);
// 访问成员函数
student1.displayInfo();
student2.displayInfo();
// 使用setter修改属性
student2.setScore(92.5);
std::cout << "李四修改后的成绩: " << student2.getScore() << std::endl;
// 静态函数调用
std::cout << "学校名称: " << Student::getSchoolName() << std::endl;
return 0;
}
9.2 继承和多态
hljs cpp
#include <iostream>
#include <string>
#include <memory>
// 基类
class Shape {
protected:
std::string color;
public:
Shape(const std::string& c) : color(c) {}
virtual ~Shape() {}
// 虚函数
virtual double getArea() const = 0; // 纯虚函数
virtual void draw() const {
std::cout << "绘制一个" << color << "的图形" << std::endl;
}
std::string getColor() const { return color; }
};
// 派生类:圆形
class Circle : public Shape {
private:
double radius;
public:
Circle(const std::string& c, double r)
: Shape(c), radius(r) {}
double getArea() const override {
return 3.14159 * radius * radius;
}
void draw() const override {
std::cout << "绘制一个" << color << "的圆形,半径: "
<< radius << std::endl;
}
};
// 派生类:矩形
class Rectangle : public Shape {
private:
double width, height;
public:
Rectangle(const std::string& c, double w, double h)
: Shape(c), width(w), height(h) {}
double getArea() const override {
return width * height;
}
void draw() const override {
std::cout << "绘制一个" << color << "的矩形,宽: "
<< width << ", 高: " << height << std::endl;
}
};
int main() {
// 使用智能指针(现代C++)
std::unique_ptr<Shape> circle = std::make_unique<Circle>("红色", 5.0);
std::unique_ptr<Shape> rectangle = std::make_unique<Rectangle>("蓝色", 4.0, 6.0);
// 多态调用
circle->draw();
std::cout << "圆形面积: " << circle->getArea() << std::endl;
rectangle->draw();
std::cout << "矩形面积: " << rectangle->getArea() << std::endl;
return 0;
}
10. 模板编程
10.1 函数模板
hljs cpp
#include <iostream>
// 函数模板 - 比较两个值
template <typename T>
T getMax(T a, T b) {
return (a > b) ? a : b;
}
// 函数模板 - 交换两个值
template <typename T>
void swapValues(T& a, T& b) {
T temp = a;
a = b;
b = temp;
}
// 模板特化
template <>
const char* getMax<const char*>(const char* a, const char* b) {
return (strcmp(a, b) > 0) ? a : b;
}
int main() {
// 使用函数模板
std::cout << "最大值: " << getMax(10, 20) << std::endl;
std::cout << "最大值: " << getMax(3.14, 2.71) << std::endl;
std::cout << "最大值: " << getMax('A', 'B') << std::endl;
// 字符串比较
const char* str1 = "apple";
const char* str2 = "banana";
std::cout << "字符串最大值: " << getMax(str1, str2) << std::endl;
// 交换值
int x = 10, y = 20;
std::cout << "交换前: x = " << x << ", y = " << y << std::endl;
swapValues(x, y);
std::cout << "交换后: x = " << x << ", y = " << y << std::endl;
return 0;
}
10.2 类模板
hljs cpp
#include <iostream>
#include <stdexcept>
// 类模板:动态数组
template <typename T>
class DynamicArray {
private:
T* data;
size_t capacity;
size_t size;
void resize() {
capacity *= 2;
T* newData = new T[capacity];
for (size_t i = 0; i < size; i++) {
newData[i] = data[i];
}
delete[] data;
data = newData;
}
public:
DynamicArray(size_t initialCapacity = 10)
: capacity(initialCapacity), size(0) {
data = new T[capacity];
}
~DynamicArray() {
delete[] data;
}
void push(const T& value) {
if (size >= capacity) {
resize();
}
data[size++] = value;
}
T& operator[](size_t index) {
if (index >= size) {
throw std::out_of_range("索引超出范围");
}
return data[index];
}
const T& operator[](size_t index) const {
if (index >= size) {
throw std::out_of_range("索引超出范围");
}
return data[index];
}
size_t getSize() const { return size; }
void print() const {
std::cout << "[";
for (size_t i = 0; i < size; i++) {
std::cout << data[i];
if (i < size - 1) std::cout << ", ";
}
std::cout << "]" << std::endl;
}
};
int main() {
// 整数数组
DynamicArray<int> intArray;
intArray.push(10);
intArray.push(20);
intArray.push(30);
std::cout << "整数数组: ";
intArray.print();
std::cout << "第二个元素: " << intArray[1] << std::endl;
// 字符串数组
DynamicArray<std::string> stringArray(5);
stringArray.push("苹果");
stringArray.push("香蕉");
stringArray.push("橙子");
std::cout << "字符串数组: ";
stringArray.print();
std::cout << "数组大小: " << stringArray.getSize() << std::endl;
return 0;
}
11. 异常处理
hljs cpp
#include <iostream>
#include <stdexcept>
#include <string>
class BankAccount {
private:
std::string accountNumber;
double balance;
public:
BankAccount(const std::string& accNum, double initialBalance = 0.0)
: accountNumber(accNum), balance(initialBalance) {}
void deposit(double amount) {
if (amount <= 0) {
throw std::invalid_argument("存款金额必须大于0");
}
balance += amount;
std::cout << "存款成功,当前余额: " << balance << std::endl;
}
void withdraw(double amount) {
if (amount <= 0) {
throw std::invalid_argument("取款金额必须大于0");
}
if (amount > balance) {
throw std::runtime_error("余额不足");
}
balance -= amount;
std::cout << "取款成功,当前余额: " << balance << std::endl;
}
double getBalance() const { return balance; }
std::string getAccountNumber() const { return accountNumber; }
};
int main() {
try {
BankAccount account("123456789", 1000.0);
// 正常操作
account.deposit(500.0);
account.withdraw(200.0);
// 异常测试
try {
account.withdraw(2000.0); // 会抛出异常
} catch (const std::runtime_error& e) {
std::cout << "取款错误: " << e.what() << std::endl;
}
try {
account.deposit(-100.0); // 会抛出异常
} catch (const std::invalid_argument& e) {
std::cout << "存款错误: " << e.what() << std::endl;
}
} catch (const std::exception& e) {
std::cout << "发生未知错误: " << e.what() << std::endl;
}
return 0;
}
12. STL标准模板库
12.1 容器
hljs cpp
#include <iostream>
#include <vector>
#include <list>
#include <map>
#include <set>
#include <stack>
#include <queue>
#include <algorithm>
int main() {
// vector - 动态数组
std::vector<int> vec = {1, 2, 3, 4, 5};
vec.push_back(6);
vec.insert(vec.begin() + 2, 99);
std::cout << "Vector: ";
for (int num : vec) {
std::cout << num << " ";
}
std::cout << std::endl;
// list - 双向链表
std::list<std::string> names = {"张三", "李四", "王五"};
names.push_front("赵六");
names.push_back("钱七");
std::cout << "List: ";
for (const std::string& name : names) {
std::cout << name << " ";
}
std::cout << std::endl;
// map - 键值对容器
std::map<std::string, int> scores;
scores["数学"] = 95;
scores["语文"] = 88;
scores["英语"] = 92;
std::cout << "成绩单:" << std::endl;
for (const auto& pair : scores) {
std::cout << pair.first << ": " << pair.second << std::endl;
}
// set - 集合容器
std::set<int> uniqueNumbers = {3, 1, 4, 1, 5, 9, 2, 6, 5};
std::cout << "Set去重后: ";
for (int num : uniqueNumbers) {
std::cout << num << " ";
}
std::cout << std::endl;
// stack - 栈
std::stack<std::string> stack;
stack.push("第一");
stack.push("第二");
stack.push("第三");
std::cout << "Stack出栈顺序: ";
while (!stack.empty()) {
std::cout << stack.top() << " ";
stack.pop();
}
std::cout << std::endl;
// queue - 队列
std::queue<int> queue;
queue.push(1);
queue.push(2);
queue.push(3);
std::cout << "Queue出队顺序: ";
while (!queue.empty()) {
std::cout << queue.front() << " ";
queue.pop();
}
std::cout << std::endl;
return 0;
}
12.2 算法
hljs cpp
#include <iostream>
#include <vector>
#include <algorithm>
#include <numeric>
int main() {
std::vector<int> numbers = {5, 2, 8, 1, 9, 3, 7, 4, 6};
// 排序
std::sort(numbers.begin(), numbers.end());
std::cout << "排序后: ";
for (int num : numbers) {
std::cout << num << " ";
}
std::cout << std::endl;
// 反向排序
std::sort(numbers.rbegin(), numbers.rend());
std::cout << "反向排序: ";
for (int num : numbers) {
std::cout << num << " ";
}
std::cout << std::endl;
// 查找
int target = 5;
auto it = std::find(numbers.begin(), numbers.end(), target);
if (it != numbers.end()) {
std::cout << "找到 " << target << " 在位置: "
<< std::distance(numbers.begin(), it) << std::endl;
}
// 二分查找(需要先排序)
std::sort(numbers.begin(), numbers.end());
bool found = std::binary_search(numbers.begin(), numbers.end(), 5);
std::cout << "二分查找5: " << (found ? "找到" : "未找到") << std::endl;
// 计算总和
int sum = std::accumulate(numbers.begin(), numbers.end(), 0);
std::cout << "总和: " << sum << std::endl;
// 最大值和最小值
auto [minIt, maxIt] = std::minmax_element(numbers.begin(), numbers.end());
std::cout << "最小值: " << *minIt << ", 最大值: " << *maxIt << std::endl;
// 计数
int count = std::count(numbers.begin(), numbers.end(), 5);
std::cout << "5的个数: " << count << std::endl;
// 自定义排序
std::vector<std::string> words = {"apple", "banana", "cherry", "date"};
std::sort(words.begin(), words.end(), [](const std::string& a, const std::string& b) {
return a.length() < b.length(); // 按长度排序
});
std::cout << "按长度排序: ";
for (const std::string& word : words) {
std::cout << word << " ";
}
std::cout << std::endl;
return 0;
}
13. 文件操作
hljs cpp
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
void writeToFile(const std::string& filename) {
std::ofstream outFile(filename);
if (!outFile) {
std::cout << "无法打开文件进行写入: " << filename << std::endl;
return;
}
outFile << "学生成绩管理系统\n";
outFile << "==================\n";
outFile << "张三,95,85,90\n";
outFile << "李四,88,92,87\n";
outFile << "王五,76,89,82\n";
outFile.close();
std::cout << "数据已写入文件: " << filename << std::endl;
}
void readFromFile(const std::string& filename) {
std::ifstream inFile(filename);
if (!inFile) {
std::cout << "无法打开文件进行读取: " << filename << std::endl;
return;
}
std::string line;
std::cout << "\n文件内容:\n";
while (std::getline(inFile, line)) {
std::cout << line << std::endl;
}
inFile.close();
}
void processStudentData(const std::string& filename) {
std::ifstream inFile(filename);
if (!inFile) {
std::cout << "无法打开文件: " << filename << std::endl;
return;
}
std::string line;
std::vector<std::string> students;
// 跳过标题行
std::getline(inFile, line);
std::getline(inFile, line);
while (std::getline(inFile, line)) {
students.push_back(line);
}
inFile.close();
std::cout << "\n学生成绩分析:\n";
for (const std::string& student : students) {
// 简单解析CSV格式
size_t pos1 = student.find(',');
size_t pos2 = student.find(',', pos1 + 1);
size_t pos3 = student.find(',', pos2 + 1);
if (pos1 != std::string::npos && pos2 != std::string::npos && pos3 != std::string::npos) {
std::string name = student.substr(0, pos1);
int math = std::stoi(student.substr(pos1 + 1, pos2 - pos1 - 1));
int english = std::stoi(student.substr(pos2 + 1, pos3 - pos2 - 1));
int science = std::stoi(student.substr(pos3 + 1));
double average = (math + english + science) / 3.0;
std::cout << name << ": 数学=" << math
<< ", 英语=" << english
<< ", 科学=" << science
<< ", 平均分=" << average << std::endl;
}
}
}
int main() {
const std::string filename = "students.txt";
// 写入文件
writeToFile(filename);
// 读取文件
readFromFile(filename);
// 处理数据
processStudentData(filename);
return 0;
}
14. 学习建议和进阶路径
14.1 学习建议
- 循序渐进:从基础语法开始,不要急于求成
- 多写代码:理论结合实践,多写小程序巩固知识
- 阅读源码:学习优秀的开源项目代码
- 参与项目:实际项目开发是最好的学习方式
- 持续学习:关注C++新标准和最佳实践
14.2 推荐资源
-
书籍:
- 《C++ Primer》- 经典入门教材
- 《Effective C++》- 最佳实践指南
- 《Modern C++》- 现代C++特性
-
在线资源:
- cppreference.com - 权威参考文档
- learncpp.com - 在线教程
- GitHub上的开源项目
14.3 进阶主题
学完基础后,可以进一步学习:
- 多线程编程
- 网络编程
- 图形界面开发
- 游戏开发
- 系统编程
15. 总结
C++是一门功能强大且应用广泛的编程语言。通过本教程,你已经学习了:
- C++基础语法和概念
- 面向对象编程思想
- 模板编程技术
- STL标准库的使用
- 文件操作和异常处理
记住,编程是一项需要持续练习的技能。继续保持学习的热情,多写代码,多解决问题,你一定能成为一名优秀的C++程序员!
祝你学习愉快! 🚀
如果你在学习过程中遇到问题,欢迎在评论区留言讨论!