C++ 编程教程:从入门到精通
C++ 编程教程:从入门到精通
代码示例均为完整、可编译的程序,并包括预期输出和测试用例,以帮助读者更好地理解和实践。所有代码假设使用标准C++编译器(如g++),并包含必要的头文件和命名空间。
第一章:C++ 简介
1.1 C++ 的历史与演变
C++ 由 Bjarne Stroustrup 在 1979 年开始开发,最初被称为 “C with Classes”,目的是扩展 C 语言的功能,使其支持面向对象编程,同时保留 C 的高效性和底层控制能力。1983 年,该语言正式命名为 C++(表示 C 的增量)。1985 年发布了第一个完整版本,并随后的标准化过程使其不断演化。C++ 的标准化由国际标准化组织(ISO)负责,主要版本包括:
- C++98:第一个标准版本,引入了标准模板库(STL)和异常处理。
- C++03:小幅修正,修复了C++98中的一些问题。
- C++11:重大更新,添加了lambda表达式、auto关键字、智能指针等现代特性,提高了语言的表达力和安全性。
- C++14:对C++11的扩展,增强了泛型lambda和constexpr。
- C++17:引入了结构化绑定、if constexpr和文件系统库。
- C++20:添加了模块、协程、概念(concepts)和ranges库,进一步提升了代码的可读性和性能。
- C++23(最新):包括更多模块支持和反射预览。
C++ 的演变强调向后兼容性,同时融入现代编程范式,如函数式编程和并发支持。未来版本(如C++26)可能聚焦于模式匹配和更好的错误处理。
1.2 C++ 的特点和优势
C++ 是一种多范式编程语言,支持过程式、面向对象、泛型和函数式编程。关键特点包括:
- 面向对象编程:支持封装(将数据和方法捆绑)、继承(代码重用)和多态(运行时动态行为),提高代码的可重用性和模块化。
- 高效性:提供直接内存管理和指针操作,适合性能敏感的应用。相比Java或Python,C++编译为本地代码,运行更快,但需要手动管理资源以避免内存泄漏。
- 标准模板库 (STL):一个内置库,包含容器(如vector、map)、算法(如sort、find)和迭代器,极大地提高了开发效率。例如,STL允许在几行代码中实现复杂的数据处理,而无需从头编写。
- 多范式支持:可以混合使用不同风格的编程,例如在同一程序中使用过程式循环和泛型模板。
- 其他优势:零开销抽象(抽象不牺牲性能)、模板元编程(编译时计算)和跨平台性(通过标准遵守)。
潜在缺点:学习曲线陡峭,容易出错(如空指针解引用),但现代特性(如智能指针)缓解了这些问题。
1.3 C++ 的应用领域
C++ 在需要高性能和低级控制的领域中广泛应用:
- 系统软件:操作系统(如Windows内核部分)、编译器(如GCC)和网络系统(如Apache服务器)。
- 应用软件:桌面应用(如Adobe Photoshop)、数据库(如MySQL)和图形用户界面(GUI)框架(如Qt)。
- 游戏开发:高性能游戏引擎,如 Unreal Engine 和 Unity 的底层部分,需要实时渲染和物理模拟。
- 嵌入式系统:汽车控制系统、家电(如智能冰箱)和机器人设计(如ROS框架),由于资源受限,C++的效率至关重要。
- 其他领域:金融交易系统(高频交易)、科学计算(如模拟)和AI框架(如TensorFlow的C++后端)。
C++ 的应用强调性能,但对于Web开发或脚本任务,可能不如Python合适。
1.4 C++ 的未来展望
随着技术的不断发展,C++ 正在与时俱进,越来越多的特性(如概念和协程)正在被引入,以满足现代开发的需求。社区对于可维护性和安全性的关注也在增加,例如通过静态分析工具(如Clang-Tidy)和新标准(如C++ Core Guidelines)减少常见错误。未来,C++可能更好地支持GPU编程、分布式系统和安全性增强。随着Rust等语言的竞争,C++将继续演化以保持竞争力。
第二章:环境搭建
2.1 安装 C++ 编译器与 IDE
要开始C++编程,需要安装编译器和集成开发环境(IDE)。编译器将源代码转换为可执行文件,IDE提供代码编辑、调试和项目管理。
- Windows:
- MinGW:轻量级的GCC端口,简单易用。下载MinGW-w64,从官网安装后添加bin目录到PATH。
- Visual Studio:微软的免费IDE(Community版),内置MSVC编译器。下载安装,选择C++工作负载。适合初学者,提供IntelliSense和调试器。
- Linux:
- 使用包管理器:Debian/Ubuntu上运行
sudo apt-get install g++;CentOS/Fedora上运行sudo yum install gcc-c++。这安装GCC编译器。
- 使用包管理器:Debian/Ubuntu上运行
- Mac:
- 使用 Homebrew(包管理器):先安装Homebrew(
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"),然后brew install gcc。或者使用Xcode Command Line Tools(xcode-select --install)。
- 使用 Homebrew(包管理器):先安装Homebrew(
推荐IDE:Visual Studio Code(跨平台,安装C++扩展)、CLion(专业但付费)或Code::Blocks(免费开源)。
2.2 配置开发环境
确保将编译器添加到系统路径中(环境变量PATH),以便从任何目录运行g++或cl.exe。可使用命令行工具或终端进行编译和运行。测试配置:打开终端,运行 g++ --version 或 cl,如果显示版本信息,则配置成功。
常见问题:路径错误导致“command not found”。在Windows上,使用系统属性添加路径;在Linux/Mac上,编辑/.bashrc或/.zshrc。
2.3 编译与运行示例程序
创建一个名为 hello.cpp 的文件,内容如下:
#include<iostream>usingnamespace std;intmain(){ cout <<"Hello, C++!"<< endl;// 输出 "Hello, C++!"return0;// 返回 0,表示程序正常结束}在命令行中,使用以下命令编译并运行程序:
g++ hello.cpp -o hello ./hello # Windows上为 hello.exe预期输出:
Hello, C++! 更多实例:一个带输入的程序 (input.cpp):
#include<iostream>usingnamespace std;intmain(){ string name; cout <<"Enter your name: "; cin >> name; cout <<"Hello, "<< name <<"!"<< endl;return0;}编译运行:g++ input.cpp -o input && ./input
测试输入:“Alice”
输出:“Hello, Alice!”
测试代码:检查错误处理。修改hello.cpp移除return 0,编译运行仍正常(隐式返回0),但添加无效代码如cout << undefined; 会导致编译错误。
第三章:基本语法
3.1 C++ 程序结构
一个基本的 C++ 程序通常包括头文件(如用于输入输出)、命名空间(using namespace std;避免std::前缀)、主函数(main()入口点)和必要的逻辑。程序从main开始执行,返回0表示成功。
扩展解释:头文件提供函数声明,main必须返回int。大型程序可能有多个文件,通过#include和链接编译。
3.2 注释的使用
使用注释可以提高代码的可读性。注释不影响执行,但有助于文档化。
- 单行:
// 这是单行注释 - 多行:
/* 这是多行注释 可以跨越多行 */
Javadoc-style注释(如/** */)可用于工具生成文档。
实例代码 (comments.cpp):
#include<iostream>usingnamespace std;// 主函数:程序入口intmain(){/* 多行注释: 输出问候语 */ cout <<"Hello, World!"<< endl;return0;}输出:“Hello, World!”
3.3 数据类型与变量
C++ 为程序员提供了种类丰富的内置数据类型和用户自定义的数据类型。下表列出了七种基本的类型:
| 类型 | 关键字 |
|---|---|
| 布尔型 | bool |
| 字符型 | char |
| 整型 | int |
| 浮点型 | float |
| 双浮点型 | double |
| 无类型 | void |
| 宽字符型 | wchar_t |
wchar_t 是 typedef short int wchar_t; 所以空间与 short int 相同。
类型修饰符:signed(默认)、unsigned(无符号)、short、long。
下表显示了各种变量类型在内存中存储值时需要占用的内存,以及范围(64位系统常见值):
注意:不同系统会有所差异,一字节为 8 位。默认int/short/long带signed。long int 8字节,int 4字节(兼容旧标准)。
| 类型 | 位 | 范围 |
|---|---|---|
| char | 8 | -128 到 127 或 0 到 255 |
| unsigned char | 8 | 0 到 255 |
| signed char | 8 | -128 到 127 |
| int | 32 | -2^31 到 2^31-1 |
| unsigned int | 32 | 0 到 2^32-1 |
| short int | 16 | -2^15 到 2^15-1 |
| unsigned short int | 16 | 0 到 2^16-1 |
| long int | 64 | -2^63 到 2^63-1 |
| unsigned long int | 64 | 0 到 2^64-1 |
| float | 32 | ~ +/- 3.4e38 (7位精度) |
| double | 64 | ~ +/- 1.7e308 (15位精度) |
| long double | 128 | ~ +/- 1.1e4932 (18-19位精度) |
| wchar_t | 16/32 | 宽字符范围 |
变量的大小取决于系统(32位 vs 64位)。以下实例输出实际大小:
#include<iostream>#include<limits>usingnamespace std;intmain(){ cout <<"type: \t\t"<<"************size**************"<< endl; cout <<"bool: \t\t"<<"字节:"<<sizeof(bool)<<"\tmax:"<<numeric_limits<bool>::max()<<"\tmin:"<<numeric_limits<bool>::min()<< endl;// ... (完整代码如原始提供,添加所有类型) cout <<"string: \t"<<"字节:"<<sizeof(string)<< endl;return0;}预期输出(如原始所示,取决于系统)。
更多变量类型解释:
- bool:true/false,1字节。
- char:ASCII字符,1字节。
- int:整数,4字节。
- float:单精度,1符号+8指数+23小数。
- double:双精度,1符号+11指数+52小数。
- void:无类型,用于函数返回或指针。
- wchar_t:宽字符,支持Unicode。
其他类型:enum、pointer、array、struct、class等后续章节讲解。
整数类型:short (2字节)、long (4/8字节)、long long (8字节)。
浮点:long double更高精度。
字符:char16_t/char32_t for Unicode。
布尔:bool。
枚举:enum {A=0, B=1};。
指针:type* p;。
数组:type arr[size];。
结构体:struct S {int x;};。
类:class C {public: int x;};。
联合:union U {int i; float f;};。
类型大小非固定,但标准规定最小范围。
丰富实例 (types.cpp):
#include<iostream>usingnamespace std;intmain(){int i =42;float f =3.14f;double d =2.718;char c ='A';bool b =true; cout <<"int: "<< i <<", float: "<< f <<", double: "<< d <<", char: "<< c <<", bool: "<< b << endl;return0;}输出:int: 42, float: 3.14, double: 2.718, char: A, bool: 1
测试代码:溢出测试 (overflow.cpp):
#include<iostream>usingnamespace std;intmain(){short s =32767;// max short s++; cout <<"Overflow short: "<< s << endl;// -32768 (signed wrap-around)return0;}警告:溢出是未定义行为,避免在生产代码中。
3.4 常量与输入输出
使用 const 关键字定义常量,不能修改。
constfloat gravity =9.81;// 定义常量// gravity = 10; // 错误使用 cin 和 cout 进行输入输出()。
实例 (io.cpp):
#include<iostream>usingnamespace std;intmain(){int number; cout <<"请输入一个数字: "; cin >> number;// 从用户输入读取数字 cout <<"你输入的数字是: "<< number << endl;// 输出用户输入的数字return0;}测试输入:5 输出:你输入的数字是: 5
更多实例:多输入 (multi_io.cpp):
#include<iostream>usingnamespace std;intmain(){ string name;int age; cout <<"Name and age: "; cin >> name >> age; cout <<"Hello, "<< name <<" ("<< age <<")"<< endl;return0;}测试:“Bob 25” 输出:Hello, Bob (25)
第四章:控制结构
4.1 条件语句
if 用于条件判断,else可选。switch 用于多分支,基于整数/枚举。
if 示例 (if.cpp):
#include<iostream>usingnamespace std;intmain(){int a =10;if(a >0){ cout <<"a 是正数"<< endl;}elseif(a <0){ cout <<"a 是负数"<< endl;}else{ cout <<"a 是零"<< endl;}return0;}输出:a 是正数
switch 示例 (switch.cpp):
#include<iostream>usingnamespace std;intmain(){int day =4;switch(day){case1: cout <<"星期一"<< endl;break;case2: cout <<"星期二"<< endl;break;default: cout <<"不是工作日"<< endl;}return0;}输出:不是工作日
更多实例:嵌套if (nested_if.cpp):
#include<iostream>usingnamespace std;intmain(){int score =85;if(score >=60){if(score >=90) cout <<"A"<< endl;else cout <<"B"<< endl;}else{ cout <<"F"<< endl;}return0;}输出:B
4.2 循环结构
for 用于已知迭代次数,while/do-while 用于条件循环。
for 示例 (for.cpp):
#include<iostream>usingnamespace std;intmain(){for(int i =0; i <5; i++){ cout <<"i 的值: "<< i << endl;}return0;}输出:0到4每行一个
while 示例 (while.cpp):
#include<iostream>usingnamespace std;intmain(){int j =0;while(j <5){ cout <<"j 的值: "<< j << endl; j++;}return0;}类似输出
do-while 示例 (do_while.cpp):
#include<iostream>usingnamespace std;intmain(){int k =0;do{ cout <<"k 的值: "<< k << endl; k++;}while(k <5);return0;}类似,但至少执行一次
更多实例:无限循环避免 (loop_test.cpp):
#include<iostream>usingnamespace std;intmain(){int count =0;while(true){// 无限循环 cout << count << endl;if(++count >5)break;// 退出}return0;}输出:0到5
测试代码:嵌套循环 (nested_loop.cpp):
#include<iostream>usingnamespace std;intmain(){for(int i =1; i <=3; i++){for(int j =1; j <=3; j++){ cout << i <<"*"<< j <<"="<< i*j <<" ";} cout << endl;}return0;}输出:乘法表
第五章:函数
5.1 函数的定义与调用
函数模块化代码。定义:返回类型 函数名(参数) {体}
示例 (add.cpp):
#include<iostream>usingnamespace std;intadd(int a,int b){return a + b;}intmain(){int result =add(5,3); cout <<"5 + 3 = "<< result << endl;return0;}输出:8
5.2 参数传递方式
值传递:拷贝,不改原值。引用:&,可改原值。
引用示例 (modify.cpp):
#include<iostream>usingnamespace std;voidmodify(int&num){ num +=10;}intmain(){int x =5;modify(x); cout <<"x 的值: "<< x << endl;// 15return0;}更多实例:常量引用避免拷贝大对象 (const_ref.cpp):
#include<iostream>#include<string>usingnamespace std;voidprint(const string& s){// 不可改s cout << s << endl;}intmain(){ string msg ="Hello";print(msg);return0;}输出:Hello
5.3 函数重载
同名不同参数。
示例 (multiply.cpp):
#include<iostream>usingnamespace std;floatmultiply(float a,float b){return a * b;}intmultiply(int a,int b){return a * b;}intmain(){ cout <<multiply(2.5f,3.0f)<< endl;// 7.5 cout <<multiply(2,3)<< endl;// 6return0;}5.4 默认参数与 inline 函数
默认参数:可选。inline:建议内联优化。
默认示例 (greet.cpp):
#include<iostream>#include<string>usingnamespace std;voidgreet(string name ="World"){ cout <<"Hello, "<< name <<"!"<< endl;}intmain(){greet();// Hello, World!greet("Alice");// Hello, Alice!return0;}inline 示例 (square.cpp):
#include<iostream>usingnamespace std;inlineintsquare(int x){return x * x;}intmain(){ cout <<square(4)<< endl;// 16return0;}5.5 Lambda表达式与函数对象
Lambda:匿名函数 捕获{体}
示例 (lambda.cpp):
#include<iostream>usingnamespace std;intmain(){auto add =[](int a,int b){return a + b;}; cout <<"Lambda add: "<<add(5,3)<< endl;// 8return0;}更多实例:捕获变量 (capture_lambda.cpp):
#include<iostream>usingnamespace std;intmain(){int x =10;auto inc =[x](int y){return x + y;};// 捕获x值 cout <<inc(5)<< endl;// 15return0;}测试代码:Lambda在STL中使用 (sort_lambda.cpp):
#include<iostream>#include<vector>#include<algorithm>usingnamespace std;intmain(){ vector<int> v ={3,1,4,1,5};sort(v.begin(), v.end(),[](int a,int b){return a < b;});for(int n : v) cout << n <<" ";// 1 1 3 4 5 cout << endl;return0;}第六章:数组与字符串
6.1 一维数组与多维数组
数组:固定大小元素集合。
一维示例 (array1d.cpp):
#include<iostream>usingnamespace std;intmain(){int arr[5]={1,2,3,4,5};for(int i =0; i <5; i++){ cout << arr[i]<<" ";} cout << endl;return0;}输出:1 2 3 4 5
多维示例 (array2d.cpp):
#include<iostream>usingnamespace std;intmain(){int matrix[2][3]={{1,2,3},{4,5,6}};for(int i =0; i <2; i++){for(int j =0; j <3; j++){ cout << matrix[i][j]<<" ";} cout << endl;}return0;}输出:1 2 3 \n4 5 6
更多实例:动态大小 (vector更好,但用array) (dynamic_array.cpp):
使用std::array (C++11):
#include<iostream>#include<array>usingnamespace std;intmain(){ array<int,5> arr ={1,2,3,4,5};for(auto& elem : arr) cout << elem <<" "; cout << endl;return0;}6.2 字符串的处理
C风格:char[] 以\0结束。C++:std::string 动态。
示例 (string.cpp):
#include<iostream>#include<string>usingnamespace std;intmain(){ string str ="Hello, World!"; cout <<"字符串长度: "<< str.length()<< endl;// 13return0;}6.3 常用字符串函数
+=连接,substr、find等。
示例 (string_ops.cpp):
#include<iostream>#include<string>usingnamespace std;intmain(){ string str ="Hello"; str +=" World";// 连接 cout << str << endl;// Hello World cout << str.substr(0,5)<< endl;// Hello size_t pos = str.find("World"); cout <<"Found at: "<< pos << endl;// 6return0;}测试代码:C风格 vs C++ (c_string.cpp):
#include<iostream>#include<cstring>usingnamespace std;intmain(){char cstr[20]="Hello";strcat(cstr," World");// 需要足够空间 cout << cstr << endl;return0;}警告:C风格易缓冲区溢出。
第七章:指针与引用
7.1 指针的概念与使用
指针存储地址,*解引用,&取址。
示例 (pointer.cpp):
#include<iostream>usingnamespace std;intmain(){int a =10;int*p =&a; cout <<"a 的值: "<<*p << endl;// 10return0;}7.2 指针与数组的关系
数组名是首元素指针。
示例 (ptr_array.cpp):
#include<iostream>usingnamespace std;intmain(){int arr[3]={1,2,3};int*p = arr; cout <<*(p +1)<< endl;// 2return0;}更多实例:指针算术 (ptr_arith.cpp):
#include<iostream>usingnamespace std;intmain(){int arr[5]={10,20,30,40,50};int* p = arr;for(int i =0; i <5; i++){ cout <<*(p + i)<<" ";// 10 20 30 40 50} cout << endl;return0;}7.3 引用的概念与使用
引用是别名,必须初始化,不可改绑。
示例 (ref.cpp):
#include<iostream>usingnamespace std;intmain(){int b =20;int&r = b; r =30; cout <<"b 的值: "<< b << endl;// 30return0;}7.4 指针与动态内存分配
new分配,delete释放。避免泄漏。
示例 (dynamic.cpp):
#include<iostream>usingnamespace std;intmain(){int*ptr =newint;*ptr =42; cout <<"动态内存中的值: "<<*ptr << endl;// 42delete ptr;return0;}测试代码:动态数组 (dyn_array.cpp):
#include<iostream>usingnamespace std;intmain(){int size =5;int* arr =newint[size];for(int i =0; i < size; i++) arr[i]= i *10;for(int i =0; i < size; i++) cout << arr[i]<<" ";// 0 10 20 30 40 cout << endl;delete[] arr;// 数组用[]return0;}第八章:结构体与联合体
8.1 结构体的定义与使用
struct 组合数据。
示例 (struct.cpp):
#include<iostream>#include<string>usingnamespace std;structPerson{ string name;int age;};intmain(){ Person p; p.name ="Alice"; p.age =30; cout <<"姓名: "<< p.name <<", 年龄: "<< p.age << endl;return0;}8.2 结构体数组
示例 (struct_array.cpp):
#include<iostream>#include<string>usingnamespace std;structPerson{ string name;int age;};intmain(){ Person people[2]={{"Alice",30},{"Bob",25}};for(int i =0; i <2; i++){ cout <<"姓名: "<< people[i].name <<", 年龄: "<< people[i].age << endl;}return0;}更多实例:嵌套struct (nested_struct.cpp):
#include<iostream>#include<string>usingnamespace std;structAddress{ string city;};structPerson{ string name; Address addr;};intmain(){ Person p ={"Alice",{"New York"}}; cout << p.name <<" in "<< p.addr.city << endl;return0;}8.3 联合体的定义与使用
union 共享内存。
示例 (union.cpp):
#include<iostream>usingnamespace std;union Data {int intValue;float floatValue;};intmain(){ Data data; data.intValue =10; cout <<"整数值: "<< data.intValue << endl;// 10 data.floatValue =5.5; cout <<"浮点值: "<< data.floatValue << endl;// 5.5 (intValue覆盖)return0;}警告:同时读写不同成员是未定义行为。
8.4 枚举类型的使用
示例 (enum.cpp):
#include<iostream>usingnamespace std;enumColor{ RED, GREEN, BLUE };intmain(){ Color c = GREEN; cout <<"选择的颜色值: "<< c << endl;// 1return0;}测试代码:强类型enum (enum_class.cpp C++11):
#include<iostream>usingnamespace std;enumclassFruit{ Apple, Banana };intmain(){ Fruit f = Fruit::Apple; cout <<static_cast<int>(f)<< endl;// 0 (需转换)return0;}第九章:类与对象
9.1 面向对象的基本概念
类:数据+方法蓝图。对象:实例。封装、继承、多态。
9.2 类的定义与对象的创建
示例 (class.cpp):
#include<iostream>#include<string>usingnamespace std;classCar{public: string brand;int year;voiddisplay(){ cout <<"品牌: "<< brand <<", 年份: "<< year << endl;}};intmain(){ Car myCar; myCar.brand ="Toyota"; myCar.year =2020; myCar.display();return0;}9.3 构造函数与析构函数
构造函数初始化,析构清理。
示例 (ctor_dtor.cpp):
#include<iostream>usingnamespace std;classPoint{public:int x, y;Point(int xVal,int yVal):x(xVal),y(yVal){ cout <<"Constructed"<< endl;}~Point(){ cout <<"Destructed"<< endl;}};intmain(){ Point p(10,20);return0;}输出:Constructed \n Destructed
更多实例:默认ctor (default_ctor.cpp):
#include<iostream>usingnamespace std;classBox{public:int size;Box():size(0){}// 默认};intmain(){ Box b; cout << b.size << endl;// 0return0;}9.4 成员函数与属性
示例 (circle.cpp):
#include<iostream>usingnamespace std;classCircle{public:double radius;doublearea(){return3.14159* radius * radius;}};intmain(){ Circle c; c.radius =5; cout <<"圆的面积: "<< c.area()<< endl;// ~78.54return0;}9.5 访问控制
public:外部可访。private:仅类内。protected:继承时。
示例 (access.cpp):
#include<iostream>usingnamespace std;classBox{private:double width;public:voidsetWidth(double w){ width = w;}doublegetWidth(){return width;}};intmain(){ Box b; b.setWidth(10.0); cout << b.getWidth()<< endl;// 10// b.width = 5; // 错误return0;}测试代码:friend函数 (friend.cpp):
#include<iostream>usingnamespace std;classSecret{private:int val;friendvoidreveal(Secret& s);public:Secret(int v):val(v){}};voidreveal(Secret& s){ cout << s.val << endl;// 可访private}intmain(){ Secret s(42);reveal(s);// 42return0;}第十章:继承与多态
10.1 继承的概念与实现
继承:派生类从基类获取属性/方法。
示例 (inherit.cpp):
#include<iostream>usingnamespace std;classAnimal{public:voideat(){ cout <<"Eating..."<< endl;}};classDog:publicAnimal{public:voidbark(){ cout <<"Barking..."<< endl;}};intmain(){ Dog d; d.eat(); d.bark();return0;}输出:Eating… \n Barking…
10.2 基类与派生类
基类公共,派生扩展。访问:public继承保持访问级。
更多实例:protected (protected_inherit.cpp):
#include<iostream>usingnamespace std;classBase{protected:int secret =42;};classDerived:publicBase{public:voidshow(){ cout << secret << endl;}// 可访protected};intmain(){ Derived d; d.show();// 42return0;}10.3 虚函数与多态
virtual允许重写,override显式。
示例 (poly.cpp):
#include<iostream>usingnamespace std;classBase{public:virtualvoidshow(){ cout <<"Base class"<< endl;}};classDerived:publicBase{public:voidshow()override{ cout <<"Derived class"<< endl;}};intmain(){ Base* b =newDerived(); b->show();// Derived classdelete b;return0;}10.4 多态的实现
通过指针/引用实现运行时多态。
示例 (poly_ptr.cpp):
#include<iostream>usingnamespace std;classShape{public:virtualdoublearea()=0;// 纯虚,抽象类};classRect:publicShape{double w, h;public:Rect(double ww,double hh):w(ww),h(hh){}doublearea()override{return w * h;}};intmain(){ Shape* s =newRect(3,4); cout << s->area()<< endl;// 12delete s;return0;}测试代码:多继承 (multi_inherit.cpp):
#include<iostream>usingnamespace std;classA{public:voidfoo(){ cout <<"A"<< endl;}};classB{public:voidfoo(){ cout <<"B"<< endl;}};classC:publicA,publicB{};intmain(){ C c; c.A::foo();// A (解决歧义)return0;}以下是为你的 C++ 教程 增加的 更多练习题(中文版),覆盖从第三章到第十章的主要内容。每章提供 3–6 道练习,由易到难排列,包含:
- 基础练习(巩固基本概念)
- 进阶练习(结合多个知识点)
- 挑战练习(需要思考或调试)
建议读者先自己编写代码,再对照参考答案或运行验证。
第三章:基本语法 - 练习
- 基础:编写程序,声明以下变量并输出它们的值和 sizeof 大小:
- bool isStudent = true;
- char grade = ‘A’;
- unsigned int score = 88;
- long long population = 1400000000LL;
- double pi = 3.1415926535;
- 基础:写一个程序,让用户输入两个整数,输出它们的和、差、积、商(整数除法)和余数。
- 进阶:编写程序,输入一个浮点数(摄氏温度),转换为华氏温度并输出,结果保留两位小数。
公式:华氏 = 摄氏 × 9/5 + 32 - 进阶:输入一个三位数,输出它的各位数字之和。例如输入 153,输出 1+5+3=9。
- 挑战:写一个程序,输入年份,判断是否为闰年(能被4整除但不能被100整除,或者能被400整除)。
第四章:控制结构 - 练习
- 基础:输入一个整数,判断它是正数、负数还是零。
- 基础:输入 1~7 的数字,输出对应的星期几(1=星期一,7=星期日),其他数字输出“输入错误”。
- 进阶:编写成绩分级程序:
- ≥90 → 优秀
- ≥80 → 良好
- ≥70 → 中等
- ≥60 → 及格
- <60 → 不及格
- 进阶:用 for 循环输出 9×9 乘法表(格式整齐,可选:只输出上三角)。
- 挑战:用 while 循环实现:猜数字游戏(程序随机生成 1~100 的数,用户猜,提示太大/太小,最多 10 次机会)。
挑战:用嵌套循环输出以下图案(n=5):
* ** *** **** ***** 第五章:函数 - 练习
- 基础:编写函数 isEven(int n),返回 bool 判断 n 是否为偶数。在 main 中测试 10 个数字。
- 基础:编写函数 max3(int a, int b, int c),返回三个数中最大的。在 main 中从键盘输入三个数并调用。
- 进阶:编写函数 factorial(int n) 计算 n 的阶乘(用 long long 防止溢出过快)。在 main 中输出 1! 到 20!。
- 进阶:编写两个重载函数:
- double calcArea(double r) // 圆面积
- double calcArea(double w, double h) // 矩形面积
在 main 中分别调用。
- 挑战:用递归实现斐波那契数列第 n 项(fib(n) = fib(n-1) + fib(n-2)),并用循环版本对比。注意:n 较大时递归会栈溢出。
挑战:编写函数 void printTriangle(int n),用 * 打印 n 行等腰三角形。例如 n=4:
* *** ***** ******* 第六章:数组与字符串 - 练习
- 基础:声明一个含 10 个元素的 int 数组,初始化为 1,3,5,…,19,输出所有元素及它们的和、平均值。
- 基础:输入 5 个整数存入数组,找出最大值、最小值及其下标。
- 进阶:编写函数 int countChar(const string& s, char ch),统计字符串中字符 ch 出现的次数。在 main 测试 “hello world” 中 ‘l’ 出现几次。
- 进阶:输入一行字符串(可含空格),统计其中元音字母(a,e,i,o,u,不区分大小写)的个数。
- 挑战:实现简单字符串反转函数 string reverseStr(string s),不使用算法库的 reverse。在 main 中测试 “C++ is fun!” → “!nuf si ++C”。
- 挑战:实现冒泡排序,对一个 int 数组从小到大排序,并输出排序前后的数组。
第七章:指针与引用 - 练习
- 基础:声明 int a = 100; 用指针 p 指向 a,输出 a 的值、地址、p 的值、*p 的值。
- 基础:用指针遍历 int arr[6] = {10,20,30,40,50,60}; 输出每个元素及地址。
- 进阶:编写函数 void swap(int& x, int& y),交换两个整数的值。在 main 中调用验证。
- 进阶:用 new 动态分配一个大小为用户输入的 int 数组,读入数据,计算平均值后 delete[] 释放。
- 挑战:实现函数 int* createArray(int size, int initValue),返回动态分配并初始化的数组指针。在 main 中使用并释放。
- 挑战:用指针实现字符串长度计算(不调用 length() 或 strlen()),即 int myStrLen(const char* str)。
第八章:结构体与联合体 - 练习
- 基础:定义结构体 Student { string name; int age; double score; },在 main 中创建两个学生对象并输出。
- 基础:定义枚举 enum WeekDay {MON=1, TUE, WED, THU, FRI, SAT, SUN}; 输入 1~7 输出对应星期。
- 进阶:定义结构体 Point { double x, y; },编写函数 double distance(Point a, Point b) 计算两点距离。
- 进阶:定义结构体 Book { string title; string author; double price; int year; },创建 Book 数组(3 本书),按价格从低到高排序后输出。
- 挑战:用 union 实现类型转换查看器:union Variant { int i; float f; char c[4]; }; 让用户输入 int,分别以 int、float、4 个 char 输出内存解释。
第九章:类与对象 - 练习
- 基础:定义类 Rectangle { private: double width, height; public: void set(double w, double h); double area(); double perimeter(); },实现成员函数。
- 基础:为 Rectangle 类添加构造函数 Rectangle(double w=1, double h=1) 和析构函数(输出“矩形销毁”)。
- 进阶:定义类 Student,包含私有成员:学号、姓名、语文成绩、数学成绩。提供:
- 构造函数
- 显示信息函数
- 计算总分函数
在 main 创建 3 个学生并显示。
- 挑战:实现一个简单的时钟类 Clock { private: int h,m,s; public: Clock(int hh=0,int mm=0,int ss=0); void tick(); void show(); },tick() 让秒+1并进位,show() 输出 hh:mm:ss 格式。
第十章:继承与多态 - 练习
- 基础:定义基类 Shape { public: virtual double area() const = 0; virtual ~Shape() {} }; 派生 Circle 和 Rectangle,实现 area()。
- 基础:在 main 中创建 Shape* 数组,放入不同派生类对象,遍历调用 area() 演示多态。
- 进阶:定义 Animal 基类(虚函数 speak()),派生 Cat(喵)、Dog(汪)、Bird(啾~),用基类指针数组调用 speak()。
- 挑战:实现银行账户系统:
- 基类 Account { protected: double balance; public: virtual void deposit(double amt); virtual bool withdraw(double amt); double getBalance() const; }
- 派生 SavingsAccount(有利息率,deposit 时加利息)
- 派生 CheckingAccount(透支额度)
在 main 中创建不同账户并操作。
这些练习由浅入深,覆盖语法、逻辑、设计等多个层面。建议:
- 先独立完成
- 遇到卡住再看提示或参考答案
- 完成后尝试优化代码(命名、可读性、异常处理等)
- 可以把完成的代码贴上来,我帮你 review 或指出改进点
第十一章:STL(标准模板库) - 练习
STL 是 C++ 的核心库,包括容器、算法、迭代器等。以下练习帮助你掌握 vector、list、map、set、algorithm 等组件。假设包含 <vector>、<list>、<map>、<set>、<algorithm> 等头文件,并使用 std:: 或 using namespace std;。
- 基础:创建一个 vector v,添加 1 到 10 的整数,输出所有元素、使用 size() 输出大小、使用 back() 输出最后一个元素。
- 基础:使用 map<string, int> 存储 5 个学生的姓名和分数,输入姓名查询分数(如果不存在输出“未找到”)。
- 进阶:用 vector 存储 10 个随机浮点数(用 rand() 生成),用 sort() 排序后输出,再用 find() 查找特定值的位置。
- 进阶:用 list l,添加 5 个元素,在中间插入一个新元素(用 iterator),然后用 erase() 删除第一个元素,输出列表。
- 挑战:实现函数 template void removeDuplicates(vector& vec),移除 vector 中的重复元素(用 set 或 sort+unique)。测试 int 和 string 类型。
- 挑战:用 multimap<int, string> 存储多个键值对(键为分数,值为姓名),按分数降序输出所有姓名(用 greater)。
第十二章:模板 - 练习
模板允许泛型编程,支持函数模板和类模板。练习聚焦于模板定义、实例化和特化。
- 基础:编写函数模板 T max(T a, T b),返回较大值。在 main 中测试 int、double 和 string(需
<string>)。 - 基础:编写类模板 class Pair<T1, T2> { T1 first; T2 second; },添加构造函数和 getFirst/getSecond 方法。实例化 Pair<int, string>。
- 进阶:编写函数模板 void printArray(T arr[], int size),输出数组元素。测试 int 数组和 char 数组。
- 进阶:编写类模板 class Stack 使用 vector 实现 push、pop 和 top。处理空栈异常(用 if 检查)。
- 挑战:为 max 函数模板添加特化版本:当 T 为 const char* 时,使用 strcmp 比较字符串。测试 “apple” 和 “banana”。
- 挑战:实现模板元编程:模板 struct Factorial { static const int value = N * Factorial::value; }; 特化 Factorial<0> = 1。在 main 输出 Factorial<5>::value。
第十三章:智能指针 - 练习
智能指针(C++11+)管理动态内存,避免泄漏。包含 <memory>,焦点 unique_ptr、shared_ptr、weak_ptr。
- 基础:用 unique_ptr p 创建动态 int(值为 42),输出 *p,然后转移所有权到另一个 unique_ptr。
- 基础:用 shared_ptr sp 创建动态 double(值为 3.14),用 use_count() 输出引用计数,复制到另一个 shared_ptr 再检查计数。
- 进阶:编写函数 unique_ptr createString(const string& s),返回 new string(s)。在 main 中调用并输出。
- 进阶:用 shared_ptr 创建数组(make_shared<int[]>(5)),初始化为 1-5,输出每个元素(用 [] 或 get())。
- 挑战:用 weak_ptr 解决循环引用:定义类 Node { shared_ptr next; weak_ptr prev; },创建两个节点互相指向,检查 use_count() 不循环递增。
- 挑战:实现一个简单的资源管理类,使用 unique_ptr 管理文件指针(FILE*,用 fopen/fclose),在析构中自动关闭文件。测试写文件。
这些练习扩展了原教程的高级主题,帮助从入门过渡到实际应用。每个练习都可独立编译运行,建议使用 C++11 或更高标准(g++ -std=c++11)。
- 提示:对于模板和智能指针,注意编译时错误(如类型不匹配)和运行时行为(如引用计数)。
- 扩展建议:结合 STL 和模板,如模板容器 vector 的自定义类使用。
以下是为你的 C++ 教程 后半部分(第十一章~第十八章)提供的 详细扩展版本。
每个章节都保留原有核心内容,并大幅增加: - 更深入的解释
- 常见陷阱与注意事项
- 更丰富的示例代码(包含完整可运行程序)
- 测试用例与输出示例
- 部分章节增加小练习提示(可用于读者自测)
第十一章:模板与泛型编程(扩展版)
11.1 函数模板
函数模板是最常见的泛型编程工具。编译器会在调用时根据实参类型自动生成具体函数(模板实例化)。
#include<iostream>usingnamespace std;// 基本加法模板template<typenameT> T add(T a, T b){return a + b;}// 支持不同类型混合(需要显式指定或让编译器推导)template<typenameT,typenameU>automixedAdd(T a, U b)->decltype(a + b){// C++11 trailing return typereturn a + b;}intmain(){ cout <<"int: "<<add(5,10)<< endl;// 15 cout <<"double: "<<add(5.5,3.5)<< endl;// 9 cout <<"string: "<<add(string("A"),string("B"))<< endl;// 混合类型 cout <<"mixed int+double: "<<mixedAdd(10,3.14)<< endl; cout <<"mixed double+int: "<<mixedAdd(2.718,100)<< endl;return0;}常见问题:
- 当 T 不支持 + 运算符时会编译失败
- 模板参数推导失败时需显式指定:
add<double>(3, 4.0)
11.2 类模板
类模板可以有多个模板参数,支持默认参数(C++11后)。
#include<iostream>#include<string>usingnamespace std;template<typenameT=int,typenameU=double>// 默认参数classPair{private: T first; U second;public:Pair(T a, U b):first(a),second(b){} T getFirst()const{return first;} U getSecond()const{return second;}voidprint()const{ cout <<"Pair<"<<typeid(T).name()<<","<<typeid(U).name()<<">: "<< first <<", "<< second << endl;}};intmain(){ Pair<>p1(100,3.14);// 使用默认类型 Pair<string>p2("Left",2025.0); Pair<double,int>p3(9.8,42); p1.print(); p2.print(); p3.print();return0;}11.3 模板特化(全特化 & 偏特化)
#include<iostream>#include<string>#include<cstring>// strcmpusingnamespace std;// 通用模板template<typenameT>classBox{ T value;public:Box(T v):value(v){}voidshow()const{ cout <<"通用: "<< value << endl;}};// 全特化 - string 类型template<>classBox<string>{ string value;public:Box(string v):value(v){}voidshow()const{ cout <<"特化(string): "<< value << endl;}};// 偏特化 - 指针类型template<typenameT>classBox<T*>{ T* ptr;public:Box(T* p):ptr(p){}voidshow()const{if(ptr) cout <<"偏特化(指针): "<<*ptr <<" (地址:"<< ptr <<")"<< endl;else cout <<"偏特化(空指针)"<< endl;}};intmain(){ Box<int>b1(2026); Box<string>b2("特化演示");int x =100; Box<int*>b3(&x); b1.show();// 通用: 2026 b2.show();// 特化(string): 特化演示 b3.show();// 偏特化(指针): 100 (地址:...)return0;}11.4 STL 常用容器快速概览 + 示例
#include<iostream>#include<vector>#include<list>#include<deque>#include<set>#include<map>#include<unordered_map>#include<algorithm>usingnamespace std;intmain(){// vector - 动态数组 vector<int> v ={10,20,5,30}; v.push_back(40);sort(v.begin(), v.end()); cout <<"vector sorted: ";for(int x : v) cout << x <<" "; cout << endl;// set - 自动排序 + 唯一 set<string> words{"apple","banana","apple","cherry"}; cout <<"set: ";for(constauto& w : words) cout << w <<" "; cout << endl;// map - 键值对 map<int, string> id2name {{1,"Alice"},{3,"Bob"},{2,"Charlie"}}; id2name[5]="David"; cout <<"map:\n";for(constauto&[id, name]: id2name){// C++17 结构化绑定 cout <<" ID "<< id <<" -> "<< name << endl;}return0;}第十二章:异常处理(扩展版)
12.1–12.3 更完整的异常处理示例
#include<iostream>#include<stdexcept>#include<string>#include<vector>usingnamespace std;classInvalidIndex:publicruntime_error{public:InvalidIndex(const string& msg):runtime_error(msg){}};classSafeVector{private: vector<int> data;public:SafeVector(initializer_list<int> init):data(init){}int&at(size_t index){if(index >= data.size()){throwInvalidIndex("索引越界: "+to_string(index)+", 有效范围 [0,"+to_string(data.size()-1)+")");}return data[index];}constint&at(size_t index)const{returnconst_cast<SafeVector*>(this)->at(index);}};voiddivide(double a,double b){if(b ==0)throwdomain_error("除数不能为零"); cout << a <<" / "<< b <<" = "<< a/b << endl;}intmain(){try{divide(10,0);}catch(const domain_error& e){ cerr <<"数学错误: "<< e.what()<< endl;} SafeVector sv {10,20,30,40};try{ cout << sv.at(2)<< endl;// 30 cout << sv.at(10)<< endl;// 抛异常}catch(const InvalidIndex& e){ cerr <<"自定义异常: "<< e.what()<< endl;}catch(const exception& e){ cerr <<"其他标准异常: "<< e.what()<< endl;}catch(...){ cerr <<"未知异常捕获!"<< endl;}return0;}异常安全级别(重要概念):
- 基本保证
- 强保证(操作要么全成功,要么不改变状态)
- 不抛异常保证(noexcept)
第十三章:文件操作(扩展版)
13.4 更实用的文件读写示例(学生成绩管理系统)
#include<fstream>#include<iostream>#include<vector>#include<string>#include<iomanip>usingnamespace std;structStudent{ string name;int score;};boolsaveToFile(const vector<Student>& students,const string& filename){ ofstream fout(filename);if(!fout)returnfalse; fout << students.size()<< endl;for(constauto& s : students){ fout << s.name <<" "<< s.score << endl;}returntrue;}boolloadFromFile(vector<Student>& students,const string& filename){ ifstream fin(filename);if(!fin)returnfalse; students.clear(); size_t count; fin >> count; fin.ignore();// 吃掉换行for(size_t i =0; i < count;++i){ Student s;getline(fin, s.name,' '); fin >> s.score; fin.ignore(); students.push_back(s);}returntrue;}intmain(){ vector<Student> data {{"Alice",88},{"Bob",92},{"Charlie",75}};if(saveToFile(data,"students.txt")){ cout <<"保存成功\n";} vector<Student> loaded;if(loadFromFile(loaded,"students.txt")){ cout <<"\n从文件读取的内容:\n"; cout << left <<setw(12)<<"姓名"<<"分数\n";for(constauto& s : loaded){ cout << left <<setw(12)<< s.name << s.score << endl;}}return0;}第十四章:标准库与命名空间(补充)
常用算法快速记忆表:
| 算法 | 功能 | 示例 |
|---|---|---|
| sort | 排序 | sort(v.begin(), v.end()) |
| stable_sort | 稳定排序 | stable_sort(…) |
| find | 查找第一个匹配 | auto it = find(v.begin(), v.end(), 42) |
| count | 统计出现次数 | count(v.begin(), v.end(), 7) |
| transform | 对每个元素应用函数 | transform(v.begin(), v.end(), v.begin(), [](int x){return x*2;}) |
| accumulate | 累加(求和等) | accumulate(v.begin(), v.end(), 0) |
第十五章:高级特性(重点扩展)
15.1 智能指针完整对比示例
#include<iostream>#include<memory>#include<string>usingnamespace std;structPerson{ string name;Person(string n):name(move(n)){ cout << name <<" 构造\n";}~Person(){ cout << name <<" 析构\n";}};intmain(){ cout <<"=== unique_ptr ===\n";{ unique_ptr<Person>up(newPerson("独占者"));// unique_ptr<Person> up2 = up; // 错误!不可拷贝 unique_ptr<Person> up2 =move(up);// 所有权转移}// 这里自动析构 cout <<"\n=== shared_ptr ===\n";{ shared_ptr<Person> sp1 =make_shared<Person>("共享A");{ shared_ptr<Person> sp2 = sp1; cout <<"引用计数: "<< sp1.use_count()<< endl;// 2} cout <<"引用计数: "<< sp1.use_count()<< endl;// 1}// 最后一次析构 cout <<"\n=== weak_ptr 解决循环引用 ===\n";// (略,参考之前章节练习)return0;}15.4 C++20 概念(Concepts)简单示例
#include<iostream>#include<concepts>usingnamespace std;template<typenameT>conceptNumeric= is_arithmetic_v<T>;template<Numeric T> T square(T x){return x * x;}intmain(){ cout <<square(5)<< endl;// ok cout <<square(3.14)<< endl;// ok// square("hello"); // 编译错误:不满足 Numeric 概念return0;}第十六章:综合项目建议(简要)
推荐小型项目选题(由易到难)
- 命令行图书管理系统(增删改查 + 文件持久化)
- 简单计算器(支持括号、函数、历史记录)
- 文本加密解密工具(凯撒、维吉尼亚、文件读写)
- 学生成绩统计系统(支持排序、统计、导出 CSV)
- 简易贪吃蛇游戏(使用 console + 定时器)
每个项目建议使用:
- 类 + 继承
- STL 容器
- 文件读写
- 异常处理
- 智能指针