引言:为什么选择C++?
在当今的编程世界中,C++依然占据着不可替代的重要地位。作为一门诞生于1985年的编程语言,C++经历了近40年的发展和演进,不仅没有被淘汰,反而在系统编程、游戏开发、高性能计算、嵌入式系统等领域持续发挥着关键作用。对于零基础的编程学习者来说,选择C++作为入门语言可能看起来有些挑战,但这种选择将为你打下坚实的编程基础,让你深入理解计算机底层工作原理。
C++的强大之处在于它同时具备高级语言的抽象能力和低级语言的控制能力。你可以用它编写简洁优雅的代码,也可以直接操作内存和硬件资源。这种灵活性使得C++成为学习其他编程语言的绝佳跳板——一旦你掌握了C++的核心概念,学习Python、Java、C#等语言将变得轻而易举。
更重要的是,C++社区活跃,生态系统成熟,就业前景广阔。从操作系统内核到大型游戏引擎,从金融交易系统到人工智能框架,C++无处不在。掌握C++不仅意味着掌握一门编程语言,更意味着获得了解决复杂工程问题的能力。
本教程专为零基础读者设计,我们将从最基础的概念开始,循序渐进地引导你掌握C++编程的核心技能。无论你是为了学术研究、职业发展,还是纯粹的兴趣爱好,这篇教程都将为你提供扎实的学习路径。
第一章:搭建C++开发环境
在开始编写C++代码之前,我们需要先搭建一个合适的开发环境。对于初学者来说,选择简单易用的工具非常重要,这样可以让我们专注于学习编程本身,而不是被复杂的配置问题困扰。
1.1 选择合适的编译器
C++代码需要通过编译器转换成计算机能够执行的机器码。目前主流的C++编译器有:
GCC (GNU Compiler Collection):这是最流行的开源C++编译器,支持所有主流操作系统。在Linux系统中通常预装,在Windows上可以通过MinGW或Cygwin安装。
Clang:由LLVM项目开发的现代编译器,以其快速的编译速度和优秀的错误提示而闻名。
Microsoft Visual C++:Windows平台上的官方编译器,集成在Visual Studio中。
对于初学者,我推荐以下选择:
- Windows用户:安装Visual Studio Community(免费版本)
- macOS用户:安装Xcode命令行工具
- Linux用户:通常已预装GCC,如未安装可使用包管理器安装
1.2 安装Visual Studio Community(Windows)

- 访问Visual Studio官网(https://visualstudio.microsoft.com/)
- 下载Visual Studio Community版本(免费)
- 运行安装程序,在工作负载中选择"使用C++的桌面开发"
- 点击安装,等待完成
安装完成后,启动Visual Studio,选择"创建新项目",然后选择"C++控制台应用程序"。
1.3 安装 Clion(Linux)
https://www.jetbrains.com/clion/

现在Clion授权个人学习用途是免费的。
1.4 验证安装
让我们编写第一个C++程序来验证环境是否正确安装:
#include <iostream>
int main() {
std::cout << "Hello, World!" << std::endl;
return 0;
}
将这段代码保存为hello.cpp,然后编译运行。如果看到输出"Hello, World!",说明你的开发环境已经准备就绪!

1.5 在线编译器选项
如果你暂时不想在本地安装开发环境,也可以使用在线C++编译器进行学习:

第二章:C++程序的基本结构
理解C++程序的基本结构是学习编程的第一步。每个C++程序都遵循特定的组织方式,掌握这些基本概念将帮助你更好地理解代码的执行流程。
2.1 最简单的C++程序
让我们从最简单的程序开始:
int main() {
return 0;
}
这个程序虽然什么也不做,但它是一个完整的、可执行的C++程序。让我们逐行分析:
int main():这是程序的入口点。每个C++程序都必须有一个main函数,程序从这里开始执行。{:函数体的开始return 0;:返回值为0,表示程序正常结束}:函数体的结束
2.2 理解 #include 指令
在之前的"Hello, World!"程序中,我们使用了#include <iostream>。这是一个预处理指令,告诉编译器在编译之前包含标准输入输出流库。
C++标准库提供了许多有用的工具和功能,通过#include指令可以引入这些功能。常见的头文件包括:
<iostream>:输入输出流<string>:字符串处理<vector>:动态数组<cmath>:数学函数
2.3 命名空间(namespace)
在"Hello, World!"程序中,我们使用了std::cout。这里的std是标准命名空间,cout是标准输出流对象。
命名空间用于避免名称冲突。想象一下,如果你的程序和标准库都定义了一个叫print的函数,编译器就不知道该使用哪个。通过命名空间,我们可以明确指定使用哪个版本。
为了简化代码,我们经常在程序开头使用:
using namespace std;
这样就可以直接使用cout而不是std::cout。但对于初学者,建议先使用完整的std::前缀,这样能更清楚地理解代码的来源。
2.4 注释的重要性
良好的注释习惯从第一天开始培养。C++支持两种注释方式:
// 这是单行注释
/*
这是多行注释
可以跨越多行
*/
注释不会影响程序的执行,但能让代码更容易理解和维护。记住:写代码是为了让计算机执行,但写注释是为了让人类理解。
2.5 程序执行流程
C++程序严格按照从上到下的顺序执行(除了函数调用和控制结构)。理解执行流程对于调试和理解程序行为至关重要。
#include <iostream>
int main() {
std::cout << "第一步" << std::endl;
std::cout << "第二步" << std::endl;
std::cout << "第三步" << std::endl;
return 0;
}
第一步
第二步
第三步

这个程序会按顺序输出三行文字,展示了程序的基本执行流程。
第三章:变量与数据类型
变量是编程的基础概念,它们用于存储和操作数据。理解C++的数据类型系统对于编写正确的程序至关重要。
3.1 什么是变量?
变量可以想象成一个有名字的容器,用来存储数据。每个变量都有:
- 名称:用于引用变量
- 类型:决定变量能存储什么类型的数据
- 值:变量当前存储的数据
3.2 基本数据类型
C++提供了几种基本的数据类型:
整数类型
int:整数,通常为32位(-2,147,483,648 到 2,147,483,647)short:短整数,通常为16位long:长整数,通常为32位或64位long long:更长的整数,通常为64位
浮点数类型
float:单精度浮点数,约7位有效数字double:双精度浮点数,约15位有效数字long double:扩展精度浮点数
字符类型
char:单个字符,8位wchar_t:宽字符,用于国际化
布尔类型
bool:布尔值,只有true和false两个值
3.3 变量声明与初始化
在C++中使用变量前必须先声明:
#include <iostream>
int main() {
// 声明变量
int age;
double price;
char grade;
bool isStudent;
// 初始化变量
age = 25;
price = 19.99;
grade = 'A';
isStudent = true;
// 或者声明时直接初始化
int year = 2024;
double pi = 3.14159;
std::cout << "年龄: " << age << std::endl;
std::cout << "价格: " << price << std::endl;
std::cout << "成绩: " << grade << std::endl;
std::cout << "是学生: " << isStudent << std::endl;
return 0;
}
年龄: 25
价格: 19.99
成绩: A
是学生: 1
3.4 常量(const)
有时我们需要定义不会改变的值,这时可以使用const关键字:
const double PI = 3.14159;
const int MAX_STUDENTS = 100;
尝试修改常量的值会导致编译错误,这有助于防止意外的修改。
3.5 自动类型推导(auto)
C++11引入了auto关键字,让编译器自动推导变量类型:
auto x = 42; // x 是 int
auto y = 3.14; // y 是 double
auto name = "John"; // name 是 const char*
这对于复杂类型特别有用,但初学者建议先明确指定类型,以更好地理解类型系统。
3.6 变量命名规则
C++对变量命名有以下规则:
- 必须以字母或下划线开头
- 只能包含字母、数字和下划线
- 不能使用关键字(如int、double等)
- 区分大小写
良好的命名习惯:
- 使用有意义的名称(如
studentCount而不是x) - 采用驼峰命名法(
firstName)或下划线命名法(first_name) - 避免单字母变量名(除了循环计数器)
3.7 作用域(Scope)
变量的作用域决定了它在程序中的可见范围:
#include <iostream>
int globalVar = 10; // 全局变量
int main() {
int localVar = 20; // 局部变量
{
int innerVar = 30; // 块作用域变量
std::cout << innerVar << std::endl; // 可以访问
}
// std::cout << innerVar << std::endl; // 错误!innerVar超出作用域
std::cout << localVar << std::endl; // 可以访问
std::cout << globalVar << std::endl; // 可以访问
return 0;
}
30
20
10
理解作用域对于避免变量冲突和内存管理非常重要。
第四章:运算符与表达式
运算符是用于执行特定操作的符号,表达式是由变量、常量和运算符组成的计算单元。掌握运算符是进行数据处理的基础。
4.1 算术运算符
C++支持基本的算术运算:
#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.333...
return 0;
}
加法: 13
减法: 7
乘法: 30
除法: 3
取余: 1
浮点除法: 3.33333
4.2 赋值运算符
赋值运算符用于给变量赋值:
int x = 5; // 基本赋值
x += 3; // 等价于 x = x + 3
x -= 2; // 等价于 x = x - 2
x *= 4; // 等价于 x = x * 4
x /= 2; // 等价于 x = x / 2
x %= 3; // 等价于 x = x % 3
4.3 比较运算符
比较运算符用于比较两个值,返回布尔结果:
#include <iostream>
int main() {
int a = 5, b = 10;
std::cout << (a == b) << std::endl; // 0 (false)
std::cout << (a != b) << std::endl; // 1 (true)
std::cout << (a < b) << std::endl; // 1 (true)
std::cout << (a > b) << std::endl; // 0 (false)
std::cout << (a <= b) << std::endl; // 1 (true)
std::cout << (a >= b) << std::endl; // 0 (false)
return 0;
}
0
1
1
0
1
0
4.4 逻辑运算符
逻辑运算符用于组合多个条件:
#include <iostream>
int main() {
bool sunny = true;
bool weekend = false;
std::cout << (sunny && weekend) << std::endl; // 0 (false) - 与
std::cout << (sunny || weekend) << std::endl; // 1 (true) - 或
std::cout << (!sunny) << std::endl; // 0 (false) - 非
return 0;
}
0
1
0
4.5 递增和递减运算符
这些运算符用于将变量值增加或减少1:
#include <iostream>
int main() {
int x = 5;
std::cout << x++ << std::endl; // 输出5,然后x变为6
std::cout << ++x << std::endl; // x先变为7,然后输出7
std::cout << x-- << std::endl; // 输出7,然后x变为6
std::cout << --x << std::endl; // x先变为5,然后输出5
return 0;
}
5
7
7
5
前缀形式(++x)先递增再使用,后缀形式(x++)先使用再递增。
4.6 运算符优先级
当表达式包含多个运算符时,优先级决定计算顺序:
#include <iostream>
int main() {
int result = 2 + 3 * 4; // 14,因为*优先级高于+
int result2 = (2 + 3) * 4; // 20,括号改变优先级
std::cout << result << std::endl;
std::cout << result2 << std::endl;
return 0;
}
14
20
常见的优先级顺序(从高到低):
- 括号
() - 递增递减
++ -- - 算术运算符
* / %然后+ - - 比较运算符
< <= > >=然后== != - 逻辑运算符
&&然后|| - 赋值运算符
= += -=等
4.7 类型转换
C++支持显式和隐式类型转换:
#include <iostream>
int main() {
// 隐式转换(自动)
int a = 10;
double b = a; // int自动转换为double
// 显式转换(强制)
double c = 3.14;
int d = (int)c; // C风格转换
int e = static_cast<int>(c); // C++风格转换(推荐)
std::cout << "b = " << b << std::endl; // 10
std::cout << "d = " << d << std::endl; // 3
std::cout << "e = " << e << std::endl; // 3
return 0;
}
b = 10
d = 3
e = 3
C++风格的static_cast更安全,因为它在编译时进行类型检查。
第五章:控制流程
控制流程语句决定了程序的执行路径。通过条件语句和循环语句,我们可以编写更加灵活和智能的程序。
5.1 if语句
if语句用于根据条件执行不同的代码块:
#include <iostream>
int main() {
int age;
std::cout << "请输入您的年龄: ";
std::cin >> age;
if (age >= 18) {
std::cout << "您已成年,可以投票。" << std::endl;
} else {
std::cout << "您还未成年。" << std::endl;
}
return 0;
}
请输入您的年龄: 25
您已成年,可以投票。
5.2 if-else if-else结构
当有多个条件需要检查时,可以使用else if:
#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 >= 70) {
std::cout << "中等" << std::endl;
} else if (score >= 60) {
std::cout << "及格" << std::endl;
} else {
std::cout << "不及格" << std::endl;
}
return 0;
}
请输入考试分数: 100
优秀
5.3 switch语句
当需要根据一个变量的多个可能值执行不同操作时,switch语句更清晰:
#include <iostream>
int main() {
char grade;
std::cout << "请输入成绩等级 (A, B, C, D, F): ";
std::cin >> grade;
switch (grade) {
case 'A':
std::cout << "优秀" << std::endl;
break;
case 'B':
std::cout << "良好" << std::endl;
break;
case 'C':
std::cout << "中等" << std::endl;
break;
case 'D':
std::cout << "及格" << std::endl;
break;
case 'F':
std::cout << "不及格" << std::endl;
break;
default:
std::cout << "无效的成绩等级" << std::endl;
}
return 0;
}
请输入成绩等级 (A, B, C, D, F): A
优秀
注意:每个case后面通常需要break语句,否则会继续执行下一个case(称为"fall-through")。
5.4 while循环
while循环在条件为真时重复执行代码块:
#include <iostream>
int main() {
int count = 1;
while (count <= 5) {
std::cout << "计数: " << count << std::endl;
count++;
}
return 0;
}
计数: 1
计数: 2
计数: 3
计数: 4
计数: 5
5.5 do-while循环
do-while循环至少执行一次,然后检查条件:
#include <iostream>
int main() {
int number;
do {
std::cout << "请输入一个正数: ";
std::cin >> number;
} while (number <= 0);
std::cout << "您输入的正数是: " << number << std::endl;
return 0;
}
请输入一个正数: 10
您输入的正数是: 10
5.6 for循环
for循环是处理已知次数循环的最佳选择:
#include <iostream>
int main() {
// 基本for循环
for (int i = 1; i <= 5; i++) {
std::cout << "第 " << i << " 次循环" << std::endl;
}
// 计算1到100的和
int sum = 0;
for (int i = 1; i <= 100; i++) {
sum += i;
}
std::cout << "1到100的和是: " << sum << std::endl;
return 0;
}
第 1 次循环
第 2 次循环
第 3 次循环
第 4 次循环
第 5 次循环
1到100的和是: 5050
5.7 循环控制语句
break和continue用于控制循环的执行:
#include <iostream>
int main() {
// 使用break提前退出循环
for (int i = 1; i <= 10; i++) {
if (i == 5) {
break; // 当i等于5时退出循环
}
std::cout << i << " ";
}
std::cout << std::endl; // 输出: 1 2 3 4
// 使用continue跳过当前迭代
for (int i = 1; i <= 10; i++) {
if (i % 2 == 0) {
continue; // 跳过偶数
}
std::cout << i << " ";
}
std::cout << std::endl; // 输出: 1 3 5 7 9
return 0;
}
1 2 3 4
1 3 5 7 9
5.8 嵌套控制结构
控制结构可以相互嵌套,创建复杂的逻辑:
#include <iostream>
int main() {
// 打印乘法表
for (int i = 1; i <= 9; i++) {
for (int j = 1; j <= i; j++) {
std::cout << j << "×" << i << "=" << i*j << "\t";
}
std::cout << std::endl;
}
return 0;
}
1×1=1
1×2=2 2×2=4
1×3=3 2×3=6 3×3=9
1×4=4 2×4=8 3×4=12 4×4=16
1×5=5 2×5=10 3×5=15 4×5=20 5×5=25
1×6=6 2×6=12 3×6=18 4×6=24 5×6=30 6×6=36
1×7=7 2×7=14 3×7=21 4×7=28 5×7=35 6×7=42 7×7=49
1×8=8 2×8=16 3×8=24 4×8=32 5×8=40 6×8=48 7×8=56 8×8=64
1×9=9 2×9=18 3×9=27 4×9=36 5×9=45 6×9=54 7×9=63 8×9=72 9×9=81
第六章:函数基础
函数是将代码组织成可重用模块的基本单元。通过函数,我们可以避免代码重复,提高程序的可读性和可维护性。
6.1 什么是函数?
函数是一段执行特定任务的代码块,它可以接受输入(参数),执行操作,并返回结果。C++程序本身就是由函数组成的,main函数就是程序的入口点。
6.2 函数的定义与声明
函数定义包含函数的完整实现:
#include <iostream>
// 函数定义
int add(int a, int b) {
return a + b;
}
int main() {
int result = add(5, 3);
std::cout << "5 + 3 = " << result << std::endl;
return 0;
}
5 + 3 = 8
函数声明(也称为函数原型)告诉编译器函数的存在:
#include <iostream>
// 函数声明
int add(int a, int b);
int main() {
int result = add(5, 3);
std::cout << "5 + 3 = " << result << std::endl;
return 0;
}
// 函数定义
int add(int a, int b) {
return a + b;
}
5 + 3 = 8
6.3 函数参数
函数可以接受零个或多个参数:
#include <iostream>
// 无参数函数
void greet() {
std::cout << "Hello!" << std::endl;
}
// 单参数函数
void greetPerson(std::string name) {
std::cout << "Hello, " << name << "!" << std::endl;
}
// 多参数函数
double calculateArea(double length, double width) {
return length * width;
}
int main() {
greet();
greetPerson("Alice");
std::cout << "面积: " << calculateArea(5.0, 3.0) << std::endl;
return 0;
}
Hello!
Hello, Alice!
面积: 15
6.4 返回值
函数可以返回一个值,也可以不返回值(使用void):
#include <iostream>
#include <string>
// 返回整数
int square(int x) {
return x * x;
}
// 返回字符串
std::string concatenate(std::string a, std::string b) {
return a + " " + b;
}
// 无返回值
void printMessage(std::string message) {
std::cout << message << std::endl;
}
int main() {
std::cout << "4的平方是: " << square(4) << std::endl;
std::cout << concatenate("Hello", "World") << std::endl;
printMessage("这是一个消息");
return 0;
}
4的平方是: 16
Hello World
这是一个消息
6.5 函数重载
C++允许定义多个同名函数,只要它们的参数列表不同:
#include <iostream>
// 重载函数
int add(int a, int b) {
return a + b;
}
double add(double a, double b) {
return a + b;
}
int add(int a, int b, int c) {
return a + b + c;
}
int main() {
std::cout << add(2, 3) << std::endl; // 调用int版本
std::cout << add(2.5, 3.7) << std::endl; // 调用double版本
std::cout << add(1, 2, 3) << std::endl; // 调用三参数版本
return 0;
}
5
6.2
6
6.6 默认参数
函数参数可以有默认值:
#include <iostream>
void printMessage(std::string message, int times = 1) {
for (int i = 0; i < times; i++) {
std::cout << message << std::endl;
}
}
int main() {
printMessage("Hello"); // 使用默认值,输出1次
printMessage("World", 3); // 指定参数,输出3次
return 0;
}
Hello
World
World
World
6.7 递归函数
函数可以调用自身,这称为递归:
#include <iostream>
// 计算阶乘的递归函数
int factorial(int n) {
if (n <= 1) {
return 1;
}
return n * factorial(n - 1);
}
int main() {
std::cout << "5! = " << factorial(5) << std::endl; // 120
return 0;
}
5! = 120
递归需要有基本情况(base case)来终止递归,否则会导致无限循环。
6.8 内联函数
对于简单的函数,可以使用inline关键字建议编译器内联展开:
#include <iostream>
inline int max(int a, int b) {
return (a > b) ? a : b;
}
int main() {
std::cout << "较大的数: " << max(10, 20) << std::endl;
return 0;
}
较大的数: 20
内联函数可以减少函数调用的开销,但会增加代码大小。
第七章:数组与字符串
数组和字符串是处理多个相关数据的基本工具。掌握它们的使用对于编写实用程序至关重要。
7.1 一维数组
数组是相同类型元素的集合,通过索引访问:
#include <iostream>
int main() {
// 声明和初始化数组
int numbers[5] = {1, 2, 3, 4, 5};
// 访问数组元素
std::cout << "第一个元素: " << numbers[0] << std::endl;
std::cout << "最后一个元素: " << numbers[4] << std::endl;
// 修改数组元素
numbers[2] = 10;
std::cout << "修改后的第三个元素: " << numbers[2] << std::endl;
// 遍历数组
for (int i = 0; i < 5; i++) {
std::cout << numbers[i] << " ";
}
std::cout << std::endl;
return 0;
}
第一个元素: 1
最后一个元素: 5
修改后的第三个元素: 10
1 2 10 4 5
7.2 多维数组
C++支持多维数组,最常见的是二维数组:
#include <iostream>
int main() {
// 3x3矩阵
int matrix[3][3] = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
// 访问元素
std::cout << "matrix[1][2] = " << matrix[1][2] << std::endl; // 6
// 遍历二维数组
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
std::cout << matrix[i][j] << " ";
}
std::cout << std::endl;
}
return 0;
}
matrix[1][2] = 6
1 2 3
4 5 6
7 8 9
7.3 C风格字符串
C风格字符串是以空字符\0结尾的字符数组:
#include <iostream>
#include <cstring>
int main() {
// 字符数组
char name[20] = "Alice";
// 字符串字面量
char greeting[] = "Hello, World!";
std::cout << "姓名: " << name << std::endl;
std::cout << "问候: " << greeting << std::endl;
// 字符串长度
std::cout << "姓名长度: " << strlen(name) << std::endl;
// 字符串复制
char copy[20];
strcpy(copy, name);
std::cout << "复制的姓名: " << copy << std::endl;
return 0;
}
姓名: Alice
问候: Hello, World!
姓名长度: 5
复制的姓名: Alice
7.4 C++ string类
C++提供了更安全、更易用的std::string类:
#include <iostream>
#include <string>
int main() {
// 创建字符串
std::string name = "Alice";
std::string greeting("Hello, World!");
// 字符串操作
std::cout << "姓名: " << name << std::endl;
std::cout << "长度: " << name.length() << std::endl;
std::cout << "是否为空: " << name.empty() << std::endl;
// 字符串连接
std::string message = name + ", " + greeting;
std::cout << "消息: " << message << std::endl;
// 访问字符
std::cout << "第一个字符: " << name[0] << std::endl;
std::cout << "最后一个字符: " << name.back() << std::endl;
// 子字符串
std::string sub = message.substr(0, 5);
std::cout << "子字符串: " << sub << std::endl;
return 0;
}
姓名: Alice
长度: 5
是否为空: 0
消息: Alice, Hello, World!
第一个字符: A
最后一个字符: e
子字符串: Alice
7.5 数组与函数
数组可以作为函数参数传递:
#include <iostream>
// 注意:数组参数实际上传递的是指针
void printArray(int arr[], int size) {
for (int i = 0; i < size; i++) {
std::cout << arr[i] << " ";
}
std::cout << std::endl;
}
// 计算数组平均值
double calculateAverage(int arr[], int size) {
int sum = 0;
for (int i = 0; i < size; i++) {
sum += arr[i];
}
return static_cast<double>(sum) / size;
}
int main() {
int numbers[] = {10, 20, 30, 40, 50};
int size = sizeof(numbers) / sizeof(numbers[0]);
printArray(numbers, size);
std::cout << "平均值: " << calculateAverage(numbers, size) << std::endl;
return 0;
}
10 20 30 40 50
平均值: 30
7.6 字符串与函数
std::string可以像普通变量一样传递给函数:
#include <iostream>
#include <string>
#include <algorithm> // 必须包含此头文件以使用 std::reverse
void printString(const std::string& str) {
std::cout << str << std::endl;
}
std::string reverseString(const std::string& str) {
std::string reversed = str;
std::reverse(reversed.begin(), reversed.end());
return reversed;
}
int main() {
std::string text = "Hello, World!";
printString(text);
std::cout << "反转: " << reverseString(text) << std::endl;
return 0;
}
Hello, World!
反转: !dlroW ,olleH
注意这里使用了const std::string&,这是传递字符串的推荐方式,避免不必要的拷贝。
第八章:指针与引用
指针和引用是C++的重要特性,它们提供了直接操作内存的能力。虽然初学者可能会觉得这些概念有些抽象,但掌握它们对于理解C++的本质至关重要。
8.1 什么是指针?
指针是一个变量,存储另一个变量的内存地址。通过指针,我们可以间接访问和修改变量的值。
#include <iostream>
int main() {
int number = 42;
int* ptr = &number; // ptr存储number的地址
std::cout << "number的值: " << number << std::endl;
std::cout << "number的地址: " << &number << std::endl;
std::cout << "ptr的值(地址): " << ptr << std::endl;
std::cout << "ptr指向的值: " << *ptr << std::endl;
// 通过指针修改值
*ptr = 100;
std::cout << "修改后的number: " << number << std::endl;
return 0;
}
number的值: 42
number的地址: 0x7ffe2a0df70c
ptr的值(地址): 0x7ffe2a0df70c
ptr指向的值: 42
修改后的number: 100
8.2 指针的基本操作
#include <iostream>
int main() {
int a = 10;
int b = 20;
int* ptr;
ptr = &a; // 指向a
std::cout << *ptr << std::endl; // 10
ptr = &b; // 指向b
std::cout << *ptr << std::endl; // 20
// 空指针
ptr = nullptr; // C++11推荐的空指针表示
// std::cout << *ptr << std::endl; // 危险!会导致程序崩溃
return 0;
}
10
20
8.3 指针与数组
数组名实际上是指向数组第一个元素的指针:
#include <iostream>
int main() {
int arr[5] = {1, 2, 3, 4, 5};
int* ptr = arr; // 等价于 ptr = &arr[0]
// 通过指针访问数组元素
for (int i = 0; i < 5; i++) {
std::cout << *(ptr + i) << " "; // 指针算术
}
std::cout << std::endl;
// 指针遍历
for (int* p = arr; p < arr + 5; p++) {
std::cout << *p << " ";
}
std::cout << std::endl;
return 0;
}
1 2 3 4 5
1 2 3 4 5
8.4 什么是引用?
引用是变量的别名,它必须在声明时初始化,并且不能重新绑定到其他变量:
#include <iostream>
int main() {
int number = 42;
int& ref = number; // ref是number的别名
std::cout << "number: " << number << std::endl;
std::cout << "ref: " << ref << std::endl;
ref = 100; // 修改ref实际上修改了number
std::cout << "修改后的number: " << number << std::endl;
return 0;
}
number: 42
ref: 42
修改后的number: 100
8.5 指针与引用的区别
| 特性 | 指针 | 引用 |
|---|---|---|
| 初始化 | 可以不初始化 | 必须初始化 |
| 重新赋值 | 可以指向不同对象 | 不能重新绑定 |
| 空值 | 可以为nullptr | 不能为空 |
| 内存占用 | 占用内存存储地址 | 不占用额外内存 |
| 语法 | 使用*和-> | 使用. |
8.6 函数参数传递方式
C++支持三种参数传递方式:
#include <iostream>
// 按值传递(拷贝)
void passByValue(int x) {
x = 100;
std::cout << "按值传递内部: " << x << std::endl;
}
// 按指针传递
void passByPointer(int* x) {
*x = 100;
std::cout << "按指针传递内部: " << *x << std::endl;
}
// 按引用传递
void passByReference(int& x) {
x = 100;
std::cout << "按引用传递内部: " << x << std::endl;
}
int main() {
int value = 50;
std::cout << "原始值: " << value << std::endl;
passByValue(value);
std::cout << "按值传递后: " << value << std::endl; // 仍然是50
passByPointer(&value);
std::cout << "按指针传递后: " << value << std::endl; // 变为100
value = 50; // 重置
passByReference(value);
std::cout << "按引用传递后: " << value << std::endl; // 变为100
return 0;
}
原始值: 50
按值传递内部: 100
按值传递后: 50
按指针传递内部: 100
按指针传递后: 100
按引用传递内部: 100
按引用传递后: 100
对于大型对象,按引用传递(特别是const引用)可以避免不必要的拷贝,提高性能。
8.7 动态内存分配
指针允许我们在运行时动态分配内存:
#include <iostream>
int main() {
// 动态分配单个整数
int* ptr = new int(42);
std::cout << "动态分配的值: " << *ptr << std::endl;
delete ptr; // 释放内存
// 动态分配数组
int size = 5;
int* arr = new int[size];
for (int i = 0; i < size; i++) {
arr[i] = i + 1;
}
for (int i = 0; i < size; i++) {
std::cout << arr[i] << " ";
}
std::cout << std::endl;
delete[] arr; // 释放数组内存
return 0;
}
动态分配的值: 42
1 2 3 4 5
重要提醒:使用new分配的内存必须使用delete释放,使用new[]分配的数组必须使用delete[]释放。忘记释放内存会导致内存泄漏。
第九章:面向对象编程基础
面向对象编程(OOP)是C++的核心特性之一。通过类和对象,我们可以将数据和操作数据的函数组织在一起,创建更加模块化和可维护的代码。
9.1 什么是类和对象?
- 类(Class):用户定义的数据类型,描述了一组具有相同属性和行为的对象
- 对象(Object):类的实例,具有类定义的属性和行为
9.2 定义简单的类
#include <iostream>
#include <string>
// 定义一个简单的类
class Person {
public:
// 数据成员
std::string name;
int age;
// 成员函数
void introduce() {
std::cout << "大家好,我是" << name << ",今年" << age << "岁。" << std::endl;
}
void haveBirthday() {
age++;
std::cout << name << "过生日了,现在" << age << "岁!" << std::endl;
}
};
int main() {
// 创建对象
Person person1;
person1.name = "张三";
person1.age = 25;
Person person2;
person2.name = "李四";
person2.age = 30;
// 调用成员函数
person1.introduce();
person2.introduce();
person1.haveBirthday();
return 0;
}
大家好,我是张三,今年25岁。
大家好,我是李四,今年30岁。
张三过生日了,现在26岁!
9.3 构造函数和析构函数
构造函数在创建对象时自动调用,析构函数在对象销毁时自动调用:
#include <iostream>
#include <string>
class Person {
private:
std::string name;
int age;
public:
// 默认构造函数
Person() {
name = "未知";
age = 0;
std::cout << "创建了一个Person对象(默认)" << std::endl;
}
// 带参数的构造函数
Person(std::string n, int a) {
name = n;
age = a;
std::cout << "创建了一个Person对象: " << name << std::endl;
}
// 析构函数
~Person() {
std::cout << "销毁Person对象: " << name << std::endl;
}
void introduce() {
std::cout << "我是" << name << "," << age << "岁。" << std::endl;
}
};
int main() {
Person person1; // 调用默认构造函数
Person person2("王五", 28); // 调用带参数构造函数
person1.introduce();
person2.introduce();
// 程序结束时自动调用析构函数
return 0;
}
创建了一个Person对象(默认)
创建了一个Person对象: 王五
我是未知,0岁。
我是王五,28岁。
销毁Person对象: 王五
销毁Person对象: 未知
9.4 访问控制
C++提供了三种访问控制修饰符:
- public:公有成员,可以从类外部访问
- private:私有成员,只能在类内部访问
- protected:受保护成员,类内部和派生类可以访问
#include <iostream>
#include <string>
class BankAccount {
private:
double balance; // 私有成员,外部无法直接访问
public:
// 构造函数
BankAccount(double initialBalance) {
if (initialBalance >= 0) {
balance = initialBalance;
} else {
balance = 0;
}
}
// 公有接口
void deposit(double amount) {
if (amount > 0) {
balance += amount;
std::cout << "存款成功,余额: " << balance << std::endl;
}
}
bool withdraw(double amount) {
if (amount > 0 && amount <= balance) {
balance -= amount;
std::cout << "取款成功,余额: " << balance << std::endl;
return true;
}
std::cout << "取款失败,余额不足" << std::endl;
return false;
}
double getBalance() const {
return balance;
}
};
int main() {
BankAccount account(1000.0);
// account.balance = -1000; // 错误!无法访问私有成员
account.deposit(500.0);
account.withdraw(200.0);
std::cout << "当前余额: " << account.getBalance() << std::endl;
return 0;
}
存款成功,余额: 1500
取款成功,余额: 1300
当前余额: 1300
9.5 this指针
this指针指向当前对象,用于区分成员变量和参数:
#include <iostream>
#include <string>
class Student {
private:
std::string name;
int id;
public:
Student(std::string name, int id) {
this->name = name; // this->name指成员变量
this->id = id; // 参数name和成员变量name同名
}
void setName(std::string name) {
this->name = name;
}
void printInfo() {
std::cout << "学生: " << name << ", 学号: " << id << std::endl;
}
};
int main() {
Student student("赵六", 12345);
student.printInfo();
return 0;
}
学生: 赵六, 学号: 12345
9.6 const成员函数
在成员函数后面加上const关键字,表示该函数不会修改对象的状态:
#include <iostream>
#include <string>
class Rectangle {
private:
double width, height;
public:
Rectangle(double w, double h) : width(w), height(h) {}
double getArea() const { // const成员函数
return width * height;
}
double getWidth() const {
return width;
}
void setWidth(double w) { // 非const成员函数
width = w;
}
};
int main() {
const Rectangle rect(5.0, 3.0); // const对象
std::cout << "面积: " << rect.getArea() << std::endl;
std::cout << "宽度: " << rect.getWidth() << std::endl;
// rect.setWidth(10.0); // 错误!const对象不能调用非const成员函数
return 0;
}
面积: 15
宽度: 5
9.7 初始化列表
构造函数可以使用初始化列表来初始化成员变量,这比在构造函数体内赋值更高效:
#include <iostream>
#include <string>
class Person {
private:
std::string name;
int age;
double height;
public:
// 使用初始化列表
Person(std::string n, int a, double h)
: name(n), age(a), height(h) {
std::cout << "使用初始化列表创建Person" << std::endl;
}
void printInfo() const {
std::cout << name << ", " << age << "岁, " << height << "米" << std::endl;
}
};
int main() {
Person person("孙七", 22, 1.75);
person.printInfo();
return 0;
}
使用初始化列表创建Person
孙七, 22岁, 1.75米
第十章:继承与多态
继承和多态是面向对象编程的高级特性,它们支持代码重用和灵活的设计模式。
10.1 什么是继承?
继承允许一个类(派生类)获得另一个类(基类)的属性和方法。这支持代码重用和层次化设计。
#include <iostream>
#include <string>
// 基类
class Animal {
protected:
std::string name;
public:
Animal(std::string n) : name(n) {}
virtual void makeSound() const {
std::cout << name << " 发出声音" << std::endl;
}
void sleep() const {
std::cout << name << " 在睡觉" << std::endl;
}
};
// 派生类
class Dog : public Animal {
public:
Dog(std::string n) : Animal(n) {}
void makeSound() const override {
std::cout << name << " 汪汪叫" << std::endl;
}
void wagTail() const {
std::cout << name << " 摇尾巴" << std::endl;
}
};
class Cat : public Animal {
public:
Cat(std::string n) : Animal(n) {}
void makeSound() const override {
std::cout << name << " 喵喵叫" << std::endl;
}
void purr() const {
std::cout << name << " 咕噜咕噜" << std::endl;
}
};
int main() {
Dog dog("旺财");
Cat cat("咪咪");
dog.makeSound(); // 旺财 汪汪叫
dog.sleep(); // 旺财 在睡觉
dog.wagTail(); // 旺财 摇尾巴
cat.makeSound(); // 咪咪 喵喵叫
cat.sleep(); // 咪咪 在睡觉
cat.purr(); // 咪咪 咕噜咕噜
return 0;
}
旺财 汪汪叫
旺财 在睡觉
旺财 摇尾巴
咪咪 喵喵叫
咪咪 在睡觉
咪咪 咕噜咕噜
10.2 访问控制与继承
继承时的访问控制:
#include <iostream>
class Base {
public:
int publicVar;
protected:
int protectedVar;
private:
int privateVar;
};
class DerivedPublic : public Base {
// publicVar 仍然是 public
// protectedVar 仍然是 protected
// privateVar 不可访问
};
class DerivedProtected : protected Base {
// publicVar 变为 protected
// protectedVar 仍然是 protected
// privateVar 不可访问
};
class DerivedPrivate : private Base {
// publicVar 变为 private
// protectedVar 变为 private
// privateVar 不可访问
};
10.3 虚函数与多态
虚函数支持运行时多态,允许通过基类指针调用派生类的函数:
#include <iostream>
#include <vector>
#include <memory>
class Shape {
public:
virtual ~Shape() = default; // 虚析构函数
virtual double getArea() const = 0; // 纯虚函数
virtual void draw() const {
std::cout << "绘制形状" << std::endl;
}
};
class Circle : public Shape {
private:
double radius;
public:
Circle(double r) : radius(r) {}
double getArea() const override {
return 3.14159 * radius * radius;
}
void draw() const override {
std::cout << "绘制圆形,半径: " << radius << std::endl;
}
};
class Rectangle : public Shape {
private:
double width, height;
public:
Rectangle(double w, double h) : width(w), height(h) {}
double getArea() const override {
return width * height;
}
void draw() const override {
std::cout << "绘制矩形,宽: " << width << ", 高: " << height << std::endl;
}
};
void processShape(const Shape& shape) {
shape.draw();
std::cout << "面积: " << shape.getArea() << std::endl;
}
int main() {
Circle circle(5.0);
Rectangle rectangle(4.0, 3.0);
processShape(circle); // 多态调用
processShape(rectangle); // 多态调用
// 使用智能指针的多态容器
std::vector<std::unique_ptr<Shape>> shapes;
shapes.push_back(std::make_unique<Circle>(3.0));
shapes.push_back(std::make_unique<Rectangle>(2.0, 4.0));
for (const auto& shape : shapes) {
shape->draw();
std::cout << "面积: " << shape->getArea() << std::endl;
}
return 0;
}
绘制圆形,半径: 5
面积: 78.5397
绘制矩形,宽: 4, 高: 3
面积: 12
绘制圆形,半径: 3
面积: 28.2743
绘制矩形,宽: 2, 高: 4
面积: 8
10.4 抽象类
包含纯虚函数的类称为抽象类,不能实例化:
// Shape类就是抽象类,因为有纯虚函数getArea()
// Shape shape; // 错误!不能实例化抽象类
10.5 函数重写与重载
- 重写(Override):派生类重新定义基类的虚函数
- 重载(Overload):同一作用域内多个同名函数,参数不同
class Base {
public:
virtual void func(int x) {
std::cout << "Base::func(int): " << x << std::endl;
}
void func(double x) { // 重载
std::cout << "Base::func(double): " << x << std::endl;
}
};
class Derived : public Base {
public:
void func(int x) override { // 重写
std::cout << "Derived::func(int): " << x << std::endl;
}
void func(int x, int y) { // 重载(新的重载版本)
std::cout << "Derived::func(int, int): " << x << ", " << y << std::endl;
}
};
第十一章:标准模板库(STL)入门
标准模板库(STL)是C++标准库的重要组成部分,提供了高效的容器、算法和迭代器。掌握STL可以大大提高编程效率。
11.1 vector容器
std::vector是动态数组,可以自动调整大小:
#include <iostream>
#include <vector>
int main() {
// 创建vector
std::vector<int> numbers;
// 添加元素
numbers.push_back(10);
numbers.push_back(20);
numbers.push_back(30);
// 访问元素
std::cout << "第一个元素: " << numbers[0] << std::endl;
std::cout << "最后一个元素: " << numbers.back() << std::endl;
// 遍历vector
for (size_t i = 0; i < numbers.size(); i++) {
std::cout << numbers[i] << " ";
}
std::cout << std::endl;
// 使用范围for循环(C++11)
for (int num : numbers) {
std::cout << num << " ";
}
std::cout << std::endl;
// 使用迭代器
for (auto it = numbers.begin(); it != numbers.end(); ++it) {
std::cout << *it << " ";
}
std::cout << std::endl;
return 0;
}
第一个元素: 10
最后一个元素: 30
10 20 30
10 20 30
10 20 30
11.2 string容器
std::string是STL中的字符串类:
#include <iostream>
#include <string>
#include <vector>
int main() {
std::string text = "Hello, World!";
// 字符串操作
std::cout << "长度: " << text.length() << std::endl;
std::cout << "是否为空: " << text.empty() << std::endl;
// 查找和替换
size_t pos = text.find("World");
if (pos != std::string::npos) {
text.replace(pos, 5, "C++");
}
std::cout << text << std::endl;
// 分割字符串(简单示例)
std::string sentence = "apple,banana,cherry";
std::vector<std::string> fruits;
size_t start = 0;
size_t end = sentence.find(',');
while (end != std::string::npos) {
fruits.push_back(sentence.substr(start, end - start));
start = end + 1;
end = sentence.find(',', start);
}
fruits.push_back(sentence.substr(start));
for (const auto& fruit : fruits) {
std::cout << fruit << std::endl;
}
return 0;
}
长度: 13
是否为空: 0
Hello, C++!
apple
banana
cherry
11.3 map容器
std::map是关联容器,存储键值对:
#include <iostream>
#include <map>
#include <string>
int main() {
// 创建map
std::map<std::string, int> ages;
// 插入元素
ages["Alice"] = 25;
ages["Bob"] = 30;
ages["Charlie"] = 35;
// 访问元素
std::cout << "Alice的年龄: " << ages["Alice"] << std::endl;
// 检查键是否存在
if (ages.find("David") != ages.end()) {
std::cout << "David存在" << std::endl;
} else {
std::cout << "David不存在" << std::endl;
}
// 遍历map
for (const auto& pair : ages) {
std::cout << pair.first << ": " << pair.second << std::endl;
}
// 统计单词频率
std::string text = "apple banana apple cherry banana apple";
std::map<std::string, int> wordCount;
// 简单的单词分割(实际应用中需要更复杂的逻辑)
std::string word;
for (char c : text) {
if (c == ' ') {
if (!word.empty()) {
wordCount[word]++;
word.clear();
}
} else {
word += c;
}
}
if (!word.empty()) {
wordCount[word]++;
}
std::cout << "\n单词频率:" << std::endl;
for (const auto& pair : wordCount) {
std::cout << pair.first << ": " << pair.second << std::endl;
}
return 0;
}
Alice的年龄: 25
David不存在
Alice: 25
Bob: 30
Charlie: 35
单词频率:
apple: 3
banana: 2
cherry: 1
11.4 算法库
STL算法库提供了大量通用算法:
#include <iostream>
#include <vector>
#include <algorithm>
#include <numeric>
int main() {
std::vector<int> numbers = {5, 2, 8, 1, 9, 3};
// 排序
std::sort(numbers.begin(), numbers.end());
std::cout << "排序后: ";
for (int n : numbers) std::cout << n << " ";
std::cout << std::endl;
// 反转
std::reverse(numbers.begin(), numbers.end());
std::cout << "反转后: ";
for (int n : numbers) std::cout << n << " ";
std::cout << std::endl;
// 查找
auto it = std::find(numbers.begin(), numbers.end(), 8);
if (it != numbers.end()) {
std::cout << "找到8,位置: " << std::distance(numbers.begin(), it) << std::endl;
}
// 计算总和
int sum = std::accumulate(numbers.begin(), numbers.end(), 0);
std::cout << "总和: " << sum << std::endl;
// 应用函数到每个元素
std::vector<int> squares(numbers.size());
std::transform(numbers.begin(), numbers.end(), squares.begin(),
[](int x) { return x * x; });
std::cout << "平方: ";
for (int s : squares) std::cout << s << " ";
std::cout << std::endl;
return 0;
}
排序后: 1 2 3 5 8 9
反转后: 9 8 5 3 2 1
找到8,位置: 1
总和: 28
平方: 81 64 25 9 4 1
11.5 迭代器
迭代器是STL的核心概念,提供了统一的容器遍历接口:
#include <iostream>
#include <vector>
#include <list>
template<typename Container>
void printContainer(const Container& container) {
for (auto it = container.begin(); it != container.end(); ++it) {
std::cout << *it << " ";
}
std::cout << std::endl;
}
int main() {
std::vector<int> vec = {1, 2, 3, 4, 5};
std::list<int> lst = {10, 20, 30, 40, 50};
printContainer(vec);
printContainer(lst);
return 0;
}
1 2 3 4 5
10 20 30 40 50
第十二章:异常处理
异常处理机制允许程序优雅地处理错误情况,避免程序崩溃。
12.1 基本异常处理
#include <iostream>
#include <stdexcept>
double divide(double a, double b) {
if (b == 0) {
throw std::invalid_argument("除数不能为零");
}
return a / b;
}
int main() {
try {
double result = divide(10.0, 0.0);
std::cout << "结果: " << result << std::endl;
} catch (const std::invalid_argument& e) {
std::cout << "捕获异常: " << e.what() << std::endl;
} catch (...) {
std::cout << "捕获未知异常" << std::endl;
}
return 0;
}
捕获异常: 除数不能为零
12.2 自定义异常
#include <iostream>
#include <stdexcept>
#include <string>
class AgeException : public std::exception {
private:
std::string message;
public:
AgeException(int age) {
message = "无效年龄: " + std::to_string(age) + "。年龄必须在0-150之间。";
}
const char* what() const noexcept override {
return message.c_str();
}
};
void setAge(int age) {
if (age < 0 || age > 150) {
throw AgeException(age);
}
std::cout << "年龄设置为: " << age << std::endl;
}
int main() {
try {
setAge(25); // 正常
setAge(-5); // 抛出异常
} catch (const AgeException& e) {
std::cout << "错误: " << e.what() << std::endl;
}
return 0;
}
年龄设置为: 25
错误: 无效年龄: -5。年龄必须在0-150之间。
12.3 异常安全
编写异常安全的代码需要考虑资源管理:
#include <iostream>
#include <memory>
class ResourceManager {
private:
int* resource;
public:
ResourceManager() {
resource = new int(42);
std::cout << "资源分配" << std::endl;
}
~ResourceManager() {
delete resource;
std::cout << "资源释放" << std::endl;
}
void riskyOperation() {
throw std::runtime_error("操作失败");
}
};
// 使用RAII(资源获取即初始化)确保异常安全
void safeFunction() {
std::unique_ptr<ResourceManager> manager = std::make_unique<ResourceManager>();
manager->riskyOperation(); // 即使抛出异常,资源也会被正确释放
}
int main() {
try {
safeFunction();
} catch (const std::exception& e) {
std::cout << "捕获异常: " << e.what() << std::endl;
}
return 0;
}
资源分配
资源释放
捕获异常: 操作失败
第十三章:文件操作
文件操作是实际应用中的常见需求,C++提供了多种文件处理方式。
13.1 基本文件读写
#include <iostream>
#include <fstream>
#include <string>
void writeToFile() {
std::ofstream file("example.txt");
if (file.is_open()) {
file << "Hello, File!" << std::endl;
file << "这是第二行" << std::endl;
file.close();
std::cout << "文件写入完成" << std::endl;
} else {
std::cout << "无法打开文件进行写入" << std::endl;
}
}
void readFromFile() {
std::ifstream file("example.txt");
if (file.is_open()) {
std::string line;
while (std::getline(file, line)) {
std::cout << line << std::endl;
}
file.close();
} else {
std::cout << "无法打开文件进行读取" << std::endl;
}
}
int main() {
writeToFile();
readFromFile();
return 0;
}
文件写入完成
Hello, File!
这是第二行
13.2 二进制文件操作
#include <iostream>
#include <fstream>
#include <vector>
struct Student {
char name[50];
int age;
double gpa;
};
void writeBinaryFile() {
std::ofstream file("students.dat", std::ios::binary);
if (file.is_open()) {
Student students[] = {
{"张三", 20, 3.8},
{"李四", 21, 3.5},
{"王五", 19, 3.9}
};
file.write(reinterpret_cast<char*>(students), sizeof(students));
file.close();
std::cout << "二进制文件写入完成" << std::endl;
}
}
void readBinaryFile() {
std::ifstream file("students.dat", std::ios::binary);
if (file.is_open()) {
file.seekg(0, std::ios::end);
size_t fileSize = file.tellg();
size_t recordSize = sizeof(Student);
size_t numRecords = fileSize / recordSize;
file.seekg(0, std::ios::beg);
std::vector<Student> students(numRecords);
file.read(reinterpret_cast<char*>(students.data()), fileSize);
file.close();
for (const auto& student : students) {
std::cout << "姓名: " << student.name
<< ", 年龄: " << student.age
<< ", GPA: " << student.gpa << std::endl;
}
}
}
int main() {
writeBinaryFile();
readBinaryFile();
return 0;
}
二进制文件写入完成
姓名: 张三, 年龄: 20, GPA: 3.8
姓名: 李四, 年龄: 21, GPA: 3.5
姓名: 王五, 年龄: 19, GPA: 3.9
第十四章:现代C++特性(C++11/14/17)
现代C++引入了许多改进,使代码更安全、更简洁、更高效。
14.1 自动类型推导(auto)
#include <iostream>
#include <vector>
#include <map>
int main() {
auto x = 42; // int
auto y = 3.14; // double
auto name = "Hello"; // const char*
auto vec = std::vector<int>{1, 2, 3}; // std::vector<int>
std::map<std::string, int> ages = {{"Alice", 25}, {"Bob", 30}};
for (const auto& pair : ages) {
std::cout << pair.first << ": " << pair.second << std::endl;
}
return 0;
}
Alice: 25
Bob: 30
14.2 智能指针
智能指针自动管理内存,避免内存泄漏:
#include <iostream>
#include <memory>
class MyClass {
public:
MyClass(int value) : data(value) {
std::cout << "构造 MyClass(" << data << ")" << std::endl;
}
~MyClass() {
std::cout << "析构 MyClass(" << data << ")" << std::endl;
}
void print() const {
std::cout << "数据: " << data << std::endl;
}
private:
int data;
};
int main() {
// unique_ptr:独占所有权
std::unique_ptr<MyClass> ptr1 = std::make_unique<MyClass>(42);
ptr1->print();
// shared_ptr:共享所有权
std::shared_ptr<MyClass> ptr2 = std::make_shared<MyClass>(100);
std::shared_ptr<MyClass> ptr3 = ptr2; // 引用计数增加
std::cout << "引用计数: " << ptr2.use_count() << std::endl;
return 0;
}
构造 MyClass(42)
数据: 42
构造 MyClass(100)
引用计数: 2
析构 MyClass(100)
析构 MyClass(42)
14.3 Lambda表达式
Lambda表达式提供了一种简洁的匿名函数定义方式:
#include <iostream>
#include <vector>
#include <algorithm>
int main() {
std::vector<int> numbers = {5, 2, 8, 1, 9, 3};
// 排序(降序)
std::sort(numbers.begin(), numbers.end(), [](int a, int b) {
return a > b;
});
// 过滤偶数
std::vector<int> evens;
std::copy_if(numbers.begin(), numbers.end(), std::back_inserter(evens),
[](int x) { return x % 2 == 0; });
// 计算平方
std::vector<int> squares;
std::transform(numbers.begin(), numbers.end(), std::back_inserter(squares),
[](int x) { return x * x; });
std::cout << "原数组: ";
for (int n : numbers) std::cout << n << " ";
std::cout << std::endl;
std::cout << "偶数: ";
for (int n : evens) std::cout << n << " ";
std::cout << std::endl;
std::cout << "平方: ";
for (int n : squares) std::cout << n << " ";
std::cout << std::endl;
return 0;
}
原数组: 9 8 5 3 2 1
偶数: 8 2
平方: 81 64 25 9 4 1
14.4 范围for循环
#include <iostream>
#include <vector>
#include <string>
int main() {
std::vector<std::string> fruits = {"apple", "banana", "cherry"};
// 只读访问
for (const auto& fruit : fruits) {
std::cout << fruit << std::endl;
}
// 修改元素
for (auto& fruit : fruits) {
fruit += "!";
}
// 显示修改后的结果
for (const auto& fruit : fruits) {
std::cout << fruit << std::endl;
}
return 0;
}
apple
banana
cherry
apple!
banana!
cherry!
14.5 移动语义
移动语义避免不必要的拷贝,提高性能:
#include <iostream>
#include <vector>
#include <string>
class HeavyObject {
private:
std::vector<int> data;
std::string name;
public:
HeavyObject(const std::string& n, size_t size) : name(n) {
data.resize(size);
std::cout << "构造 " << name << std::endl;
}
// 拷贝构造函数
HeavyObject(const HeavyObject& other) : data(other.data), name(other.name + "_copy") {
std::cout << "拷贝构造 " << name << std::endl;
}
// 移动构造函数
HeavyObject(HeavyObject&& other) noexcept
: data(std::move(other.data)), name(std::move(other.name) + "_moved") {
std::cout << "移动构造 " << name << std::endl;
}
const std::string& getName() const { return name; }
};
HeavyObject createObject() {
return HeavyObject("temp", 1000); // 返回临时对象,触发移动
}
int main() {
HeavyObject obj = createObject(); // 移动构造,不是拷贝构造
std::cout << "最终对象: " << obj.getName() << std::endl;
return 0;
}
构造 temp
最终对象: temp
第十五章:项目实践——简单的学生管理系统
现在让我们综合运用所学知识,构建一个简单的学生管理系统。
#include <iostream>
#include <vector>
#include <string>
#include <fstream>
#include <algorithm>
#include <memory>
class Student {
private:
std::string name;
int id;
double gpa;
public:
Student(const std::string& n, int i, double g) : name(n), id(i), gpa(g) {}
// Getter方法
const std::string& getName() const { return name; }
int getId() const { return id; }
double getGpa() const { return gpa; }
// Setter方法
void setGpa(double g) { gpa = g; }
// 显示学生信息
void display() const {
std::cout << "姓名: " << name << ", 学号: " << id << ", GPA: " << gpa << std::endl;
}
// 用于文件存储
void saveToFile(std::ofstream& file) const {
file << name << "," << id << "," << gpa << std::endl;
}
static std::unique_ptr<Student> loadFromFile(std::ifstream& file) {
std::string line;
if (std::getline(file, line)) {
size_t pos1 = line.find(',');
size_t pos2 = line.find(',', pos1 + 1);
if (pos1 != std::string::npos && pos2 != std::string::npos) {
std::string name = line.substr(0, pos1);
int id = std::stoi(line.substr(pos1 + 1, pos2 - pos1 - 1));
double gpa = std::stod(line.substr(pos2 + 1));
return std::make_unique<Student>(name, id, gpa);
}
}
return nullptr;
}
};
class StudentManager {
private:
std::vector<std::unique_ptr<Student>> students;
public:
void addStudent(const std::string& name, int id, double gpa) {
// 检查学号是否已存在
if (findStudentById(id) != nullptr) {
std::cout << "错误:学号 " << id << " 已存在!" << std::endl;
return;
}
students.push_back(std::make_unique<Student>(name, id, gpa));
std::cout << "学生 " << name << " 添加成功!" << std::endl;
}
Student* findStudentById(int id) {
auto it = std::find_if(students.begin(), students.end(),
[id](const std::unique_ptr<Student>& student) {
return student->getId() == id;
});
return (it != students.end()) ? it->get() : nullptr;
}
void displayAllStudents() const {
if (students.empty()) {
std::cout << "没有学生记录。" << std::endl;
return;
}
std::cout << "\n=== 所有学生信息 ===" << std::endl;
for (const auto& student : students) {
student->display();
}
}
void updateStudentGpa(int id, double newGpa) {
Student* student = findStudentById(id);
if (student) {
student->setGpa(newGpa);
std::cout << "学生 " << student->getName() << " 的GPA已更新为 " << newGpa << std::endl;
} else {
std::cout << "未找到学号为 " << id << " 的学生。" << std::endl;
}
}
void deleteStudent(int id) {
auto it = std::find_if(students.begin(), students.end(),
[id](const std::unique_ptr<Student>& student) {
return student->getId() == id;
});
if (it != students.end()) {
std::cout << "学生 " << (*it)->getName() << " 已删除。" << std::endl;
students.erase(it);
} else {
std::cout << "未找到学号为 " << id << " 的学生。" << std::endl;
}
}
void saveToFile(const std::string& filename) const {
std::ofstream file(filename);
if (file.is_open()) {
for (const auto& student : students) {
student->saveToFile(file);
}
file.close();
std::cout << "数据已保存到 " << filename << std::endl;
} else {
std::cout << "无法打开文件 " << filename << " 进行保存。" << std::endl;
}
}
void loadFromFile(const std::string& filename) {
std::ifstream file(filename);
if (file.is_open()) {
students.clear();
while (true) {
auto student = Student::loadFromFile(file);
if (student) {
students.push_back(std::move(student));
} else {
break;
}
}
file.close();
std::cout << "数据已从 " << filename << " 加载。" << std::endl;
} else {
std::cout << "无法打开文件 " << filename << " 进行加载。" << std::endl;
}
}
};
void displayMenu() {
std::cout << "\n=== 学生管理系统 ===" << std::endl;
std::cout << "1. 添加学生" << std::endl;
std::cout << "2. 显示所有学生" << std::endl;
std::cout << "3. 更新学生GPA" << std::endl;
std::cout << "4. 删除学生" << std::endl;
std::cout << "5. 保存到文件" << std::endl;
std::cout << "6. 从文件加载" << std::endl;
std::cout << "0. 退出" << std::endl;
std::cout << "请选择操作: ";
}
int main() {
StudentManager manager;
int choice;
while (true) {
displayMenu();
std::cin >> choice;
switch (choice) {
case 1: {
std::string name;
int id;
double gpa;
std::cout << "请输入姓名: ";
std::cin >> name;
std::cout << "请输入学号: ";
std::cin >> id;
std::cout << "请输入GPA: ";
std::cin >> gpa;
manager.addStudent(name, id, gpa);
break;
}
case 2:
manager.displayAllStudents();
break;
case 3: {
int id;
double gpa;
std::cout << "请输入学号: ";
std::cin >> id;
std::cout << "请输入新的GPA: ";
std::cin >> gpa;
manager.updateStudentGpa(id, gpa);
break;
}
case 4: {
int id;
std::cout << "请输入要删除的学生学号: ";
std::cin >> id;
manager.deleteStudent(id);
break;
}
case 5:
manager.saveToFile("students.txt");
break;
case 6:
manager.loadFromFile("students.txt");
break;
case 0:
std::cout << "感谢使用学生管理系统!" << std::endl;
return 0;
default:
std::cout << "无效选择,请重新输入。" << std::endl;
}
}
return 0;
}
=== 学生管理系统 ===
1. 添加学生
2. 显示所有学生
3. 更新学生GPA
4. 删除学生
5. 保存到文件
6. 从文件加载
0. 退出
请选择操作:

这个项目展示了如何将前面学到的概念应用到实际开发中:
- 面向对象设计(类和对象)
- 动态内存管理(智能指针)
- STL容器(vector)
- 文件操作
- 异常处理(输入验证)
- 现代C++特性(auto、lambda表达式)
结语:持续学习的建议
恭喜你完成了这个C++入门教程!从"Hello, World!"到完整的学生管理系统,你已经掌握了C++编程的核心概念。然而,编程学习是一个持续的过程,以下是一些建议帮助你继续进步:
1. 实践是最好的老师
- 动手编写代码:不要只是阅读代码,要亲自编写和调试
- 解决实际问题:尝试用C++解决你感兴趣的问题
- 参与开源项目:阅读和贡献开源代码是提高技能的好方法
2. 深入学习的方向
- 数据结构与算法:掌握更复杂的数据结构和算法
- 系统编程:学习操作系统、网络编程等底层知识
- 模板元编程:探索C++模板的高级用法
- 并发编程:学习多线程和并发控制
3. 推荐的学习资源
- 书籍:《C++ Primer》、《Effective C++》、《The C++ Programming Language》
- 在线课程:Coursera、edX上的C++课程
- 文档:cppreference.com是权威的C++参考网站
- 社区:Stack Overflow、Reddit的r/cpp社区
4. 养成良好的编程习惯
- 代码风格:保持一致的代码格式和命名规范
- 版本控制:学习使用Git管理代码
- 测试驱动:编写测试用例确保代码质量
- 代码审查:学会阅读和评价他人代码
记住,每个优秀的程序员都是从"Hello, World!"开始的。保持好奇心,勇于尝试,不要害怕犯错。编程不仅是一项技能,更是一种思维方式。通过C++的学习,你不仅掌握了一门强大的编程语言,更重要的是培养了解决复杂问题的能力。
558

被折叠的 条评论
为什么被折叠?



