还剩48页未读,继续阅读
本资源只提供10页预览,全部文档请下载后查看!喜欢就下载吧,查找使用更方便
文本内容:
程序设计C++欢迎参加《程序设计》课程!本课程由周老师讲授,将在年C++NO2025春季学期开设我们将系统地学习编程语言的核心概念和实践应用,C++从基础语法到高级特性,帮助您掌握这门强大的编程语言本课程由编程语言与软件工程系精心设计,旨在培养学生扎实的编程基础和解决实际问题的能力无论您是计算机专业的学生,还是希望提升编程技能的爱好者,都能在这门课程中获得丰富的知识和实践经验让我们一起开启编程的精彩旅程!C++课程概述教学目标与学习成果课程安排与评分标准本课程旨在培养学生掌握C++编程的课程为期16周,每周3学时,包括理核心概念和技能,能够独立设计和实论讲授和上机实践评分标准平时现中小型软件系统学习成果包括作业30%,上机实验20%,期中项目掌握C++语法结构,理解面向对象编20%,期末考试30%出勤情况将作程范式,能够应用STL解决实际问题,为平时成绩的重要组成部分,无故缺以及具备基本的软件工程思维席超过三次将影响最终成绩教材与参考资源主教材《C++Primer》(第5版)参考书目《Effective C++》、《STL源码剖析》、《C++标准库》等此外,我们还将提供在线资源,包括MOOC课程、代码示例库和编程练习平台,以辅助学习本课程要求学生具备基本的计算机科学知识和一定的编程经验,特别是C语言基础如果您在这些方面有欠缺,建议在课程开始前进行一些预习和准备工作我们也将在开学初提供补充材料,帮助基础较弱的学生快速跟上简介C++11979年Bjarne Stroustrup在贝尔实验室创建了C withClasses,这是C++的前身,旨在将面向对象的特性引入C语言21983年语言正式命名为C++,++是C语言中的递增运算符,象征着C语言的增强版本31998年首个C++国际标准(C++98)发布,标志着C++成为一种成熟的编程语言42011年至今推出了C++
11、C++
14、C++17和C++20等重要更新,不断增加新特性和改进语言能力C++从C语言扩展而来,继承了C语言高效、灵活的特点,同时增加了面向对象编程的能力作为一种多范式编程语言,C++不仅支持面向对象编程,还支持过程式编程、泛型编程和函数式编程等多种编程范式,使它成为一种功能强大且应用广泛的编程语言的特点C++面向对象编程C++支持面向对象编程的三大特性封装、继承和多态通过类和对象的概念,可以创建模块化、可复用的代码,使复杂系统的开发和维护变得更加简单类的机制允许数据和操作数据的函数被绑定在一起,形成一个整体泛型编程支持通过模板机制,C++实现了强大的泛型编程能力,允许程序员编写可以处理不同数据类型的通用算法标准模板库STL是C++泛型编程的典范,提供了丰富的容器、算法和迭代器,大大提高了编程效率高性能与底层控制C++保留了C语言的高效性和对硬件的直接控制能力,同时提供了更高级的抽象它允许直接管理内存,进行底层操作,这使得C++在系统编程、游戏开发和高性能计算等领域具有优势标准库的丰富功能C++标准库提供了大量实用的工具,包括容器、算法、字符串处理、输入输出流、多线程支持等近年来的标准更新不断丰富库的功能,如智能指针、正则表达式、文件系统操作等,使编程变得更加便捷开发环境搭建常用IDE选择合适的集成开发环境IDE能显著提高编程效率Visual Studio是Windows平台最流行的选择,提供完整的开发工具链;CLion是跨平台的IDE,基于IntelliJ平台,提供智能代码补全;CodeBlocks是轻量级的开源IDE,适合初学者和小型项目编译器GCCGNU CompilerCollection是最常用的开源编译器,支持多种操作系统;Clang提供友好的错误消息和快速编译速度,是Apple平台的默认选择;MSVC是微软开发的编译器,与Visual Studio深度集成,对Windows平台优化良好构建工具CMake是跨平台的构建系统生成器,能为不同平台生成原生的构建文件;Make是传统的构建自动化工具,通过Makefile定义构建规则;现代IDE通常内置了项目构建系统,但了解这些工具对于大型项目开发非常重要除了上述工具外,掌握一些调试技巧也非常重要学会使用断点、监视变量、检查内存、分析调用栈等功能,能够快速定位和修复代码中的问题版本控制工具如Git也是现代软件开发的必备工具,建议同学们在学习C++的同时,也熟悉这些辅助工具的使用第一个程序C++示例程序结构分析Hello World指令引入必要的头文件•#include#include函数是程序的入口点•main用于向控制台输出信息int main{•std::cout//这是一个简单的C++程序每个语句以分号结束•std::coutHello,World!表示程序正常结束•return0std::endl;编译过程包括预处理、编译、链接三个阶段,最终生成可执return0;行文件良好的编码风格包括适当的缩进、命名规范和注释,}这对于代码的可读性和维护性至关重要这个简单的程序展示了程序的基本结构它包含头文件C++引入、主函数定义、输出语句和返回值注释使用标记,//帮助解释代码基本语法C++关键字语言预定义的保留字,不能用作标识符表达式与语句表达式产生值,语句执行操作代码块与作用域使用{}定义代码块,确定变量的可见范围命名空间避免名称冲突的机制C++是一种区分大小写的语言,这意味着标识符count和Count被视为不同的名称标识符可以包含字母、数字和下划线,但必须以字母或下划线开头,且不能使用C++的关键字命名空间是C++中组织代码的重要机制,标准库中的所有组件都定义在std命名空间内,使用时需要通过作用域解析运算符::访问,或使用using声明良好的编程实践包括合理组织代码结构,避免全局变量,适当使用命名空间,以及遵循一致的命名规范这些习惯能够使代码更加清晰、易于维护,并减少潜在的错误基本输入输出iostream库提供C++标准输入输出功能cin与cout标准输入流和标准输出流格式化输出控制输出的精度、宽度和对齐方式文件输入输出通过fstream库操作文件C++的输入输出系统基于流的概念,流是字符序列的抽象表示iostream库定义了cin(连接到标准输入设备,通常是键盘)和cout(连接到标准输出设备,通常是显示屏)对象这些对象与提取运算符和插入运算符一起使用,实现数据的输入和输出格式化输出可以通过操纵符(如std::setw、std::setprecision)或通过流的成员函数控制文件输入输出通过fstream库实现,它提供了ifstream(输入文件流)、ofstream(输出文件流)和fstream(输入输出文件流)类,使得读写文件变得简单高效数据类型
(一)类型关键字典型大小值范围整型int4字节-2,147,483,648到2,147,483,647短整型short2字节-32,768到32,767长整型long4-8字节依平台而定单精度浮点float4字节约±
3.4E±38(7位有效数字)双精度浮点double8字节约±
1.7E±308(15位有效数字)字符型char1字节-128到127或0到255布尔型bool1字节true或falseC++提供了多种基本数据类型,以满足不同的数据存储和计算需求整型(int、short、long、long long)用于表示整数值,具有不同的大小和范围在需要精确表示小数时,可以使用浮点型(float、double)字符型(char)用于表示单个字符,布尔型(bool)用于表示逻辑值(true或false)需要注意的是,各种数据类型的大小可能因编译器和平台而异,上表中列出的是最常见的情况使用sizeof运算符可以获取特定类型或变量在当前环境中的确切大小选择合适的数据类型对于程序的性能和内存使用效率有很大影响数据类型
(二)类型修饰符类型转换C++提供了几种类型修饰符,用于扩展或限类型转换是将一种数据类型的值转换为另一制基本数据类型的范围和行为常用的修饰种类型C++支持隐式转换(自动进行,可符包括signed(有符号,默认)、能导致精度损失)和显式转换(程序员明确unsigned(无符号)、long(扩展范围)、指定)C++11引入了四种命名的类型转换short(缩小范围)和const(常量)这些运算符static_cast、dynamic_cast、修饰符可以组合使用,如unsigned longint const_cast和reinterpret_cast,它们提供了更安全、更明确的类型转换机制类型推导C++11引入了auto关键字,它允许编译器根据初始化表达式自动推导变量的类型这简化了复杂类型的声明,特别是在使用模板和迭代器时decltype运算符可以获取表达式的类型,常用于模板编程C++14进一步增强了auto的功能,允许它用于函数返回类型推导sizeof运算符返回类型或变量的字节大小,是一个编译时运算符,不会实际计算其操作数了解类型的精确大小对于内存管理和性能优化很重要,特别是在处理大量数据或需要跨平台兼容性时在进行类型转换时应当谨慎,特别是涉及指针类型的转换,不当的转换可能导致难以调试的程序错误变量与常量变量声明与定义变量是程序中可以存储和修改的数据存储单元变量声明告诉编译器变量的类型和名称,而定义则为变量分配内存空间在C++中,声明可以不定义(使用extern关键字),但所有变量在使用前必须定义变量命名应遵循一定的规范,以提高代码可读性常量常量是程序执行期间值不能改变的数据使用const关键字声明常量,如const intMAX_VALUE=100;C++还支持constexpr关键字,用于声明可以在编译时计算的常量表达式常量的使用可以提高程序的安全性和可读性,同时允许编译器进行更多优化字面量字面量是程序中直接表示的值,如数字、字符和字符串C++支持多种字面量类型和表示方法,如整型字面量(123,0x7B)、浮点字面量(
3.14,
1.5e-10)、字符字面量(A)和字符串字面量(Hello)C++11引入了用户定义字面量,允许程序员自定义字面量的行为变量的作用域决定了变量在程序中的可见性,包括全局作用域、局部作用域和命名空间作用域生命周期指变量在内存中存在的时间段,分为自动生命周期(函数内的局部变量)、静态生命周期(使用static关键字声明的变量)和动态生命周期(使用new分配的内存)良好的编程实践包括限制变量的作用域,避免使用全局变量,合理使用常量,以及清晰的变量命名这些做法有助于减少程序中的错误,提高代码的可维护性运算符
(一)算术运算符关系运算符与逻辑运算符位运算符提供了基本的算术运算符用于数学计关系运算符用于比较操作数的值位运算符操作二进制位C++算等于按位与•==a==b•ab加法•+a+b不等于按位或•!=a!=b•|a|b减法•-a-b大于、大于等于按位异或•=•^a^b乘法•*a*b小于、小于等于按位取反•=•~~a除法(注意整数除法会截断小•/a/b左移(左移位)•an n逻辑运算符用于组合条件数部分)右移(右移位)•an n•取模%a%b(仅用于整数,求余•逻辑与expr1expr2位运算通常用于系统编程、硬件控制和优数)逻辑或•||expr1||expr2化特定算法自增或(前缀或后缀形•++++a a++逻辑非•!!expr式)自减或(前缀或后缀形式)•----a a--运算符
(二)赋值运算符条件运算符成员访问运算符赋值运算符用于将右操作数的值赋给左操作条件运算符:是C++中唯一的三元运算符,C++提供了几种访问类、结构体、联合体成数简单赋值运算符是=,如a=bC++形式为conditionexpr1:expr2如果条员的运算符点运算符.用于通过对象访问还提供了复合赋值运算符,将其他操作与赋件为真,则整个表达式的值为expr1的值,成员,如obj.member箭头运算符-用于值结合起来,如+=、-=、*=、/=、%=、=、否则为expr2的值这是if-else语句的简洁替通过指针访问成员,如ptr-member,等价=、=、^=、|=复合赋值通常效率更高,代方式,常用于简单的条件分支,如为变量于*ptr.member作用域解析运算符::用代码更简洁赋予条件值、返回条件值等场景于访问命名空间或类的成员运算符优先级决定了表达式中运算符的计算顺序例如,乘法和除法的优先级高于加法和减法,所以a+b*c会先计算b*c,然后再加上a运算符的结合性决定了相同优先级的运算符的计算顺序,大多数运算符是左结合的(从左到右计算),但赋值运算符、条件运算符等是右结合的(从右到左计算)了解运算符的优先级和结合性很重要,但在实际编程中,建议使用括号明确表达计算顺序,以提高代码可读性并避免错误例如,使用a+b*c明确表示先进行加法再进行乘法,比依赖运算符优先级规则更安全控制结构
(一)条件语句if语句最基本的条件语句,根据条件表达式的值决定是否执行代码块if condition{//如果条件为真,执行这里的代码}if-else语句在if语句的基础上增加了条件不满足时的分支if condition{//如果条件为真,执行这里的代码}else{//如果条件为假,执行这里的代码}if-else if-else链多条件判断,按顺序测试每个条件,执行第一个为真的分支if condition1{//如果条件1为真}else ifcondition2{//如果条件1为假,条件2为真}else{//如果条件1和条件2都为假}嵌套条件语句在条件语句内部再使用条件语句,处理更复杂的逻辑判断if outer_condition{if inner_condition{//两个条件都为真}}控制结构
(二)循环结构for循环while循环适用于已知循环次数的情况,包含初始化、条件和更在循环开始前检查条件,如果条件为假则不执行循环新三个部分体for初始化;条件;更新{while条件{//循环体//循环体}}do-while循环先执行循环体,然后检查条件,至少执行一次循环控制break语句用于跳出循环,continue语句用于跳过当前do{迭代,继续下一次循环//循环体}while条件;循环结构是实现重复执行代码块的基本方式选择合适的循环类型取决于具体需求for循环通常用于确定迭代次数的场景;while循环适用于基于条件的不确定次数循环;do-while循环确保循环体至少执行一次,适用于需要在检查条件前先执行操作的情况合理使用循环控制语句可以提高程序的灵活性break语句可以在满足特定条件时提前结束循环,避免不必要的迭代;continue语句可以跳过当前迭代中的剩余代码,直接进入下一次迭代在嵌套循环中,break和continue默认只影响最内层循环,如需控制外层循环,可以使用带标签的循环或重构代码结构控制结构
(三)分支结构switch语句多路分支结构,根据表达式值选择执行路径case与defaultcase标签定义匹配值,default处理默认情况goto语句无条件跳转到标记位置,慎用条件表达式三元运算符:提供简洁的条件判断switch语句是多路分支的有效工具,适合处理一个表达式有多个可能值的情况它的基本形式如下switch expression{case value1://当expression等于value1时执行break;case value2://当expression等于value2时执行break;default://当没有匹配的case时执行}表达式通常是整型或枚举类型,不支持浮点类型或字符串直接比较每个case后面必须是常量表达式break语句用于跳出switch结构,如果省略break,执行将会落入下一个case,这种行为称为贯穿虽然有时这种贯穿特性可以有意利用,但更多时候是程序错误的来源,因此建议总是显式使用breakgoto语句允许无条件跳转到程序中标记的位置,但使用不当会导致意大利面条式代码,破坏程序结构,通常应避免使用数组一维数组多维数组一维数组是相同类型元素的线性集合,在内存C++支持多维数组,如二维数组(矩阵)int中连续存储声明方式type name[size];,例matrix
[3]
[4];多维数组的初始化可以使用嵌套如int numbers
[5]数组可以在声明时初始化的{}int matrix
[2]
[3]={{1,2,3},{4,5,6}};在int numbers
[5]={1,2,3,4,5};或者使用{}初内存中,二维数组的元素以行优先顺序存储,始化器int numbers[]={1,2,3,4,5},编译即先填充第一行,再填充第二行,依此类推器会自动计算大小数组与指针在许多上下文中,数组名会自动转换为指向第一个元素的指针这种关系使得数组可以通过指针算术来访问例如,*arr+i等同于arr[i]但要注意,数组名不是完全等同于指针,它不能被赋值给另一个指针,也不能指向其他位置C++中的数组有一些重要限制和特性数组大小必须是常量表达式,在运行时不能改变;数组不会自动检查边界,越界访问可能导致严重的运行时错误;数组不能被直接赋值给另一个数组,需要逐元素复制;传递数组给函数时通常会退化为指针,失去大小信息现代C++编程中,标准库容器(如std::array和std::vector)通常是数组的更安全、更灵活的替代品std::array提供了固定大小数组的安全封装,而std::vector则提供了动态大小的数组功能然而,原始数组在特定场景下仍有其性能优势,尤其是在需要与C代码接口或追求极致性能的情况下字符串C风格字符串C++string类C风格字符串是以空字符\0结尾的字符数组声明方式C++标准库提供的string类是对C风格字符串的安全封装,提供了丰富的操作方法char str1
[10]=Hello;//显式指定大小char str2[]=World;//编译器计算大小#includeconst char*str3=C++;//字符串字面量std::string s1=Hello;std::string s2=World;std::string s3=s1++s2;//Hello World操作C风格字符串需要借助cstring(C++中为string.h)库中的函数,如strlen(获取长度)、strcpy(复制)、strcat(连接)、strcmp(比较)等这些函数不检查缓冲区边界,可能导致内存溢出和安全问题string类自动管理内存,支持动态大小调整,提供了丰富的成员函数,如length、substr、find、replace等它还重载了比较运算符(==,!=,,,=,=)和输入输出运算符(,),使字符串处理更加直观和安全在现代C++编程中,std::string类应该是处理文本的首选方式,因为它提供了内存安全保证,自动管理字符串大小,并且有丰富的操作函数C++17引入了string_view类,提供了字符串的非拥有引用,适用于需要只读访问字符串的场景,可以提高性能处理国际化文本时,应考虑使用wstring(宽字符串)或支持Unicode的库从C++11开始,C++还提供了u16string和u32string类型,分别用于UTF-16和UTF-32编码的字符串选择合适的字符串表示对于国际化应用程序非常重要函数
(一)基础函数声明函数定义函数调用函数声明告诉编译器函数名称、返回类型和参数列表,但不包函数定义包含了函数的完整实现,包括函数体函数定义提供函数调用是在程序中使用函数的过程调用函数时,程序控制含函数体也称为函数原型,通常放在头文件中了函数声明中指定的功能转移到函数代码,执行完后返回到调用点int addinta,int b;//函数声明int addinta,int b{int sum=add5,3;//函数调用return a+b;//函数定义}函数是C++程序的基本构建块,它们将代码组织成可重用的单元,提高了程序的模块化程度函数有四个主要组成部分返回类型、函数名、参数列表和函数体返回类型可以是任何有效的数据类型,包括void(表示不返回值)函数名应该清晰地表明函数的功能参数列表定义了函数接受的输入,可以为空函数体包含了函数的实现代码C++的函数调用过程涉及几个步骤参数的压栈(按从右到左的顺序)、保存当前执行位置、跳转到函数代码、执行函数体、计算返回值、恢复调用前的执行位置、清理栈了解这个过程有助于理解函数的性能影响和一些高级概念,如递归、内联函数和尾调用优化函数
(二)参数传递12值传递引用传递参数的副本传给函数,函数内的修改不影响原始值这是最简单的传递方式,适合小型对象,但对大型传递参数的引用,使函数可以直接修改原始值这提高了效率(避免复制),并允许函数修改调用者的对象可能效率较低变量34指针传递const参数传递指向参数的指针,功能类似引用传递,但语法不同,且可以传递NULL指针更灵活但使用更复杂使用const修饰参数表示函数不会修改该参数,这是一种良好的编程实践,特别是对于引用和指针参数在C++中,选择合适的参数传递方式对于函数的性能和行为有重要影响值传递适合小型对象或需要函数内部使用参数副本的情况由于参数会被复制,所以原始对象不会被修改,但对于大型对象,复制开销可能很大引用传递是传递大型对象的高效方式,因为它避免了复制如果不需要修改参数,应使用const引用,如void processconstBigObject obj这既高效又安全指针传递在需要表示可选参数(可以传递nullptr)或需要改变指针本身指向的情况下很有用在现代C++中,引用通常比指针更受欢迎,因为它们更安全、更易于使用,不需要解引用操作,也不能为空函数
(三)高级特性默认参数C++允许为函数参数指定默认值,使调用者可以省略这些参数默认参数必须从右到左指定,不能跳过参数在函数声明和定义中,默认参数只能指定一次默认参数的值可以是常量表达式、全局变量或函数调用使用默认参数可以简化函数调用,减少重载函数的数量函数重载函数重载允许定义多个同名但参数列表不同的函数编译器根据调用时提供的参数类型和数量选择合适的函数版本函数的返回类型不参与重载决议重载使代码更加直观,避免了为相似功能创建不同名称的函数注意避免模糊的重载,它们可能导致编译错误或不可预测的行为内联函数使用inline关键字建议编译器将函数调用替换为函数体,避免了函数调用的开销内联是一种优化提示,编译器可能忽略适合简短、频繁调用的函数过度使用内联可能导致代码膨胀,增加程序大小和缓存压力类的成员函数在类定义内部定义时隐式内联函数模板函数模板允许创建可处理多种数据类型的通用函数编译器根据调用时的参数类型生成具体的函数实例模板参数可以有默认值模板使代码更加通用和可维护,但也可能导致复杂的编译错误和代码膨胀函数模板是C++泛型编程的基础,在C++标准库中广泛使用指针
(一)基础概念指针的定义与声明指针操作指针是一种特殊的变量,用于存储内存地址指针的声明使用星号*运算符主要的指针操作包括•取地址运算符获取变量的内存地址int*ptr;//指向整数的指针•解引用运算符*访问指针指向的值double*dptr;//指向双精度浮点数的指针char*cptr;//指向字符的指针•指针初始化通常使用取地址运算符或动态内存分配•指针赋值将一个地址赋给指针变量指针的类型决定了它指向的数据类型,这影响指针的算术运算和解引用操作指针变量本身占用的内存int num=10;空间与平台相关,通常在32位系统上为4字节,64位系统上为8字节,与指向的数据类型无关int*ptr=#//ptr存储num的地址*ptr=20;//通过指针修改num的值coutnum;//输出20空指针是不指向任何有效内存位置的指针在C++11之前,通常使用NULL或0表示空指针,C++11引入了nullptr关键字作为空指针常量检查指针是否为空是避免解引用空指针导致程序崩溃的重要实践int*ptr=nullptr;if ptr!=nullptr{*ptr=10;//只有当ptr非空时才执行}野指针是指向已释放或无效内存位置的指针使用未初始化的指针或在对象生命周期结束后仍使用指向它的指针都可能导致野指针野指针是危险的,因为它们可能导致不可预测的行为、内存损坏或安全漏洞避免野指针的最佳实践包括始终初始化指针、在对象释放后将指针设为nullptr、使用智能指针自动管理内存指针
(二)指针运算指针算术指针算术是指针特有的运算方式,主要包括加法、减法和比较指针加上整数n会移动n个元素大小的距离,而不是简单地加上n个字节例如,如果p指向int(假设4字节),则p+1实际上将地址增加4个字节这种行为使得数组元素的遍历非常直观指针减法计算两个指针之间的元素数量,要求两个指针指向同一数组指针与数组在C++中,数组名在大多数上下文中会退化为指向首元素的指针这种关系使得指针和下标表示法可以互换使用arr[i]等价于*arr+i但数组名不完全等同于指针数组名不能被重新赋值,也不能对其应用自增/自减运算符此外,对数组名使用sizeof运算符会返回整个数组的大小,而对指针使用则返回指针本身的大小指针数组与多级指针指针数组是元素为指针的数组,声明形式为type*array[size]多级指针是指向指针的指针,如int**ptr(指向int指针的指针)多级指针在处理二维数组、字符串数组等复杂数据结构时非常有用例如,C风格的命令行参数argv就是一个字符指针数组,而字符串数组可以用char**表示多级指针使用时要注意解引用的顺序,每个*操作符都会减少一级指针指针的灵活性使其成为强大的工具,但也容易导致错误常见错误包括越界访问(访问数组边界外的内存)、使用未初始化的指针、解引用空指针等安全的指针使用需要仔细跟踪指针生命周期和有效范围在现代C++中,许多指针相关的任务可以通过更安全的抽象来完成,如标准库容器、迭代器和智能指针引用引用的概念引用与指针比较引用是一个变量的别名,提供了间接访问另一个对象的方式引用和指针都提供了间接访问变量的方式,但有重要区别引用在声明时必须初始化,一旦绑定到一个对象,就不能改引用必须初始化且不能重绑定,而指针可以随时修改指向;变其绑定引用声明使用符号,但不是地址运算符引用不占用额外存储空间(通常实现为常量指针),而指针是独立变量;引用不需要解引用操作,使用更直观;引用不能为空,而指针可以为nullptr;引用通常更安全,因为没有空引用或无效引用的问题int num=10;int ref=num;//ref是num的引用ref=20;//修改ref也会修改num右值引用C++11引入了右值引用(),专门绑定到临时对象(右值)右值引用是实现移动语义和完美转发的基础,大大提高了处理临时对象时的效率右值引用允许我们窃取临时对象的资源,而不是进行昂贵的复制操作std::vector getVector;//返回一个临时向量std::vector vec=getVector;//右值引用绑定临时对象常量引用(const引用)是指向常量的引用,形式为const Typename虽然称为常量引用,但实际上是引用到常量,即通过引用不能修改被引用的对象常量引用有一个重要特性它可以绑定到临时对象(右值),而非常量引用不能这使得常量引用成为函数参数的理想选择,尤其是当函数不需要修改参数且希望避免复制开销时引用在C++中有多种重要应用作为函数参数(避免复制开销)、作为函数返回值(允许函数调用出现在赋值语句左侧)、实现运算符重载(允许自然的语法)、在基于范围的for循环中(避免复制集合元素)等合理使用引用可以提高程序的性能和清晰度动态内存管理new和deleteC++使用new运算符在堆上分配内存,使用delete运算符释放内存new在堆上分配足够存储指定类型对象的内存,并返回指向该内存的指针delete释放指针指向的内存,使其可以被重用对象的构造函数在new操作时调用,析构函数在delete操作时调用int*p=new int10;//分配内存并初始化为10delete p;//释放内存p=nullptr;//防止悬空指针动态数组使用new[]分配动态数组,使用delete[]释放必须使用正确的形式释放内存,否则会导致未定义行为,尤其是对于包含非平凡析构函数的对象数组int*arr=new int
[5];//分配5个整数的数组delete[]arr;//释放数组内存内存泄漏内存泄漏发生在动态分配的内存未被释放的情况下常见原因包括丢失指向已分配内存的指针、忘记调用delete、循环引用、异常发生时没有正确释放资源等长时间运行的程序中的内存泄漏会导致可用内存减少,最终可能导致程序崩溃智能指针C++11引入了智能指针,自动管理动态内存的生命周期,大大减少内存泄漏风险主要包括unique_ptr(独占所有权,不可复制)、shared_ptr(共享所有权,引用计数)和weak_ptr(共享所有权但不增加引用计数,解决循环引用)std::unique_ptr upnewint10;std::shared_ptr sp=std::make_shared20;面向对象编程
(一)类的基础对象类的实例,封装数据和行为类定义对象的蓝图,包含数据成员和方法封装将数据和操作数据的方法绑定在一起访问控制private,protected,public限制成员的可见性类是C++面向对象编程的核心概念,它将数据和函数组合成一个单元,实现了封装原则一个类的定义通常包括数据成员(属性)和成员函数(方法)数据成员表示对象的状态,成员函数表示对象的行为类的定义使用class或struct关键字,唯一区别是默认访问权限class默认为private,struct默认为public访问控制是封装的实现机制,它通过限制类成员的可见性来保护数据的完整性public成员可以被任何代码访问;private成员只能被类自身的成员函数访问;protected成员可以被类自身和派生类的成员函数访问良好的封装实践通常是将数据成员设为private,并提供public的访问器(getter)和修改器(setter)方法来控制数据的访问和修改这样可以确保数据的一致性,实现数据验证,并允许将来改变实现而不影响类的接口面向对象编程
(二)构造与析构构造函数构造函数在对象创建时自动调用,用于初始化对象的状态构造函数的名称与类名相同,没有返回类型一个类可以有多个构造函数(重载),包括默认构造函数(无参数)、参数化构造函数和复制构造函数等如果不提供任何构造函数,编译器会生成一个默认构造函数复制构造函数复制构造函数用于从现有对象创建新对象,形式为ClassNameconst ClassName当对象通过值传递、返回或显式使用另一个对象初始化时会调用复制构造函数如果类管理资源(如内存、文件句柄),应该定义自己的复制构造函数,实现深复制,避免多个对象共享同一资源导致的问题初始化列表构造函数初始化列表是在函数体执行前初始化类成员的机制,语法为ClassName:member1value1,member2value2{}它比在函数体内赋值更高效,因为成员只被初始化一次,而不是先默认初始化再赋值对于常量成员、引用成员和没有默认构造函数的成员类,必须使用初始化列表析构函数析构函数在对象被销毁时自动调用,用于清理资源,如释放动态分配的内存、关闭文件等析构函数的名称是类名前加~,如~ClassName,没有参数和返回类型每个类只能有一个析构函数如果类管理资源,必须定义合适的析构函数,否则会导致资源泄漏面向对象编程
(三)类的高级特性静态成员静态数据成员属于类而非对象,所有类实例共享同一个静态成员变量静态成员函数不与具体对象关联,可以在没有对象实例的情况下调用,且不能访问非静态成员静态成员通常用于表示所有对象共享的属性或操作,如计数器、配置信息等静态数据成员必须在类外定义(除了C++17中的内联静态成员)友元友元机制允许函数或类访问声明它为友元的类的私有和保护成员友元声明使用friend关键字友元不是类的成员,不受访问控制规则约束友元打破了封装性,应谨慎使用,通常在需要紧密协作的类之间或为了提高性能的特定场景中使用常见场景包括运算符重载和需要访问两个类私有成员的辅助函数运算符重载运算符重载允许自定义类型的对象使用C++内置运算符,使代码更直观重载可以通过成员函数或全局函数实现,成员函数比全局函数少一个参数(左操作数是*this)常重载的运算符包括赋值=、相等比较==,!=、流操作,、算术运算符+,-,*,/等某些运算符如、||、,逗号不建议重载,因为它们有特殊的求值顺序成员函数指针是指向类成员函数的指针,允许在运行时选择调用哪个成员函数成员函数指针的语法比普通函数指针复杂,因为它必须指定类型和所属类class MyClass{public:void funcint x;};void MyClass::*mfpint=MyClass::func;//成员函数指针MyClass obj;obj.*mfp10;//通过对象调用成员函数指针在实现回调、命令模式、状态机等设计模式时非常有用,它提供了一种灵活的方式来间接调用成员函数C++11引入的std::function和lambda表达式提供了更现代、更易用的替代方案,但在某些低级或性能关键的场景中,成员函数指针仍有其用途继承继承的基本概念继承类型继承是面向对象编程的三大支柱之一,允许创建基于现有类的新类,从而实现代码复用和建立类层次结构在C++中,C++支持三种继承类型,影响派生类如何访问继承的成员从其他类(基类)派生的类称为派生类,它继承基类的成员,并可以添加新成员或修改继承的行为•公有继承public保持基类成员的访问级别•私有继承private所有继承的成员在派生类中变为私有class Base{//基类成员•保护继承protected公有成员变为保护成员,其他不变};公有继承表示是一个关系,最常用;私有继承表示使用一个关系,实现与组合类似;保护继承很少使用,适用于作为其他类的基类但不对外公开的中间类class Derived:public Base{//派生类成员,同时继承基类成员};C++支持多重继承,允许一个类从多个基类继承这提供了巨大的灵活性,但也带来了挑战,特别是菱形继承问题当一个类间接从同一个基类继承多次时,会创建基类的多个副本,导致歧义和资源浪费class A{public:intx;};class B:public A{};class C:public A{};class D:public B,public C{};//菱形继承,D有两个A的副本虚继承解决了菱形继承问题,确保共同基类只有一个实例class B:virtual publicA{};class C:virtual publicA{};class D:public B,public C{};//现在D只有一个A的实例虚继承虽然解决了多重继承的问题,但增加了实现复杂性和运行时开销因此,许多C++设计指南建议避免多重继承,或者仅在一个具体类和多个接口(纯虚类)的情况下使用多态
(一)基本概念静态绑定与动态绑定虚函数的概念虚函数表绑定是将函数调用与其实现关联的过程静态绑定虚函数是使用virtual关键字声明的成员函数,允许虚函数表vtable是C++实现动态绑定的内部机制(早期绑定)在编译时确定调用哪个函数,基于对派生类重写其行为当通过基类指针或引用调用虚每个包含虚函数的类都有一个虚函数表,包含指向象的静态类型动态绑定(晚期绑定)在运行时根函数时,会调用指向的对象的实际类型对应的函数该类虚函数实现的指针每个对象包含一个指向其据对象的实际类型决定调用哪个函数,通过虚函数版本,而不是基类版本这是C++实现多态的关键类虚函数表的指针vptr,通常位于对象内存布局的表实现C++默认使用静态绑定,使用virtual关键机制虚函数在基类中声明,在派生类中可以使用开始位置虚函数调用需要额外的间接寻址,因此字可以启用动态绑定override关键字明确标记重写,提高代码可读性和比普通函数调用略慢,但这种性能开销在现代硬件安全性上通常可以忽略运行时类型识别RTTI是C++提供的一组特性,允许在运行时确定对象的实际类型RTTI的主要组件包括•dynamic_cast运算符安全地将基类指针或引用转换为派生类指针或引用,如果转换无效则返回nullptr(指针)或抛出异常(引用)•typeid运算符返回表示对象类型的std::type_info对象,可以用来比较类型•std::type_info类包含类型信息,通过typeid获取RTTI只适用于包含至少一个虚函数的类,因为它需要动态类型信息虽然RTTI提供了强大的类型检查能力,但应谨慎使用,因为它可能表明设计问题,并且有一定的运行时开销在许多情况下,虚函数提供了更优雅的多态解决方案,无需显式类型检查多态
(二)实现机制纯虚函数抽象类接口实现纯虚函数是在基类中声明但没有实现的虚函数,通过在声明后添加=0标记virtual含有至少一个纯虚函数的类称为抽象类抽象类不能实例化,只能作为其他类的基类C++中的接口通常通过只包含纯虚函数的抽象类实现一个类可以实现多个接口(通void func=0;纯虚函数为派生类定义接口,强制派生类提供实现基类可以声明抽象类的主要目的是定义接口,建立类层次结构的框架派生类必须实现所有继承的过多重继承),提供极大的灵活性接口类通常以I前缀命名(如IDrawable),应纯虚函数的默认实现,派生类可以选择调用它void Derived::func{Base::func;}纯虚函数,否则也是抽象类抽象类可以包含普通成员和实现代码,为派生类提供共该有虚析构函数防止内存泄漏接口分离原则建议创建小而专注的接口,而不是大而同功能全的接口多态在实际编程中有广泛应用,以下是几个典型示例//图形编辑器中的形状层次结构class Shape{public:virtual void draw const=0;virtual double area const=0;virtual~Shape{}};class Circle:public Shape{private:double radius;public:Circledouble r:radiusr{}void drawconst override{/*实现圆的绘制*/}double areaconst override{return
3.14159*radius*radius;}};class Rectangle:public Shape{private:double width,height;public:Rectangledouble w,double h:widthw,heighth{}voiddrawconst override{/*实现矩形的绘制*/}doubleareaconst override{return width*height;}};//使用多态void drawShapesconststd::vector shapes{for constauto shape:shapes{shape-draw;//动态绑定到适当的draw实现}}异常处理try-catch块try块包含可能引发异常的代码,catch块处理特定类型的异常可以有多个catch块处理不同类型的异常,按顺序匹配catch...可以捕获任何类型的异常,通常放在最后异常匹配基于类型兼容性,派生类异常可以被基类异常处理程序捕获try{//可能抛出异常的代码}catch std::exception e{//处理标准异常}catch...{//处理任何其他异常}throw表达式throw语句用于引发异常,可以抛出任何类型的值抛出异常后,控制流会跳转到最近的匹配的catch块如果没有匹配的catch块,程序将调用std::terminate终止可以在catch块中使用throw;重新抛出当前异常,保留原始异常的所有信息,通常用于在处理过程中转发异常if value0{throw std::invalid_argumentValue cannotbe negative;}标准异常类C++标准库提供了异常类层次结构,基类是std::exception常用派生类包括std::logic_error(表示程序逻辑错误,如无效参数)、std::runtime_error(表示可能在运行时检测到的错误,如溢出)、std::bad_alloc(内存分配失败)等自定义异常类通常应继承自std::exception或其派生类,并重写what方法提供错误描述异常处理最佳实践异常应用于真正的异常情况,不应用于正常的控制流应捕获异常的引用,避免对象切片构造函数失败应抛出异常,析构函数不应抛出异常利用RAII(资源获取即初始化)确保资源在异常发生时也能正确释放函数应在其接口文档中声明可能抛出的异常类型(异常规范)模板
(一)函数模板模板的基本概念函数模板定义模板参数推导与特化模板是C++支持泛型编程的核心机制,允许定义独立函数模板的定义使用template关键字,后跟尖括号中调用函数模板时,编译器通常可以从参数类型自动推于特定类型的代码模板不是实际的函数或类,而是的模板参数列表,然后是函数定义导模板参数生成函数或类的蓝图编译器根据模板和使用它的具体类型生成实际代码,这个过程称为模板实例化template inti=max10,20;//T推导为模板解决了代码重复的问题,同时保持了类型安全,T maxTa,T b{int比宏或void*指针更安全、更灵活return aba:b;double d=max
3.14,
2.72;//T推导为}double模板特化允许为特定类型提供自定义实现这里的T是类型参数,可以在函数定义中作为类型使template用typename和class关键字在模板参数中是等价的const char*maxconst char*a,const函数模板可以有多个模板参数,也可以有非类型参数char*b{(如整数常量)和模板模板参数return strcmpa,b0a:b;}特化版本会优先于通用模板被选中C++还支持偏特化,允许特化部分模板参数模板
(二)类模板类模板定义类模板的定义与函数模板类似,使用template关键字和模板参数列表,但应用于整个类templateclass Stack{private:std::vector elements;public:void pushconst T value;T pop;bool emptyconst;};这定义了一个可以存储任何类型数据的栈模板类模板可以有多个模板参数,包括类型参数、非类型参数和模板模板参数类模板实例化使用类模板时,必须显式指定模板参数(与函数模板不同,编译器通常不能自动推导类模板的参数)Stack intStack;//整数栈Stack strStack;//字符串栈在模板参数指定后,编译器生成对应的类定义不同模板参数的实例化是不同的类型,彼此独立C++17引入了类模板参数推导,在某些情况下可以省略模板参数类模板的成员函数类模板的成员函数定义有两种方式在类内部定义(隐式内联)或在类外部定义在类外定义时,需要重复模板声明和模板参数templatevoid Stack::pushconstTvalue{elements.push_backvalue;}成员函数只有被调用时才会实例化,未使用的成员函数不会生成代码,这称为延迟实例化标准模板库()概述STL容器算法容器是存储数据集合的模板类,分为序列容器(如算法是操作容器中元素的函数模板,包括排序、搜索、vector,list)、关联容器(如map,set)和容器适配器转换、数值计算等STL算法设计为泛型,通过迭代器(如stack,queue)每种容器都有特定的性能特点和操作,与具体容器类型无关算法通常遵循函数式编程操作保证,选择合适的容器可以显著影响程序效率容理念,不修改数据结构本身,而是生成新结果(除了特器负责内存管理,自动处理元素的动态分配和释放定的_if版本)迭代器函数对象迭代器是连接容器和算法的桥梁,提供了访问容器元素函数对象(仿函数)是重载了运算符的类,可以像函的统一接口迭代器类似于指针,支持解引用*、递增数一样调用,但可以保存状态STL提供了许多预定义++等操作STL定义了不同类别的迭代器(输入、输的函数对象(如less,greater等),并支持通过适配器出、前向、双向、随机访问),具有不同的功能集容修改其行为C++11引入的lambda表达式简化了函数对器提供begin和end方法返回表示范围的迭代器对象的创建,成为了现代C++中使用STL的重要工具STL的设计理念基于泛型编程和算法与数据结构分离的原则它使用静态多态性(通过模板)而非动态多态性(通过虚函数),从而在提供灵活性的同时保持高效率STL组件之间松散耦合算法通过迭代器操作容器,不直接依赖于容器类型;容器管理存储,不关心存储的内容将如何被处理理解STL的设计理念对于有效使用C++至关重要使用STL可以大大减少自己实现数据结构和算法的需求,提高代码可靠性和性能现代C++开发中,熟练掌握STL已成为基本技能,它不仅提供了丰富的功能,也展示了C++模板和泛型编程的强大能力容器
(一)序列容器STL容器特点适用场景时间复杂度vector动态数组,连续存储,需要频繁随机访问,在随机访问O1,末尾增随机访问末尾增删元素删O1,中间增删Onlist双向链表,非连续存储频繁在任意位置插入删访问On,任意位置增除删O1deque双端队列,分段连续存需要在两端频繁操作随机访问O1,两端增储删O1,中间增删Onarray固定大小数组,连续存大小固定,需要高效访随机访问O1,不支持储问增删forward_list单向链表,非连续存储内存受限,只需向前遍访问On,任意位置增历删O1vector是最常用的STL容器,提供了类似数组的随机访问能力,但大小可以动态变化它在内存中连续存储元素,支持快速的索引访问和迭代当容量不足时,vector会重新分配更大的内存块并复制元素,这可能导致性能波动为了减少重新分配,可以使用reserve预先分配空间list实现为双向链表,每个元素包含指向前后元素的指针它支持在任意位置快速插入和删除,但不支持随机访问list的迭代器在元素插入或删除后仍然有效(除了被删除的元素),这是它相对于vector的一个优势deque提供了两端快速插入删除的能力,同时保持了接近vector的随机访问性能,但内部实现更复杂array是C++11引入的固定大小数组的包装,提供了STL容器接口,比原始数组更安全forward_list是单向链表,比list更节省内存,但功能有限容器
(二)关联容器STL有序关联容器有序关联容器基于红黑树实现,元素按键排序主要包括-set存储唯一键,按键排序-multiset允许重复键,按键排序-map存储键值对,按键排序,键唯一-multimap允许重复键的键值对,按键排序有序容器支持高效的查找、插入和删除操作,均为Olog n复杂度它们要求键类型必须定义小于运算符()或提供自定义比较函数迭代遍历的元素按键的升序排列无序关联容器C++11引入的无序关联容器基于哈希表实现,不维护元素顺序主要包括-unordered_set键唯一,无序-unordered_multiset允许重复键,无序-unordered_map键值对,键唯一,无序-unordered_multimap允许重复键的键值对,无序无序容器的查找、插入和删除操作平均为O1复杂度,最坏情况为On它们要求键类型提供哈希函数和相等比较运算符无序容器通常比有序容器更快,但不保证遍历顺序,且消耗更多内存容器适配器容器适配器是基于其他容器实现的特殊容器,提供了受限的接口以支持特定的数据结构-stack后进先出LIFO数据结构,默认基于deque-queue先进先出FIFO数据结构,默认基于deque-priority_queue优先队列,始终保持最大元素在顶部,默认基于vector实现的堆容器适配器不支持迭代器,只能通过其特定接口访问元素它们可以基于不同的底层容器实现,只要该容器支持所需的操作例如,stack可以基于vector、list或deque实现算法STL非修改序列算法这类算法不修改容器中的元素,用于查询和分析主要包括find(查找元素)、count(计数)、equal(比较序列)、search(搜索子序列)、for_each(对每个元素应用函数)等例如,find算法用于在范围内查找特定值,返回指向该值的迭代器或end迭代器auto it=std::findv.begin,v.end,42;if it!=v.end{std::coutFound:*itstd::endl;}修改序列算法这类算法会修改容器中的元素或容器结构主要包括copy(复制元素)、move(移动元素)、transform(转换元素)、replace(替换元素)、fill(填充元素)、remove(删除元素)、unique(删除重复元素)等例如,transform算法用于将一个范围内的元素转换为另一种形式std::transformv.begin,v.end,v.begin,[]int n{return n*2;};排序与相关算法这类算法用于排序和处理已排序范围主要包括sort(排序)、partial_sort(部分排序)、nth_element(找第n大元素)、binary_search(二分查找)、merge(合并有序序列)、partition(划分)等例如,sort算法用于对容器进行排序std::sortv.begin,v.end;//默认升序std::sortv.begin,v.end,std::greater;//降序数值算法这类算法用于数值计算,定义在numeric头文件中主要包括accumulate(累加)、inner_product(内积)、partial_sum(部分和)、adjacent_difference(相邻差)等例如,accumulate算法用于计算范围内元素的总和int sum=std::accumulatev.begin,v.end,0;迭代器随机访问迭代器最强大的迭代器类型,支持所有操作双向迭代器支持前向和后向移动,但不支持随机访问前向迭代器只能向前移动,支持多次访问同一元素输入/输出迭代器最基本的迭代器,只支持单次遍历迭代器是STL的核心概念,提供了容器元素的统一访问方式不同类型的迭代器支持不同级别的操作输入迭代器支持读取元素并前进(如istream_iterator);输出迭代器支持写入元素并前进(如ostream_iterator);前向迭代器支持多次读写同一元素并前进(如forward_list的迭代器);双向迭代器增加了后退能力(如list的迭代器);随机访问迭代器支持任意位置的直接访问和迭代器算术(如vector的迭代器)迭代器适配器修改迭代器的行为,包括reverse_iterator(反向遍历)、back_insert_iterator(在容器末尾插入)、front_insert_iterator(在容器开头插入)和insert_iterator(在指定位置插入)C++标准库提供了函数来创建这些适配器rbegin/rend(返回反向迭代器)、back_inserter、front_inserter和inserterC++20引入了范围库(Ranges),提供了更高级的迭代器抽象,简化了算法的使用自定义迭代器需要满足特定概念的要求,通常通过继承std::iterator或满足相应的概念来实现新特性
(一)C++11/14/17auto关键字lambda表达式范围for循环C++11增强了auto关键字,允许编译器从初始化表达lambda表达式是C++11引入的匿名函数,可以在需要C++11引入的范围for循环提供了一种简洁的方式来遍式推导变量类型这简化了复杂类型的声明,提高了函数对象的地方创建内联函数lambda由捕获列表、历容器或数组中的所有元素它的语法为for代码的可维护性和灵活性auto特别适用于迭代器、参数列表、返回类型(可选)、函数体组成捕获列element_declaration:container元素声明可以是lambda表达式返回类型、模板函数返回值等场景表指定如何访问外部变量[]不捕获,[=]按值捕获,按值或按引用,后者允许修改原始元素范围for循环C++14进一步允许auto用于函数返回类型推导和[]按引用捕获,[this]捕获this指针等C++14引入了适用于任何提供begin和end方法的容器,原生数lambda参数auto可能导致代码可读性下降,应谨慎泛型lambda(参数使用auto),C++17添加了组,以及初始化列表C++17改进了范围for循环,支使用,特别是在类型对理解代码行为很重要的情况下constexpr lambdalambda特别适合用作算法的谓词持初始化语句,类似于常规for循环或回调函数for autoelem:container{auto it=container.begin;//代替std::for_eachv.begin,v.end,elem*=2;//修改原始元素std::vector::iterator[]int n{n*=2;};}auto lambda=[]auto x{return x*2;};//C++14:泛型lambdanullptr关键字C++11引入nullptr代替NULL或0表示空指针,解决了C++中空指针常量的类型安全问题nullptr的类型是std::nullptr_t,可以隐式转换为任何指针类型,但不能转换为整数类型,这避免了意外的整数转换使用nullptr可以改善函数重载解析,使代码意图更明确,减少潜在错误现代C++编程应始终使用nullptr而非NULL或0表示空指针void funcint;void funcchar*;funcNULL;//调用funcint,可能不是预期的funcnullptr;//明确调用funcchar*新特性
(二)C++11/14/17移动语义与右值引用智能指针C++11引入的移动语义是一个重要优化,允许窃取临时对象的资源,避免不必要的复制这通过右值引用T实现,C++11标准化了三种智能指针,简化内存管理并避免内存泄漏它可以绑定到临时对象移动构造函数和移动赋值运算符接受右值引用参数,负责接管资源•std::unique_ptr独占所有权模型,不可复制,但可以移动•std::shared_ptr共享所有权模型,使用引用计数class MyClass{public:•std::weak_ptr共享所有权但不增加引用计数,解决循环引用//移动构造函数创建智能指针应使用工厂函数MyClassMyClass othernoexcept:dataother.data{auto up=std::make_unique42;//C++14other.data=nullptr;//防止双重释放auto sp=std::make_shared42;//C++11}private:int*data;};这些函数比直接使用new更安全,还能优化std::shared_ptr的实现std::move将左值转换为右值引用,显式启用移动语义;std::forward在保持参数的值类别(左值或右值)的同时转发参数C++11引入的可变参数模板允许函数和类接受任意数量的模板参数,极大地提高了泛型编程的能力templatevoid funcArgs...args{//处理任意数量的参数}这里的Args是一个模板参数包,args是一个函数参数包展开参数包可以使用递归模板实例化、折叠表达式(C++17)或std::index_sequence等技术可变参数模板是实现完美转发的基础,也是std::tuple、std::function、容器emplace方法等标准库组件的关键C++17进一步简化了可变参数编程,引入了折叠表达式,允许对参数包应用二元运算符args+...将展开为arg1+arg2+arg3+...并发编程线程创建与管理互斥量与锁C++11引入了标准线程库,提供了创建和管理线程的工具std::thread类表示一个执行互斥量(mutex)用于保护共享数据,防止多线程同时访问导致的数据竞争C++提供线程,可以用函数、函数对象或lambda表达式构造了多种互斥量•std::mutex基本互斥量std::thread t[]{•std::recursive_mutex允许同一线程多次获取的互斥量//线程执行的代码for inti=0;i10;++i{•std::timed_mutex/std::recursive_timed_mutex支持超时的互斥量std::coutistd::endl;•std::shared_mutex(C++17)支持共享(读)和独占(写)模式}直接使用互斥量的lock/unlock方法容易出错,应使用RAII封装};•std::lock_guard简单的RAII锁•std::unique_lock更灵活的RAII锁,支持延迟锁定、超时等线程创建后立即开始执行join方法等待线程完成,detach方法分离线程使其独立运•std::shared_lock(C++14)共享模式的RAII锁行线程对象不可复制,但可以移动如果线程对象销毁时既未join也未detach,程序•std::scoped_lock(C++17)同时锁定多个互斥量,避免死锁会终止条件变量(std::condition_variable)用于线程间通信,允许线程等待特定条件满足一个线程调用wait等待条件,另一个线程在条件满足时调用notify_one或notify_all唤醒等待线程条件变量通常与互斥量一起使用,确保条件检查的原子性C++11引入的异步任务机制通过std::future和std::promise实现线程间的结果传递std::async函数简化了异步任务的创建,可以在新线程、线程池或当前线程中执行函数,并返回std::future用于获取结果std::packaged_task包装一个可调用对象,将其结果存储在std::future中这些工具使得编写高层次的并发代码变得简单,隐藏了线程管理的复杂性内存模型与原子操作C++内存模型原子类型C++11引入了正式的内存模型,定义了多线程程序中内存访问的行为和同步规则std::atomic模板提供了泛型原子类型,T可以是整型、指针或bool原子类型上的操内存模型解决了指令重排、可见性和缓存一致性等问题,为并发程序提供了可靠的作保证是原子的,不会被其他线程中断标准库也提供了特定类型的原子类型别名,基础它定义了什么是数据竞争(未同步的并发访问,至少一个是写操作),以及如std::atomic_int、std::atomic_bool等原子类型支持基本操作如load(读取)、如何通过同步操作避免数据竞争内存模型还指定了原子操作的语义,使编写无锁store(写入)、exchange(交换)、compare_exchange_weak/strong(比较并发代码成为可能并交换)等某些操作如++、--也重载为原子操作内存顺序原子操作与同步内存顺序(memory order)控制原子操作如何与其他内存操作同步C++定义了六原子操作配合适当的内存顺序可以建立线程间的同步关系,而无需互斥量等重量级种内存顺序-memory_order_relaxed最弱,只保证操作是原子的,不提供同步机制release-acquire顺序是常见的同步模式一个线程使用-memory_order_consume读取依赖于该结果的操作不会重排到之前(较少使用)memory_order_release存储数据,另一个线程使用memory_order_acquire加载数-memory_order_acquire用于读取,后续操作不会被重排到之前-据,这确保了第二个线程可以看到第一个线程在存储前对共享数据的所有修改原memory_order_release用于写入,之前的操作不会被重排到之后-子操作是实现无锁数据结构的基础,但正确使用需要深入理解内存模型和并发理论memory_order_acq_rel组合acquire和release语义-memory_order_seq_cst最强,提供全局一致的顺序(默认)文件操作文件流文本文件处理二进制文件处理随机访问C++通过流类处理文件输入输出,这些类定文本文件以行为单位存储数据,每行以换行二进制文件存储原始字节,无格式转换打文件流支持随机访问,可以在文件中任意位义在头文件中std::ifstream(输入文件流)符结束读取文本文件可以使用运算符、开二进制文件需要指定std::ios::binary标志置读写seekg/seekp方法用于移动读/写用于读取文件,std::ofstream(输出文件流)getline函数或get方法写入文本文件可读写二进制数据使用read和write方法,位置指针,tellg/tellp返回当前位置位用于写入文件,std::fstream(文件流)同时以使用运算符或put方法文本模式下,它们操作原始内存块二进制I/O适合存储置可以相对于文件开始beg、当前位置cur支持读写文件流继承自std::istream和系统会执行换行符转换(例如在Windows上,复杂数据结构、数值数组或非文本数据或文件结束end指定随机访问对于处理std::ostream,因此支持与std::cin和\n可能转换为\r\n)数据库文件、更新特定记录或读取文件特定std::cout相同的操作部分非常有用文件操作常见的错误处理方式包括检查文件是否成功打开、使用异常处理捕获I/O错误、检查读写操作后的流状态等流状态可以通过good、eof、fail和bad方法查询,也可以通过将流对象直接用于条件表达式(如iffile)来检查错误处理对于创建健壮的文件操作代码至关重要,尤其是在处理用户输入的文件路径或网络文件时C++17引入了文件系统库,提供了平台无关的文件和目录操作这个库简化了路径操作、文件复制和移动、目录遍历等任务,使得文件系统操作更加简单和安全现代C++程序应该优先使用这个标准库,而不是依赖于特定平台的API或传统的C函数正则表达式regex库介绍模式匹配替换操作C++11引入了头文件,提供了与Perl兼容的正则表达C++提供了几种匹配函数std::regex_match(检查std::regex_replace函数用于执行基于正则表达式的文式处理能力核心类包括std::regex(表示一个正整个字符串是否匹配模式),std::regex_search(查本替换它接受一个字符串、一个正则表达式和一个则表达式),std::match_results/std::smatch(存储找字符串中第一个匹配模式的部分),替换格式,返回替换后的新字符串替换格式可以包匹配结果),std::regex_iterator(用于迭代所有匹配std::regex_iterator(查找所有匹配项)这些函数可含特殊序列,如$(表示整个匹配项)、$n(表示项),std::regex_token_iterator(用于迭代子表达式以将结果存储在std::smatch对象中,该对象提供了访第n个捕获组)、$`(表示匹配前的文本)、$(表示匹配或非匹配部分)正则表达式库支持多种语法风问整个匹配项和捕获组的方法捕获组是用括号括起匹配后的文本)等替换操作常用于文本格式转换、格,如ECMAScript(默认)、basic、extended、来的子表达式,可以通过数字索引或名称引用清理和规范化任务awk等正则表达式语法是一套强大的模式描述语言,主要元素包括•字符类[abc](任何这些字符),[^abc](除这些字符外的任何字符),\d(数字),\w(单词字符),\s(空白字符)等•锚点^(行开始),$(行结束),\b(单词边界)•量词*(零或多次),+(一或多次),(零或一次),{n}(恰好n次),{n,}(至少n次),{n,m}(n到m次)•分组...(捕获组),:...(非捕获组),...(命名捕获组)•选择|(或),如a|b表示匹配a或b•转义\特殊字符可以匹配字面值,如\.匹配句点正则表达式是一种强大的文本处理工具,适用于验证输入格式(如电子邮件、电话号码)、从文本中提取信息、复杂的搜索和替换操作等然而,复杂的正则表达式可能难以阅读和维护,过度依赖它们可能导致一个问题变成两个问题对于简单的字符串操作,标准的字符串方法通常更清晰和高效调试与性能优化调试技巧内存分析工具有效的调试需要系统方法和适当工具使用集成调内存问题(泄漏、越界访问、悬垂指针)是C++常试器设置断点、监视变量、检查调用栈是基本技巧见错误源Valgrind是强大的内存检查工具,检测泄日志输出关键信息可以帮助理解程序行为断言漏和非法访问Visual Studio内存分析器在(assert宏)验证假设,在开发阶段捕获错误调Windows上提供类似功能Google的Sanitizer工具试版本应启用所有编译器警告,并使用工具如集成于现代编译器,以小性能代价提供强大检测能AddressSanitizer、UndefinedBehaviorSanitizer检力定期内存分析应成为开发流程一部分,特别是测内存和未定义行为问题对长时间运行程序优化策略性能分析工具性能优化遵循特定方法先确定性能目标,建立测性能优化应基于测量,而非猜测分析器(profiler)量基线,识别瓶颈,应用针对性优化,评估改进,识别程序的热点(耗时最多的部分)Linux上的重复过程常见优化包括算法改进(通常影响最perf、gprof、Windows上的Visual StudioProfiler、大)、数据结构选择、内存布局优化(提高缓存效跨平台的Intel VTune都是强大分析工具它们提供率)、减少内存分配、避免虚函数调用的过度使用、CPU使用、缓存命中率、分支预测、函数调用频率利用现代CPU特性(SIMD指令、多线程)过早优等信息微基准测试工具如Google Benchmark可精化是万恶之源,优化应针对实际瓶颈确测量小代码片段性能项目实践
(一)简单文本编辑器1需求分析一个基本的文本编辑器应具备创建、打开、编辑、保存文本文件的功能用户界面需要显示当前文件内容,提供光标导航,支持基本文本编辑操作(输入、删除、复制、粘贴)为了保持简单,我们将实现一个控制台界面的编辑器,专注于核心文本处理功能,而非图形界面2设计思路采用模块化设计,将程序分为数据模型(文本存储和操作)、视图(显示文本和光标)、控制器(处理用户输入和命令)三部分数据结构上,使用std::vector存储每一行文本,便于插入和删除操作命令模式可实现撤销/重做功能文件操作使用C++标准的文件流这种分层设计使各部分职责明确,便于扩展和维护3核心功能实现文本存储类(TextBuffer)管理字符串向量,提供插入、删除、查找、替换等操作编辑器类(Editor)集成TextBuffer,处理文件载入/保存、用户命令执行、光标移动等命令类层次(Command基类及具体命令类)封装编辑操作,支持撤销/重做控制台渲染类(ConsoleRenderer)负责在终端显示文本和光标位置键盘输入处理函数将按键映射到编辑器命令4代码结构项目组织为多个头文件和源文件TextBuffer.*(文本缓冲区类)、Editor.*(编辑器核心类)、Command.*(命令类层次)、ConsoleRenderer.*(控制台渲染)、main.cpp(程序入口)使用命名空间避免名称冲突,如namespace TextEditor采用异常处理机制处理文件操作和用户输入错误关键类采用单元测试确保功能正确性这种组织使代码模块化、可测试,并为未来扩展(如图形界面)奠定基础项目实践
(二)学生信息管理系统系统架构学生信息管理系统采用三层架构数据层负责数据存储和访问,使用文件或简单数据库;业务逻辑层处理核心功能,如学生信息管理、成绩计算、查询筛选;表示层提供用户界面,可以是控制台或图形界面各层之间通过清晰定义的接口通信,保持松耦合这种分层设计允许独立修改各层实现,如切换数据库系统或更改界面类型,而不影响其他部分数据结构设计系统的核心数据实体包括Student(学生)、Course(课程)、Grade(成绩)等Student类存储学号、姓名、性别、年龄、联系方式等基本信息Course类包含课程编号、名称、学分、教师等属性Grade类关联学生、课程和具体成绩使用STL容器存储这些对象,如unordered_map以学号为键快速查找学生,vector存储所有课程以支持遍历设计考虑数据完整性约束和关系维护核心功能实现系统实现的主要功能包括学生信息管理(添加、删除、修改、查询);课程信息管理;成绩录入与统计;数据导入导出(支持CSV或自定义格式)使用Repository模式封装数据访问,Service类实现业务逻辑查询功能支持多条件组合和排序,利用STL算法和函数对象实现灵活筛选采用观察者模式使数据变更自动通知UI更新异常处理确保系统稳定性,日志系统记录关键操作便于追踪问题界面交互控制台界面版本使用菜单系统,提供清晰的命令和选项对于图形界面版本,可使用Qt或wxWidgets等跨平台库实现UI设计遵循直观性原则,主界面显示学生列表,支持排序和筛选,双击打开详细信息表单验证确保数据输入正确性状态栏显示系统消息和操作结果设计考虑用户体验,如提供快捷键、记住用户偏好、支持撤销/重做常见操作界面与业务逻辑分离,通过Controller类或事件系统连接综合案例分析商业项目案例程序设计模式应用代码质量评估性能优化案例分析一个实际金融交易系统的实现设计模式是解决常见软件设计问题的高质量C++代码具有多个维度正确图像处理库优化案例初始版本处理该系统使用C++处理高频交易,要求可复用方案在一个3D游戏引擎中,性(无bug,处理边缘情况);可读4K图像需要2秒,优化后降至200毫秒极低延迟和高可靠性系统架构包括我们可以看到多种模式的应用工厂性(清晰命名,一致格式,适当注优化步骤包括首先用分析器识别瓶多个专用组件市场数据接收器、订模式创建游戏对象;观察者模式处理释);可维护性(模块化,低耦合,颈,发现像素遍历和滤波器计算占大单管理系统、风险控制模块、执行引事件通知;组合模式构建场景图;策遵循SOLID原则);性能(高效算法,部分时间;改进算法,使用积分图像擎等关键性能优化包括自定义内略模式实现不同渲染技术;单例模式合理资源使用);安全性(输入验证,预计算减少重复计算;优化内存访问存分配器避免动态分配开销、无锁数管理资源;访问者模式处理不同类型异常处理,资源管理)评估工具包模式,调整数据布局提高缓存命中率;据结构减少线程同步开销、内存布局实体的物理碰撞模式使用不应机械,括静态分析器(如Clang-Tidy、应用SIMD指令并行处理多个像素;最优化提高缓存利用率系统采用事件而应根据具体问题选择或调整合理CPPCheck)检测潜在问题;代码覆后使用多线程处理不同图像区域每驱动设计,使用状态机处理交易生命使用设计模式可以提高代码的灵活性、盖工具衡量测试完整性;性能分析器步优化后进行基准测试,确保改进并周期这个案例展示了C++在性能关可维护性和可扩展性,但过度使用会识别瓶颈;代码审查捕获自动化工具避免回退这个案例展示了系统性能键应用中的价值导致不必要的复杂性无法发现的问题持续集成系统可集优化方法测量、分析、改进、验证成这些工具,确保代码质量持续达标的迭代过程课程总结与展望知识体系回顾本课程系统讲解了C++编程的核心概念和高级特性我们从基础语法、数据类型和控制结构开始,逐步深入到面向对象编程、泛型编程和现代C++特性通过学习指针、引用、内存管理,理解了C++的底层机制STL容器、算法和迭代器的学习使我们掌握了强大的编程工具多态、模板、异常处理等高级概念拓展了解决复杂问题的能力这些知识形成了完整的C++技能体系,为未来的编程工作和学习打下坚实基础C++在行业中的应用C++凭借其高性能、底层控制能力和丰富的库生态,在多个领域占据重要地位游戏开发中,C++是主流引擎(Unreal、Unity)的核心语言高性能计算领域,C++用于科学模拟、金融建模和数据分析系统编程中,操作系统、数据库、浏览器等关键软件大量使用C++嵌入式系统开发中,C++在资源受限环境下提供面向对象抽象而不牺牲性能AI和机器学习领域,TensorFlow、PyTorch等框架的核心实现使用C++了解这些应用场景有助于将所学知识与实际需求对接学习路径建议掌握C++是持续学习的过程建议从小型项目开始实践,逐步挑战更复杂问题阅读优质书籍如《Effective ModernC++》深化理解研读开源项目代码学习实际编程风格和技术参与开源社区获取反馈和指导建立代码评审习惯,不断改进编程实践学习相关技术如版本控制、构建系统、调试工具、单元测试,提升全面开发能力专注特定领域(如游戏、系统编程)深入学习领域知识保持对C++新标准的关注,如C++20/23引入的概念、协程、模块等新特性未来发展趋势C++正持续演进,未来发展趋势包括语言简化,减少复杂性和历史包袱;性能改进,更好地支持现代硬件架构;安全性增强,减少内存错误和未定义行为;模块系统完善,改进构建时间和依赖管理;并发编程模型优化,简化多核编程;与其他语言更好的互操作性新的应用领域如量子计算、区块链、元宇宙开发等也为C++带来机遇尽管新语言不断涌现,C++凭借其性能、控制力和庞大的代码基础,在可预见的未来仍将保持重要地位持续学习和适应变化是C++开发者的关键能力。
个人认证
优秀文档
获得点赞 0