还剩48页未读,继续阅读
本资源只提供10页预览,全部文档请下载后查看!喜欢就下载吧,查找使用更方便
文本内容:
知识点回顾与解答C++欢迎参加我们的知识点回顾课程在接下来的课程中,我们将全面梳理C++编程语言的核心概念,解答常见问题,并帮助你深入理解这门强大的编C++程语言无论你是初学者还是想要巩固知识的有经验开发者,本课程都C++将为你提供系统化的学习体验通过这节课程内容,我们将从基础语法出发,逐步深入到面向对象编50C++程、模板、等高级主题,并结合实际面试题和编程实践,帮助你全面掌握STL编程技能让我们一起开始这段学习旅程!C++课程目标与结构介绍梳理核心知识点解答常见难点本课程将系统梳理的核心知识针对学习中的常见难点和误区,C++C++点,包括基础语法、面向对象编程、如内存管理、指针、虚函数等,我们泛型编程、库等重要内容,帮助将提供深入浅出的解析,通过比喻和STL你建立完整的知识体系我们将确保实例帮助你真正理解这些概念这些每个知识点都有充分的解释和实例演解释将基于实际编程经验,确保你能示够应用到实践中面试与实战准备课程后半部分将针对典型面试问题和实战案例进行分析,帮助你做好就业准备我们将分享高频面试题解答技巧,并提供实用的编程范例,增强你的实际编程能力本课程采用循序渐进的方式,从基础知识开始,逐步过渡到高级主题,最后结合实际应用场景每个主题都包含理论讲解和实践示例,帮助你全面掌握编程技能C++发展与应用领域C++1诞生阶段1979-1983在贝尔实验室开始开发,这是的Bjarne StroustrupC withClasses C++前身初始目标是在语言基础上添加面向对象特性,同时保持高效率和系C统级编程能力2标准化进程1985-1998语言被正式命名为,并开始了标准化过程年,第一个国际标准C++1998发布,奠定了的核心特性和标准库框架C++98C++3至今现代2011-C++的发布标志着现代时代的开始,引入了大量新特性如智能指针、C++11C++表达式等随后的、、继续推动语言进化,lambda C++14C++17C++20使其更加强大和易用凭借其高性能和灵活性,在多个领域有着广泛应用在游戏开发中,是主流引擎C++C++如的核心语言;在金融行业,它支持高频交易系统的开发;在嵌入式系统Unreal Engine中,的效率和对硬件的控制能力使其成为首选语言之一C++基础语法回顾变量与基本数据类型——浮点型字符型用于表示小数值的数据类型char类型,用于表示单个字符整型•float单精度,通常占4字节•通常占1字节包括int、short、long、long long等•double双精度,通常占8字节•使用单引号定义,如a布尔型类型,用于表示整数值bool类型,表示真/假值•int通常占4字节,范围约±21亿•long long通常占8字节,范围•占1字节更大•取值为true或false2314C++是强类型语言,变量必须在使用前声明其类型不同数据类型占用不同内存空间,了解这些基本类型的特性对于编写高效代码至关重要合理选择数据类型可以优化内存使用并提高程序性能关键字与命名规则关键字autoC++11引入,用于自动推导变量类型编译器会根据初始化表达式自动确定变量的类型,简化代码编写尤其适用于模板和迭代器类型声明,使代码更简洁易读关键字const声明常量,表示变量值不可修改可用于变量、函数参数、成员函数和返回值const成员函数不能修改对象的成员变量,提供了编译时的安全保证关键字static用于局部变量时,使其在函数调用之间保持值;用于全局变量和函数时,将其作用域限制在当前文件;用于类成员时,使其被类的所有实例共享关键字extern声明变量或函数在其他文件中定义,用于跨文件访问在大型项目中,extern关键字帮助组织和管理全局变量,实现多文件协作C++命名规则遵循标识符要求由字母、数字和下划线组成,不能以数字开头,不能使用关键字作为标识符良好的命名约定对提高代码可读性至关重要,通常类名采用首字母大写的驼峰命名法,变量和函数名采用小写字母或下划线分隔的命名方式输入输出流及其用法标准输入输出•cout变量/常量标准输出流,用于输出数据到控制台•cin变量标准输入流,用于从键盘获取用户输入•cerr标准错误输出流,用于输出错误信息字符串流操作•getlinecin,str读取一整行文本到字符串变量str中•stringstream字符串流,用于在字符串和其他数据类型之间转换•操纵符如endl、setw、setprecision控制输出格式文件流操作•ifstream文件输入流,用于从文件读取数据•ofstream文件输出流,用于向文件写入数据•fstream文件输入输出流,同时支持读写操作流操作技巧•链式操作cout值1值2endl;•格式控制使用iomanip库中的函数设置精度、宽度等•文件模式ios::in,ios::out,ios::app等控制文件打开方式C++的输入输出系统基于流的概念,将数据视为从程序流入或流出的字节序列理解并熟练使用这些流操作可以有效处理程序中的数据交互,包括用户界面、文件处理和字符串操作等关键任务运算符和表达式类别运算符结合性示例算术运算符左到右+,-,*,/,%a+b,a*b关系运算符左到右,,=,=,ab,a==b==,!=逻辑运算符左到右,||,!ab,!a位运算符左到右,|,^,~,,ab,a2赋值运算符右到左=,+=,-=,*=,/=,a=b,a+=5%=C++中的运算符有严格的优先级和结合性规则括号具有最高优先级,可用于改变默认的运算顺序算术运算符一般高于关系运算符,关系运算符高于逻辑运算符,赋值运算符优先级较低运算符重载是C++的强大特性,允许自定义类型如何响应标准运算符通过重载运算符,可以使自定义类的对象像内置类型一样使用例如,可以重载+运算符使两个复数对象相加,或重载运算符使对象能直接输出到流运算符重载通过定义特殊的成员函数或友元函数实现控制结构条件分支结构包括if语句、if-else语句和switch语句,用于根据条件执行不同代码块循环结构包括for循环、while循环和do-while循环,用于重复执行代码块跳转语句包括break、continue和return语句,用于控制程序执行流程的跳转条件分支结构中,if语句根据条件表达式的值决定是否执行一段代码;if-else语句提供两种可能的执行路径;switch语句适用于多分支条件判断,通常比多重if-else更清晰高效在循环结构中,for循环适合已知循环次数的情况,包含初始化、条件和迭代三个部分;while循环适合未知循环次数、需要前测试的情况;do-while循环确保代码块至少执行一次再检查条件跳转语句提供了额外的流程控制break用于终止当前循环或switch语句;continue跳过当前循环的剩余部分,开始下一次迭代;return语句结束函数执行并返回值掌握这些控制结构对编写清晰、高效的C++代码至关重要函数与参数传递值传递引用传递指针传递参数的副本传递给函数,函数内的修改不参数的引用传递给函数,函数内的修改会参数的地址传递给函数,通过解引用修改影响原始变量影响原始变量原始变量void incrementintx{void incrementintx{void incrementint*x{x++;//不影响调用处变量x++;//修改原始变量*x++;//修改指针指向的值}}}优点是安全,缺点是对大型对象可能造成效率高,可用限定避免意外修改灵活性高,可以传递表示无效值const nullptr性能开销支持默认参数,在函数声明时指定默认值,调用时可以省略这些参数内联函数通过在编译时将函数代码直接插入调用点来减C++inline少函数调用开销,适用于简短且频繁调用的函数递归函数是调用自身的函数,需要有明确的终止条件避免无限递归导致栈溢出指针与引用基础指针的本质引用特性指针与数组指针是存储内存地址的变引用是变量的别名,必须在数组名本质上是指向数组第量,通过指针可以间接访问声明时初始化且不能改变引一个元素的指针常量指内存中的数据指针声明使用对象引用声明使用符针可以通过数组名加下标用星号*,如int*ptr,使号,如int ref=num引或指针算术运算访问数组元用获取变量地址,如ptr=用没有自己的存储空间,它素指针可以指向数组的任num通过解引用操作符在底层实现上通常是通过指何元素,并可以通过指针遍*访问指针指向的值,如针常量实现的,但对程序员历数组,如*arr+i等同于*ptr=100隐藏了指针操作的复杂性arr[i]指针的优势在于其灵活性和控制能力,能够实现动态内存分配、间接引用和多级指针;引用则提供了更安全、更直观的接口,特别适合用作函数参数和返回值理解指针与引用的区别和联系对于掌握C++内存管理和高效编程至关重要在实际编程中,现代C++推荐优先使用引用而非指针,除非需要处理可能为空的情况或需要修改指针本身智能指针如shared_ptr进一步简化了内存管理,减少了原始指针带来的风险内存管理与野指针内存模型内存分配方式野指针问题内存泄漏原因C++C++程序内存分为栈、堆、全局/静态C++提供两种动态内存分配方式C野指针是指向无效内存区域的指针,内存泄漏是分配的内存未被释放导致存储区和常量区栈存储局部变量,风格的malloc/free和C++的常见原因有使用未初始化的指针、系统资源耗尽常见原因包括忘记自动管理;堆用于动态分配,需手动new/deletenew不仅分配内存还调访问已释放的内存、指针超出作用调用delete、异常发生时跳过释放代管理;全局区存放全局和静态变量;用构造函数,delete释放内存并调用域解决方法包括初始化为nullptr、码、循环引用等可使用智能指针、常量区存放常量析构函数,而malloc/free只负责内存及时释放和检查指针有效性RAII技术和内存检测工具预防分配和释放现代C++推荐使用RAII资源获取即初始化原则和智能指针来管理资源,避免手动内存管理的风险std::unique_ptr适用于独占所有权场景,std::shared_ptr适用于共享所有权,它们能大幅减少内存泄漏风险,提高代码安全性和可维护性数组与字符串一维数组基础多维数组一维数组是最基本的数组类型,存储相同类型多维数组是数组的数组,如二维数组可表示矩的元素序列声明方式type阵声明方式typearray_name[size]数组大小必须是常量表array_name[size1][size2]二维数组可看作达式数组下标从0开始,访问方式为一维数组的数组,内存中是连续存储的访问array_name[index]数组名是指向第一个元元素使用两个下标array_name[i][j]初始素的常量指针化可以嵌套花括号int arr
[2]
[3]={{1,2,3},{4,5,6}}字符串处理C++支持两种字符串C风格字符串字符数组和std::stringC风格字符串是以空字符\0结尾的字符数组,使用cstring库函数处理std::string是C++标准库类,提供了丰富的成员函数,自动管理内存,推荐在现代C++中使用使用std::string相比C风格字符串有诸多优势无需担心缓冲区溢出、自动处理内存分配和释放、支持直接使用+运算符连接字符串、提供丰富的查找和替换功能std::string的实现通常采用小字符串优化技术,对短字符串性能很好C++17引入了std::string_view,它提供了字符串的只读视图,避免了不必要的复制操作,特别适合作为函数参数传递大字符串时使用,可以显著提高性能在处理大量字符串数据时,选择合适的字符串表示方式对性能有重要影响结构体与联合体struct union结构体特性联合体特性枚举类型struct unionenum结构体是用户定义的数据类型,可以包含不同类联合体是一种特殊的数据结构,所有成员共享同枚举定义了一组命名的整型常量,提高代码可读型的数据成员在C++中,struct与class的主一块内存空间,大小等于最大成员的大小任一性C++11引入enum class,提供类型安全和作要区别在于默认访问权限struct默认为时刻只有一个成员可以存储值,修改一个成员会用域控制,避免名称冲突public,class默认为private影响其他成员enum Color{RED,GREEN,BLUE};struct Student{union Data{enum classFruit{APPLE,ORANGE,int id;int i;BANANA};string name;float f;float score;char str
[20];};};普通enum会隐式转换为整数,而enum class需要显式转换,更加安全结构体的内存布局受内存对齐规则影响,总大小联合体常用于需要节省内存的场景,如嵌入式系可能大于各成员大小之和统或网络协议解析内存对齐是编译器为提高访问效率而进行的优化处理器通常以特定大小如4或8字节读取内存,对齐的数据结构可以减少内存访问次数可以使用#pragmapack指令或alignas说明符控制对齐方式,但过度对齐可能导致内存浪费、与类型别名typedef using的使用方法的优势typedef usingtypedef是C++继承自C语言的类型别名C++11引入的using关键字提供了更现定义工具,它允许为现有类型创建新名代、更灵活的类型别名方式相比称,使代码更易读typedef特别适合简typedef,using语法更直观,尤其在处化复杂类型声明,如函数指针或复杂模板理模板时更加清晰using还可以用于引类型在大型项目中,统一使用typedef入命名空间成员,这是typedef无法实现可以提高代码一致性和可维护性的功能现代C++编程推荐优先使用using而非typedef两者用法对比typedef和using在大多数情况下可以互换使用,但语法结构不同使用typedef时,新类型名称位于声明中间;而using则采用更直观的赋值式语法例如定义函数指针类型,typedef写法较复杂,using写法更清晰易懂类型别名在提高代码可读性方面发挥重要作用对于复杂类型如std::vectorstd::mapstd::string,int,使用类型别名可以大大简化代码类型别名也方便进行类型修改,只需在一处修改别名定义,所有使用该别名的代码都会受影响,增强了代码的可维护性具体示例使用typedef定义函数指针typedef int*FuncPtrint,int;使用using定义相同类型using FuncPtr=int*int,int;使用using定义模板别名templatetypename TusingVec=std::vectorT;这在typedef中需要更复杂的语法实现新特性总览C++11/14/17核心特性C++11•自动类型推导auto根据初始化表达式推导变量类型•nullptr类型安全的空指针常量,替代NULL•Lambda表达式创建匿名函数对象•右值引用与移动语义优化资源管理•智能指针unique_ptr,shared_ptr,weak_ptr改进C++14•通用lambda表达式参数类型可使用auto•返回类型推导函数可省略返回类型•变量模板允许变量也可以是模板•constexpr函数增强放宽了限制•二进制字面量如0b1010表示二进制重要更新C++17•结构化绑定auto[x,y]=getPair;•if/switch语句中的初始化器•std::optional,std::variant,std::any•std::string_view字符串的非拥有视图•文件系统库std::filesystemC++11是一次重大更新,它引入了大量现代化特性,使C++更易用、更高效C++14主要是对C++11的完善和扩展C++17则带来了更多实用工具和语法糖,进一步提升了开发效率这些新特性大幅改善了C++的表达能力和安全性,同时保持了高性能的传统面向对象核心类与对象——对象实例化基于类创建具体实例,拥有独立的成员变量类定义封装数据与方法的蓝图数据抽象提取共同特征,隐藏实现细节类是C++面向对象编程的基本单元,提供了数据抽象和封装的机制类定义了一个新的数据类型,包含数据成员(属性)和成员函数(方法)数据成员描述对象的状态,成员函数定义对象的行为通过访问控制(public、private、protected),类可以隐藏内部实现细节,只暴露必要的接口,实现信息隐藏对象是类的实例,每个对象有自己的成员变量副本,但共享类的成员函数创建对象时,系统分配内存存储其数据成员成员函数通过this指针隐式访问调用对象C++支持多种对象创建方式栈上创建(自动管理生命周期)、堆上动态创建(需手动管理)和数组形式创建(多个相同类型对象)良好的类设计遵循单一职责原则,每个类应只有一个变更的理由合理使用封装可以降低系统复杂度,提高代码可维护性和复用性面向对象设计是构建大型软件系统的重要范式,掌握类与对象的基本概念是深入学习C++的关键构造函数与析构函数默认构造函数带参构造函数无参数的构造函数,如未定义则编译器接受参数的构造函数,用于创建对象时可能生成用于创建对象时初始化成员根据参数初始化可以定义多个带参构变量,确保对象处于有效状态可以使造函数,实现重载支持初始化列表语用显式要求编译器生成法,推荐用于效率和常量成员初始化=default析构函数拷贝构造函数对象销毁时自动调用,释放资源无参用同类型对象初始化新对象时调用参数无返回值,每个类只有一个析构函数为类的常量引用深拷贝浅拷贝vs数管理动态内存或其他资源时必须正管理资源时需实现深拷贝可用确实现,防止内存泄漏禁止拷贝=delete引入了移动构造函数和移动赋值运算符,通过右值引用实现资源转移而非复制,大幅提高性能现代还支持委托构造函C++11C++数,允许一个构造函数调用同类的其他构造函数,减少代码重复特殊成员函数遵循三五法则如果需要定义析构函数、拷贝构造函数或拷贝赋值运算符中的任何一个,通常需要定义所有三个;加上移动构造和移动赋值则成五法则指针的用途this区分成员变量与局部变量实现链式调用实现多态调用当成员函数的参数或局部变量与成员变量同名时,使用this指针通过在成员函数中返回*this(当前对象的引用),可以实现方在成员函数内部调用其他虚函数时,使用this指针可以确保多态可以明确引用成员变量例如,this-value表示成员变量法的链式调用这种设计模式在流式接口和构建器模式中非常有调用通过this-virtualFunc可以确保调用的是最终派生类value,而不是局部变量这种情况在构造函数和setter方法中用,使代码更简洁易读每个修改对象状态的方法都返回对象本中的版本,而非当前类中的版本这对于实现模板方法设计模式特别常见,避免了命名冲突的问题身,允许连续调用多个方法尤其重要void setValueintvalue{MyClass setValueintv{void process{this-value=value;//明确引用成员变量value=v;//确保调用最终派生类的方法}return*this;//返回当前对象引用this-preProcess;}//处理逻辑...//使用:obj.setValue
10.setNametest;this-postProcess;}this指针是C++中的一个隐含参数,指向调用成员函数的对象它是非静态成员函数的隐式第一个参数,允许成员函数访问调用对象的数据this是一个常量指针(Type*const this),不能修改其指向,但可以修改指向的内容(除非是const成员函数)在静态成员函数中不能使用this指针,因为静态函数不与特定对象关联理解this指针的工作原理有助于掌握C++面向对象编程的核心概念,特别是在设计复杂类关系和实现高级设计模式时类的访问权限控制protected保护成员只能被本类和派生类的成员函数访问•介于公有和私有之间,支持继承但限制外部访问public•用于需要在继承链中共享但不对外公开的功能公有成员可以被任何函数访问,包括类外部的函数•实现部分封装,为继承设计提供灵活性•类的接口部分,定义类与外部世界的交互方式private•通常包括构造函数、析构函数和公共操作方法私有成员只能被本类的成员函数和友元访问•代表is-a关系,派生类可继承并直接使用•类的实现细节,完全隐藏内部机制•默认的类成员访问权限(struct默认为public)•派生类不能直接访问基类的私有成员访问权限控制是C++封装机制的核心,通过限制对内部数据和实现细节的访问,提高了代码的安全性和可维护性良好的类设计应该公开最小必要的接口,隐藏所有实现细节,这符合信息隐藏原则在继承关系中,访问修饰符与继承方式(public,protected,private)共同决定派生类成员的最终访问权限例如,如果使用protected继承,基类的public成员在派生类中变为protected理解访问权限控制对于设计良好的类继承结构至关重要,它有助于实现适当的封装和信息隐藏静态成员与类成员函数静态成员变量静态成员函数静态成员变量是属于类而非对象的变量,所有对象共享同一份数据它们在程序开始时分配内存,静态成员函数是属于类而非对象的函数,不依赖于任何对象实例它们没有this指针,只能访问静程序结束时释放,生命周期贯穿整个程序态成员变量和调用其他静态成员函数•必须在类外进行定义和初始化(C++17前)•使用类名直接调用ClassName::staticFunc•可以通过类名直接访问ClassName::staticVar•无法访问非静态成员变量和函数•常用于计数器、共享配置等场景•不能声明为const、volatile或virtual•可以是私有的,遵循类的访问控制规则•常用于工厂方法、单例模式实现class Counter{class MathUtil{private:public:static int count;static double PI{return
3.14159;}public:static intaddint a,int b{return a+b;}Counter{count++;}};static intgetCount{return count;}//使用double pi=MathUtil::PI;};int Counter::count=0;//必须在类外定义静态成员的主要用途包括实现所有对象共享的数据(如配置信息)、记录类相关的全局状态(如对象计数)、提供不需要对象实例的功能(如工具函数)静态成员是实现单例模式、工厂模式等设计模式的关键组件C++17引入了inline静态成员变量,允许在类内部直接初始化静态成员,简化了代码static inlineintcount=0;这种方式避免了在类外部重复定义的需要,提高了代码的可维护性友元函数与友元类友元函数基础友元函数是定义在类外部但有权访问类的所有私有成员和保护成员的函数通过在类内部使用friend关键字声明,可以赋予外部函数访问权限运算符重载应用友元函数常用于运算符重载,特别是需要访问左操作数私有成员的双目运算符例如流插入和提取运算符,通常实现为友元函数友元类实现友元类是指一个类的所有成员函数都可以访问另一个类的私有和保护成员这种关系不具有传递性和继承性,需要显式声明封装性考量友元关系破坏了封装性,因此应谨慎使用良好的设计应尽量通过公有接口解决问题,只在确实必要时才使用友元友元函数示例当需要定义一个需要访问类私有成员的非成员函数时,友元声明非常有用例如,复数类可以将输出运算符声明为友元,以便直接访问实部和虚部class Complex{double real,imag;public:Complexdouble r,double i:realr,imagi{}friend ostream operatorostream os,const Complexc;};ostreamoperatorostreamos,const Complexc{return osc.real+c.imagi;}友元类应用场景包括密切合作的类之间需要共享内部实现细节;迭代器类需要访问容器类的内部结构;辅助类需要高效访问主类的私有数据虽然友元提供了灵活性,但过度使用会降低代码的可维护性和安全性,应当作为设计中的特殊手段而非常规选择类的继承与派生基本继承概念继承允许子类获取父类的属性和行为继承方式与访问控制public,protected,private继承影响成员访问权限多继承与菱形继承虚拟继承解决多重继承下的二义性问题继承是面向对象编程的核心概念之一,允许创建基于现有类的新类派生类子类继承基类父类的成员,并可以添加新成员或重写现有成员C++支持三种继承方式public继承is-a关系,protected继承和private继承has-a或is-implemented-in-terms-of关系继承方式决定了基类成员在派生类中的访问权限构造和析构的顺序遵循一定规则构造时,先构造基类,再构造派生类;析构时顺序相反这确保对象在任何时候都处于有效状态派生类构造函数可以通过初始化列表调用特定的基类构造函数C++支持多继承,一个类可以同时从多个基类派生然而,多继承可能导致菱形继承问题如果类D继承自类B和类C,而B和C又都继承自类A,那么D会包含A的两个副本使用虚拟继承virtual可以解决这个问题,确保共同基类只有一个实例虽然多继承提供了强大的设计能力,但也增加了复杂性,应谨慎使用虚函数与多态机制虚函数基础虚函数是基类中使用virtual关键字声明的成员函数,允许派生类重写override该函数实现这是C++实现多态的核心机制,使得通过基类指针或引用调用函数时,能够根据对象的实际类型调用相应的函数版本虚函数表vtable包含虚函数的类会生成一个虚函数表,存储该类所有虚函数的地址每个类对象包含一个指向虚函数表的指针vptr,这是C++实现动态绑定的内部机制虚函数表在编译时建立,运行时使用函数重写规则派生类重写虚函数时,函数签名必须完全匹配参数、返回类型相同C++11引入了override关键字,明确标示重写意图并使编译器验证是否正确重写重写函数的访问权限可以不同于基类动态绑定是C++多态的核心机制,它允许程序在运行时根据对象的实际类型决定调用哪个函数实现当通过基类指针或引用调用虚函数时,系统会查找对象的vptr,通过虚函数表找到对应的函数地址这种机制使得代码更加灵活,能够处理不同类型的对象而无需修改调用代码虚析构函数是一个重要的设计考量如果类设计为基类,其析构函数应该声明为虚函数这确保当通过基类指针删除派生类对象时,能正确调用派生类的析构函数,避免资源泄漏虚函数有轻微的性能开销额外的内存和间接调用,但带来的灵活性通常远超过这些成本class Base{public:virtual void show{coutBaseendl;}virtual~Base{}};class Derived:public Base{public:voidshowoverride{coutDerivedendl;}};//多态调用示例Base*ptr=new Derived;ptr-show;//输出Deriveddelete ptr;//正确调用Derived的析构函数抽象类与纯虚函数纯虚函数声明抽象类特性纯虚函数是用=0声明的虚函数,表明该函数没有实现,必须由派生类提供实包含至少一个纯虚函数的类称为抽象类抽象类不能实例化对象,只能作为基类现语法为virtual返回类型函数名参数列表=0;纯虚函数可以有函数体,用于派生派生类必须实现所有纯虚函数,否则仍然是抽象类抽象类可以包含但必须在类外定义,这种情况较少见数据成员和普通成员函数接口类设计仅包含纯虚函数的抽象类通常被称为接口类C++中接口类通常不包含数据成员和函数实现接口类定义了一组功能规范,但不关心具体实现多个不相关的类可以实现同一接口,提供多态行为抽象类在设计模式中有广泛应用在工厂方法模式中,抽象工厂定义创建对象的接口,具体工厂实现该接口在策略模式中,抽象策略类定义算法族的通用接口,具体策略类提供实现在观察者模式中,抽象观察者定义通知接口,具体观察者实现响应行为抽象类相比普通基类的优势在于强制派生类实现特定功能,确保类层次结构的一致性它提供了接口与实现分离的机制,增加了代码的灵活性和可扩展性在大型系统设计中,抽象类常用于定义组件间的交互协议,使系统各部分能够独立演化而不影响整体结构//抽象类示例class Shape{public:virtual double area const=0;//纯虚函数virtual double perimeter const=0;//纯虚函数virtual~Shape{}//虚析构函数};//具体派生类class Circle:public Shape{private:double radius;public:Circledouble r:radiusr{}double areaconst override{return
3.14159*radius*radius;}doubleperimeterconst override{return2*
3.14159*radius;}};运算符重载实战运算符重载是C++的强大特性,允许自定义类型的对象像内置类型一样使用运算符通过重载,我们可以使代码更直观、可读性更强运算符重载有两种实现方式成员函数和非成员函数通常是友元函数常见重载运算符包括
1.流操作符,通常实现为友元函数,使对象可以直接用于流输入输出
2.赋值运算符=管理资源的类需要实现深拷贝
3.比较运算符==,!=,,允许对象间比较
4.算术运算符+,-,*,/实现对象间的数学运算
5.下标操作符[]允许像数组一样访问对象元素
6.函数调用操作符使对象可以像函数一样调用,实现函数对象重载运算符时应遵循直觉原则,保持与内置类型行为一致例如,+应表示加法而非减法某些运算符如.,.*,::,:不能重载复合赋值运算符+=,-=应实现为成员函数,而二元算术运算符通常实现为非成员函数以支持隐式类型转换模板编程基础函数模板——模板定义函数模板是一种特殊的函数,可以对不同数据类型执行相同的操作使用template关键字和类型参数声明,类型参数通常命名为T、U等函数模板通过替换类型参数生成具体函数实例这是C++泛型编程的基础templateT maxTa,T b{return aba:b;}模板实例化函数模板在调用时才生成具体的函数代码,这个过程称为模板实例化可以通过两种方式调用函数模板隐式实例化编译器根据参数类型推断和显式实例化手动指定类型参数编译器为每个使用的类型生成独立的函数代码int i=max10,20;//隐式实例化double d=max
3.14,
2.72;//显式实例化高级特性与应用函数模板支持多个类型参数、默认参数和非类型参数函数模板可以重载,编译器根据参数匹配规则选择最适合的版本模板参数推导遵循严格的类型匹配规则,但允许某些隐式转换函数模板在STL算法库中广泛应用templateauto addTa,U b-decltypea+b{return a+b;}templatevoid printArrayTarr[size]{forint i=0;isize;++istd::coutarr[i];}函数模板的核心优势在于代码复用一次编写,多种类型使用、类型安全编译期类型检查和维护性集中管理算法逻辑函数模板在编译时展开,没有运行时开销,这是C++模板机制的效率优势然而,过度使用模板可能导致代码膨胀和编译时间增加模板编程类模板——类模板基础类模板实例化类模板是创建类家族的蓝图,允许类定义中使用一个或多个类型参数这些类型参使用类模板时必须明确指定模板参数类型,不像函数模板可以自动推导类模板的数在类被实例化时才确定具体类型类模板通常用于创建容器、智能指针等通用组每个实例化版本是完全独立的类型类模板的成员函数只有被调用时才会实例化,件声明类模板使用template关键字,后跟类型参数列表未使用的成员函数不会生成代码,这称为惰性实例化容器实现模板与继承STLSTL容器如vector、list、map等都是基于类模板实现的这些容器通过模板参数指类模板可以继承自普通类、其他类模板或模板类型参数模板参数也可以有默认定存储的元素类型,内部管理内存和元素操作了解类模板机制有助于理解和正确值,简化常见用例模板类成员函数可以在类内或类外定义,类外定义需要完整的使用STL容器不同容器有不同的内部实现和性能特性模板声明类模板也可以有非模板成员,所有实例共享该实现以STL中的vector为例,它是一个动态数组实现的容器类模板vector内部管理一个连续内存块,支持快速随机访问和尾部插入/删除操作其关键实现包括动态扩容通常容量翻倍、元素构造/析构管理和迭代器支持vector源码分析可以帮助理解内存管理、异常安全和性能优化技术list是另一个重要的STL容器,基于双向链表实现与vector不同,list支持常数时间的插入/删除操作任意位置,但不支持随机访问list节点在堆上分配,各节点内存不连续,因此内存局部性较差list的实现涉及节点管理、迭代器设计和特殊的拼接操作模板的特化与偏特化模板特化基本概念函数模板特化类模板偏特化模板特化允许为特定类型提供专门的实现,优化性能或处理特函数模板只支持全特化,不支持偏特化实践中,函数模板特类模板支持偏特化,可以为一组相关类型提供统一实现偏特殊情况特化分为全特化完全指定所有模板参数和偏特化部化使用较少,通常通过函数重载实现类似功能C++17引入的if化在STL中广泛应用,如traits类实现类型特性检测偏特化可分指定模板参数特化的主要目的是针对特定类型提供更高效constexpr提供了更优雅的条件编译方式,可以在单个函数模以基于类型属性如指针、const等或模板参数关系或更适合的实现板中处理不同类型//指针类型的偏特化//主模板//主函数模板templatetemplate templateclass Container{/*...*/};class Container{/*...*/};void printconstT value{std::coutvaluestd::endl;//多参数模板的偏特化//全特化(char*类型)}templatetemplate class Pair{/*...*/};class Container{/*...*/};//特化版本(针对char*)template templatevoidprintchar*const str{classPair{/*...*/};std::cout字符串:strstd::endl;}模板特化的实际应用场景包括类型萃取type traits实现类型特性检测;容器对特定类型的优化;算法对特殊类型的定制;处理不支持通用操作的类型标准库的std::vector就是一个著名的特化例子,它使用位压缩存储布尔值,节省内存空间使用模板特化需要注意特化版本必须在所有使用点可见;特化可能导致代码膨胀;过度特化会增加维护难度;特化与重载的选择取决于具体需求良好的模板设计应该在通用性和专用性之间找到平衡异常处理机制抛出异常使用throw语句抛出异常对象,表示发生了错误或异常情况可以抛出任何类型的异常,包括内置类型、标准异常类型或自定义异常类型异常抛出后,程序控制权转移到最近的匹配catch块块try将可能引发异常的代码封装在try块中try块后必须跟随至少一个catch块当try块中的代码抛出异常时,程序跳过try块中剩余代码,查找匹配的catch块捕获异常catch块用于处理特定类型的异常可以有多个catch块处理不同类型的异常catch...可以捕获任何类型的异常异常处理完成后,程序继续执行try-catch块之后的代码说明符noexceptC++11引入noexcept说明符,声明函数不会抛出异常可以是无条件的noexcept或有条件的noexceptexpression违反noexcept声明会导致std::terminate被调用,程序终止C++标准库定义了一系列标准异常类,都派生自std::exception基类常用的标准异常包括std::runtime_error运行时错误、std::logic_error逻辑错误、std::bad_alloc内存分配失败、std::out_of_range越界访问等自定义异常类通常应该继承自标准异常类,并提供有用的错误信息异常处理的最佳实践包括异常应该用于处理异常情况,不应替代正常的控制流;捕获异常应尽可能具体,避免捕获太广泛的异常类型;资源管理应使用RAII模式确保异常安全;异常说明throw规范已被废弃,应使用noexcept;构造函数和析构函数需要特别注意异常安全理解异常安全保证级别基本、强、无抛出对编写健壮代码至关重要名字空间管理namespace命名空间基础访问命名空间成员命名空间是C++中用于解决名称冲突的机制,它创建了一个具名的作用域,将名访问命名空间成员有三种主要方式完全限定名称namespace::member、称如变量、函数、类封装在其中命名空间可以嵌套,可以跨多个文件,并且可using声明using namespace::member和using指令using namespace以添加新成员命名空间的声明使用namespace关键字,后跟名称和花括号包围namespace_name完全限定名称最明确但较冗长;using声明引入特定名的成员定义称;using指令引入整个命名空间,便捷但可能导致名称污染namespace Math{//完全限定名称doublePI=
3.14159;double area=Math::PI*Math::squareradius;double squaredoublex{return x*x;}}//using声明using Math::PI;double area=PI*Math::squareradius;//using指令using namespaceMath;doublearea=PI*squareradius;最佳实践在头文件中避免using指令,防止命名空间污染;在实现文件.cpp中可以使用using,但应限制在函数作用域内;标准库组件位于std命名空间,应使用std::前缀或有选择地使用using声明;创建库时应使用唯一的命名空间名称;大型项目可以使用嵌套命名空间组织代码//嵌套命名空间namespace Company{namespace Project{namespace Module{void function;}}}//C++17简化语法namespace Company::Project::Module{void function;}标准模板库简介STL算法1提供通用操作如排序、搜索、转换等容器数据结构如向量、列表、映射、集合等迭代器连接算法与容器的桥梁函数对象可调用实体,用于自定义算法行为适配器5修改接口以适应特定需求分配器6管理内存分配与释放标准模板库STL是C++标准库的核心组件,提供了通用的容器、算法、迭代器和函数对象STL的设计基于泛型编程范式,实现了算法与数据结构的分离,通过迭代器连接两者这种设计使算法可以独立于具体容器工作,大幅提高了代码复用性和灵活性STL的主要组件包括容器存储和组织数据的集合类;算法对容器元素进行操作的函数模板;迭代器提供遍历容器元素的统一接口;函数对象封装可调用实体,用于自定义算法行为;适配器修改容器或迭代器的接口;分配器管理内存分配STL的设计哲学强调效率、泛化和抽象它建立在RAII原则上,确保资源安全管理;利用静态多态性模板而非动态多态性虚函数,避免运行时开销;提供类型安全的接口,在编译期捕获错误掌握STL是高效C++编程的关键,它可以大幅减少手动实现数据结构和算法的需求,提高开发效率和代码质量容器详解STL——vector内部结构动态扩容随机访问插入与删除vector是连续内存的动态数组,通常由三当vector需要更多空间时,它会分配更大vector支持O1时间复杂度的随机访问,尾部插入通常为O1除非触发扩容,而个指针实现指向数据开始、当前使用结的内存块通常是当前容量的
1.5或2倍,通过下标或迭代器均可实现连续内存布中间插入需要On时间,因为需要移动插束和分配内存结束的指针这三个指针控将现有元素复制或移动到新位置,然后释局提供了优异的缓存局部性,使得vector入点后的所有元素删除操作的复杂度类制vector的大小和容量管理放旧内存这种扩容策略平衡了内存使用在遍历操作上性能出色似,尾部删除为O1,中间删除为On和复制操作开销vector是STL中最常用的容器之一,适用于需要频繁随机访问、元素数量可变且主要在尾部操作的场景其主要方法包括push_back/emplace_back添加元素、pop_back移除末尾元素、insert/erase在指定位置插入/删除、resize调整大小、reserve预留容量、at有边界检查的访问、[]操作符无检查访问使用vector的最佳实践包括提前reserve预留足够空间避免频繁扩容;优先使用emplace_back代替push_back减少复制开销;避免在遍历过程中修改vector大小,可能导致迭代器失效;注意erase操作会返回下一个有效迭代器,便于继续遍历;使用shrink_to_fit释放多余容量;需要频繁在中间插入/删除时,考虑使用list或deque替代容器STL——list/forward_list双向链表单向链表Listforward_list是一个双向链表,每个节点包含前向和后向指针,可以在是引入的单向链表,每个节点只包含指向下一std::list O1std::forward_list C++11时间执行插入和删除操作不支持随机访问,必须从头或尾迭代到个节点的指针比更节省内存,但只能向前遍历,不list forward_list list目标位置,访问时间为能反向迭代On•优势任意位置快速插入/删除•优势内存开销最小的链表容器•劣势内存占用较大,缓存不友好•劣势只能前向遍历,无size方法•特性迭代器在插入/删除后仍然有效•特性适合内存受限的场景特有操作拼接、合并、排序、去使用等接口,需要特别注list splicemergesortunique forward_list before_begin/insert_after重、删除意remove/remove_if链表的内部结构基于节点分配,每个节点包含数据和指针的节点结构通常包含数据部分和两个指针前向和后向;的节std::liststd::forward_list点只有数据和一个下一节点指针链表的内存不连续,节点分散在堆上,这导致较差的缓存局部性,但提供了内存分配的灵活性链表适用场景需要频繁在中间位置插入删除元素;需要稳定的迭代器插入删除操作不会使其他位置的迭代器失效;不需要随机访问;元素数量//频繁变化在选择容器时,应根据实际操作模式权衡如果主要是随机访问和尾部操作,通常更合适;如果主要是中间插入删除,可能vector/list是更好的选择;如果内存极其有限且只需前向遍历,是理想选择forward_list容器与STL——map set底层实现特性特性多值变体Map Setmap和set基于红黑树实现,红黑std::map存储键值对key-value std::set存储唯一的元素集合,按multimap和multiset允许多个元树是一种自平衡二叉搜索树,保证pairs,按键排序每个键在map值排序set的元素一旦插入就不素具有相同的键或值multimap了核心操作的对数时间复杂度红中唯一,通过键可以快速查找对应能修改(因为修改可能破坏排适用于一对多映射(如字典中一个黑树通过特定规则(如节点颜色、的值map支持[]操作符访问或插序),只能删除再插入set常用单词有多个定义);multiset适用路径黑节点数等)保持平衡,防止入元素,以及at方法进行边界检于需要维护有序唯一元素集合的场于需要跟踪重复元素的有序集合树退化为链表,确保Olog n的查查的访问常用于需要键值关联且景,如过滤重复项、按顺序处理独(如词频统计)这些容器提供找效率要求有序的场景特值equal_range方法查找所有匹配元素map和set的时间复杂度分析插入、删除、查找操作平均和最坏情况下都是Olog n,这比unordered_map/set的平均O1慢,但最坏情况更好map和set始终保持元素有序,支持范围查询和顺序遍历,这是基于哈希表的容器所不具备的优势使用示例map可用于实现字典、配置存储、缓存系统等;set常用于去重、集合运算、有序遍历等自定义类型作为键或元素时,需要提供比较函数(默认使用小于运算符)C++17引入了extract方法,允许在不复制的情况下移动节点,提高了重键操作的效率选择合适的STL容器时,应考虑操作模式、元素顺序需求和查询类型容器与STL——unordered_map unordered_set容器数据结构查找/插入/删除元素排序主要应用unordered_map哈希表平均O1,最坏On无序快速查找键值对unordered_set哈希表平均O1,最坏On无序快速查找/去重map红黑树Olog n按键排序有序键值对set红黑树Olog n按值排序有序唯一集合unordered_map和unordered_set是C++11引入的基于哈希表的关联容器,分别对应map和set的无序版本它们使用哈希函数将键映射到桶bucket,每个桶包含零个或多个元素当多个键映射到同一个桶时,会形成链表或其他结构来存储冲突元素哈希表的性能高度依赖于哈希函数质量和负载因子元素数量/桶数量良好的哈希函数应该将键均匀分布到各个桶,减少冲突;合适的负载因子通常小于1可以平衡内存使用和查找效率当负载因子超过阈值时,容器会自动增加桶数量并重新哈希rehash,这是一个代价较高的操作与有序容器相比,无序容器的主要优势是平均情况下O1的查找、插入和删除性能,适用于不需要元素排序但需要频繁查找的场景然而,它们也有劣势内存占用通常更大;迭代顺序不稳定,可能随着插入/删除操作变化;对自定义类型需要提供哈希函数和相等比较函数;在极端情况下性能可能退化算法库STL algorithmSTL算法库algorithm头文件提供了大量针对容器元素操作的通用算法,它们通过迭代器与容器交互,使得同一算法可以应用于不同类型的容器算法库的设计理念是将数据结构与算法分离,实现高度的代码复用常用算法分类•非修改序列操作find,count,for_each,all_of,any_of,none_of等•修改序列操作copy,move,transform,replace,fill,generate等•排序和相关操作sort,partial_sort,nth_element,binary_search等•分区操作partition,stable_partition等•数值操作numeric accumulate,inner_product,partial_sum等•堆操作make_heap,push_heap,pop_heap,sort_heap等•最小/最大操作min,max,minmax,min_element,max_element等算法使用技巧传递函数对象或lambda表达式自定义行为;注意算法的前提条件和保证如排序算法要求随机访问迭代器;理解修改与非修改算法的区别;熟悉具名与匿名函数模式_if,_copy,_n等后缀C++17的执行策略允许指定并行、向量化或串行执行,提高多核系统性能使用STL算法可以提高代码质量、避免常见错误并利用优化实现,是C++程序员必备技能迭代器与遍历for_each迭代器基础概念迭代器是一种抽象的指针概念,提供了访问容器元素的统一接口它封装了元素访问和遍历的细节,是算法和容器之间的桥梁迭代器支持至少部分指针操作如*,++,=,==等,但不一定支持所有指针功能迭代器使得算法能够独立于具体容器实现,大大提高了代码的复用性迭代器种类层次STL定义了五种主要迭代器类别,每种提供不同级别的功能输入迭代器只读一次、输出迭代器只写一次、前向迭代器可多次读写,只能前进、双向迭代器可前进后退、随机访问迭代器支持任意跳转这些类别形成继承关系,较强的迭代器满足较弱迭代器的所有要求算法根据需要指定最低迭代器类别要求容器迭代器支持不同容器提供不同级别的迭代器vector/array/deque提供随机访问迭代器;list/set/map/multiset/multimap提供双向迭代器;forward_list/unordered_set/unordered_map提供前向迭代器了解容器支持的迭代器类型有助于选择合适的算法和容器组合每个容器通常提供begin/end方法获取迭代器,以及cbegin/cend获取常量迭代器for_each是STL中的一个通用算法,用于对范围内的每个元素应用指定操作它可以与函数指针、函数对象或lambda表达式结合使用,提供了简洁的遍历语法与传统for循环相比,for_each更加抽象,能更好地表达意图,并可能获得更好的优化C++11引入的基于范围的for循环range-based for提供了更简洁的遍历语法for autoelement:container{...}它简化了迭代器的使用,自动处理begin/end以及递增操作,适用于任何提供begin/end方法或全局begin/end函数的容器使用引用可以修改元素,使用常量引用const可以高效访问而不修改,使用值拷贝适用于简单类型或需要元素副本的情况智能指针()C++11unique_ptr shared_ptr独占所有权的智能指针,资源只能由一个共享所有权的智能指针,多个shared_ptr可以unique_ptr拥有不支持复制,但可以通过移指向同一资源内部使用引用计数跟踪所有者数动转移所有权std::move适用于不需要共享量,当计数归零时自动释放资源支持复制和赋资源的场景,如工厂模式返回的对象、函数内的值,有轻微的性能开销引用计数操作适用于临时资源等无额外开销,性能与原始指针几乎需要在多处共享资源的场景,如在多个对象间共相同支持自定义删除器,可用于管理数组等特享状态、缓存等线程安全的引用计数,但指向殊资源的对象不自动线程安全weak_ptrweak_ptr是shared_ptr的伴随指针,观察共享资源但不影响其生命周期不增加引用计数,因此不会阻止资源释放使用前需要通过lock方法检查并获取shared_ptr主要用于解决共享指针循环引用问题,以及访问可能已被释放资源的场景如观察者模式、缓存等智能指针是现代C++内存管理的核心工具,基于RAII资源获取即初始化原则,在对象销毁时自动释放资源使用智能指针可以大幅减少内存泄漏风险,不再需要手动调用delete标准库提供了make_unique和make_shared函数模板,用于安全地创建智能指针,避免内存泄漏和异常安全问题智能指针的最佳实践优先使用unique_ptr,只在确实需要共享所有权时使用shared_ptr;避免使用裸指针管理资源;警惕shared_ptr的循环引用问题;理解移动语义对智能指针的影响;注意自定义删除器的使用;避免通过裸指针同时创建多个智能指针智能指针通常作为类成员、函数参数和返回值,替代传统的裸指针,大大提高了代码的安全性和可维护性表达式使用lambda语法结构LambdaLambda表达式的完整语法是[捕获列表]参数列表mutable noexcept-返回类型{函数体}捕获列表指定了外部变量的访问方式;参数列表与普通函数相同;mutable允许修改按值捕获的变量;返回类型通常可省略由编译器推导变量捕获方式Lambda支持多种捕获方式[=]捕获所有变量的值,[]捕获所有变量的引用,[var]按值捕获特定变量,[var]按引用捕获特定变量,[]不捕获任何变量可以混合使用,如[=,var]按值捕获所有变量,但var按引用捕获实际应用场景Lambda常用于需要简短函数的场景,如STL算法参数、自定义排序、事件处理等它避免了定义单独函数的麻烦,代码更紧凑、可读性更高Lambda可以捕获上下文状态,比函数指针更灵活,比函数对象定义更简洁Lambda表达式是C++11引入的重要特性,它允许在调用点定义匿名函数对象Lambda背后的实现是编译器生成的仿函数函数对象类,捕获的变量成为类的成员变量Lambda使得函数式编程风格在C++中更加自然,特别适合与STL算法结合使用C++14和C++17对Lambda进行了重要扩展C++14允许在捕获列表中使用初始化表达式,如[n=0];允许泛型Lambda,参数可以使用auto;C++17允许在Lambda中使用constexpr,实现编译期执行;引入捕获*this,捕获当前对象的副本这些扩展使Lambda更加强大和灵活,能够应对更复杂的编程需求右值引用与移动语义右值与右值引用移动语义原理右值是临时对象或即将消亡的对象,不能取地址移动语义允许资源从一个对象转移到另一个对象,右值引用类型是对右值的引用,可延长临时对而不是复制通过移动构造函数和移动赋值运算符象生命周期与左值引用类型不同,右值引用只实现,这些函数接受右值引用参数移动操作窃取能绑定到右值std::move将左值转换为右值引源对象资源如指针,将源对象置于有效但未指定用,使其可被移动状态,通常资源指针置空完美转发性能优势完美转发是右值引用的另一重要应用,通过移动语义显著减少了不必要的复制,特别是对于管std::forward保持参数的值类别左值/右值模板理动态资源的大型对象如容器例如,vector的函数使用通用引用T接收任意参数,然后使用移动操作只需转移内部指针,而不是复制所有元std::forward精确传递给其他函数,保持原始参数素这在返回大对象、容器操作等场景下提供了显特性著性能提升移动语义的实现遵循以下模式移动构造函数接受右值引用参数T,窃取资源并将源对象置于安全状态;移动赋值运算符同样接受右值引用,先释放自身资源,再窃取源对象资源;这些函数应标记为noexcept以获得最佳优化STL容器在C++11后都支持移动语义,大幅提高了性能使用右值引用和移动语义的最佳实践理解区分值类别左值、纯右值、将亡值;使用std::move明确表示对象可以移动;实现三五法则如果定义了析构函数,也应定义移动操作和复制操作;编写移动操作时确保源对象处于有效状态;避免移动const对象常量无法修改,只能复制;注意容器元素移动的要求如vector::push_back在重新分配内存时会移动元素多线程与并发()C++11基础std::threadC++11引入std::thread用于创建和管理线程线程可以执行函数、函数对象、成员函数或lambda表达式创建线程后立即开始执行主线程必须通过join等待子线程完成,或通过detach分离子线程未join或detach的线程在析构时会调用std::terminate终止程序互斥锁与同步std::mutex提供基本的互斥功能,保护共享数据std::lock_guard和std::unique_lock提供RAII风格的锁管理,确保锁的正确释放条件变量std::condition_variable用于线程间的通知和等待,实现复杂同步模式std::atomic提供原子操作,某些简单场景下可以替代互斥锁异步任务与结果获取std::async允许异步执行任务,返回std::future用于获取结果可以指定启动策略async立即启动新线程、deferred延迟到future.get调用时执行或由系统决定std::promise/future对提供了更灵活的异步结果传递机制std::packaged_task封装可调用对象,将其执行与结果获取分离并发编程挑战多线程编程常见问题包括竞争条件多线程无序访问共享数据、死锁线程互相等待资源、活锁线程不断响应对方变化而无法继续、线程饥饿线程无法获得所需资源以及原子性、可见性和顺序性问题合理设计并严格遵循同步策略是关键C++11内存模型为并发编程提供了坚实基础,定义了多线程环境下内存操作的语义内存模型包括原子操作、内存顺序memoryordering以及同步和happens-before关系,确保多线程程序的可预测行为std::atomic提供了不同强度的内存序sequential_consistency最强,最简单、acquire-release中等、relaxed最弱,性能最好C++17进一步增强了并发支持,引入了并行算法库和std::shared_mutexC++20引入了std::jthread自动join、std::barrier、std::latch、std::semaphore等同步原语,以及std::atomic_ref和协程支持并发编程的最佳实践避免共享可变状态;优先使用高级抽象如并行算法;正确使用RAII管理资源和锁;考虑无锁编程技术;理解并遵循内存模型编译流程与链接过程C++预处理阶段源代码.cpp首先经过预处理器处理展开宏定义#define,处理条件编译指令#ifdef等,包含头文件#include,移除注释输出结果是扩展后的源代码,仍然是文本形式g++中使用-E选项可查看预处理后的代码编译阶段预处理后的代码被编译器转换为汇编代码包括词法分析、语法分析、语义分析、优化和代码生成这一阶段检查语法错误、类型错误等生成特定CPU架构的汇编代码.s文件g++中使用-S选项可生成汇编代码文件汇编阶段汇编器将汇编代码转换为机器码,生成目标文件.o或.obj目标文件包含二进制指令、数据和重定位信息,但外部引用未解析每个源文件独立汇编为一个目标文件,这允许增量编译g++中使用-c选项可生成目标文件链接阶段链接器将多个目标文件和库文件组合成一个可执行文件或库解析外部引用,如函数调用和全局变量执行地址分配和重定位,确定每个符号的最终内存位置链接可以是静态的库代码复制到可执行文件或动态的运行时加载库链接过程面临的主要问题包括符号解析找到每个未定义符号的定义;地址分配为代码和数据分配内存位置;符号重定位调整代码中的地址引用链接错误常见类型包括未定义的引用undefined reference、多重定义multiple definition和未解析的外部符号unresolved externalsymbol链接相关的高级主题静态链接vs动态链接性能、内存使用、更新便利性的权衡;函数名修饰name mangling使重载和模板成为可能;弱符号和强符号的区别;ABI应用程序二进制接口兼容性问题;调用约定参数传递和栈清理方式;链接时优化LTO通过全程序分析提高性能理解编译链接过程有助于解决复杂的构建问题和优化编译时间与构建工具Makefile CMake基础特性Makefile CMakeMakefile是一种传统的构建系统,通过规则定义源文件之间的依赖关系和构建命令CMake是更高级的构建系统生成器,它不直接构建项目,而是生成原生构建系统如它由一系列规则组成,每条规则指定目标文件、依赖文件和构建命令Make工具根据Makefile、Visual Studio项目等项目通过CMakeLists.txt文件描述,使用平台无文件修改时间决定是否需要重新构建,实现增量编译关的语法,大大简化了跨平台开发target:dependencies cmake_minimum_requiredVERSION
3.10command projectMyProjectadd_executablemain main.cpp helper.cppmain:main.o helper.og++-o mainmain.o helper.oCMake支持复杂的依赖管理、配置检测、构建类型配置,以及通过find_package机制集成第三方库Makefile的主要优势是轻量且广泛支持,但在大型跨平台项目中编写和维护较为复杂现代CMake推荐采用目标导向方法,通过target_link_libraries、target_include_directories等命令明确表达依赖关系,避免全局包含路径和编译标志这种方法提高了构建系统的可维护性和模块化程度,特别适合大型项目构建系统的最佳实践包括保持构建配置的一致性;将公共设置提取到共享模块;正确处理依赖关系,避免循环依赖;合理组织项目结构,促进代码复用;使用条件编译处理平台差异;设置合适的警告级别提高代码质量;提供便捷的构建配置选项掌握高效的构建系统对于管理复杂C++项目至关重要,能够大幅提高开发效率和代码质量内存对齐与性能优化内存对齐基础内存对齐是指数据在内存中的起始地址必须满足特定对齐要求对齐要求通常是数据类型大小的倍数,如4字节整数通常对齐到4字节边界对齐源于硬件架构设计,大多数处理器一次读取4或8字节数据,对齐的数据可以一次完成读取,非对齐数据需要多次读取结构体内存填充编译器会在结构体成员之间插入填充字节,确保每个成员正确对齐结构体总大小通常是最大成员对齐要求的倍数结构体成员的排序会影响总大小,将相似大小的成员分组可以减少填充,优化内存使用alignas关键字可以指定更严格的对齐要求缓存友好访问现代CPU使用多级缓存加速内存访问缓存按缓存行通常64字节管理,访问连续内存区域时性能最佳缓存友好编程包括顺序访问数组元素、减少随机访问、避免频繁跨越大内存区域、数据结构设计时考虑内存局部性数据布局优化结构体打包#pragma pack可减少内存使用但可能降低访问速度数据导向设计Data-Oriented Design强调根据访问模式组织数据,如使用结构体数组代替数组指针避免虚继承和过深继承链,它们增加内存访问间接性内存性能优化技术还包括利用SIMD指令SSE/AVX并行处理数据;避免虚函数表查找开销;减少动态内存分配次数;使用内存池管理小对象;避免伪共享false sharing导致的缓存冲突;利用预取指令提前加载数据;局部变量优先于全局变量;考虑对象生命周期管理内存碎片编译器优化对内存性能也有显著影响,如内联展开减少函数调用开销;循环展开和向量化;常量传播和死代码消除;指针分析优化内存访问选择合适的优化级别-O2,-O3和指令集扩展-mavx等可以显著提高性能性能优化应基于测量而非猜测,使用性能分析工具如perf,VTune识别瓶颈,避免过早优化导致代码可读性和可维护性下降常见面试题剖析
(一)智能指针释放机制虚函数表相关面试题unique_ptr使用独占所有权模型,只能通过虚函数表vtable是编译器为包含虚函数的类创建的静std::move转移所有权析构时自动调用关联的删除态数组,存储虚函数地址每个类有一个vtable,每器默认为deleteshared_ptr使用引用计数管理生个对象包含一个vptr指向该表派生类重写基类虚函命周期,计数在构造、复制、移动和析构时更新当数时,会在自己的vtable相应位置替换函数地址多引用计数降为零时释放资源weak_ptr作为重继承情况下,对象可能包含多个vptr虚函数调用shared_ptr的观察者,不增加引用计数,可用于检测通过vptr间接查表,有少量性能开销对象是否还存在构造函数与异常安全菱形继承问题构造函数抛出异常时,已构造的成员会正常析构,但菱形继承发生在类D继承自类B和C,而B和C都继承自对象本身的析构函数不会调用这可能导致资源泄类A这会导致D包含A的两个副本,引发歧义虚拟漏,特别是手动管理资源时解决方案包括使用智继承virtual inheritance解决此问题,确保共同基类3能指针管理资源;将资源获取放入单独的init方法;只有一个实例使用虚继承时基类构造顺序发生变使用try-catch块处理构造过程中的异常;采用RAII范化虚基类构造函数由最终派生类直接调用,不再遵式分离资源获取和初始化循普通继承链面试官通过这些问题评估候选人对C++核心概念的深入理解,特别是内存管理和对象模型解答时,不仅需要知道是什么,还要解释为什么和如何工作展示对实际问题的认识如循环引用导致内存泄漏以及解决方案如使用weak_ptr打破循环尤为重要准备面试的最佳策略是理解底层机制而非仅仅记忆语法例如,理解虚函数动态绑定的实现原理,而不只是知道virtual关键字用法能够讨论设计决策的权衡如性能与灵活性也很重要理想的回答应包含具体例子、常见陷阱及其规避方法,展示实际开发经验和深入思考能力常见面试题剖析
(二)扩容原理与用法vector staticconststd::vector使用连续内存块存储元素,当空间不足时需要扩容扩容过程分配static关键字根据上下文有不同含义更大的新内存块通常是当前容量的
1.5或2倍;将现有元素移动或复制到新位置;•静态局部变量函数调用间保持值释放旧内存扩容是一个代价较高的操作,涉及内存分配和元素移动•静态全局变量限制作用域在文件内性能考量•静态类成员属于类而非对象•reserve预分配容量可避免频繁扩容•静态成员函数无this指针,只能访问静态成员•push_back均摊复杂度为O1const关键字表示不可修改•扩容策略在不同标准库实现中可能不同•const变量创建后不可修改•移动语义减少了扩容时的复制开销•const参数函数内不能修改•const成员函数不能修改对象状态•const与指针const int*vs int*const面试常见的关于vector的延伸问题包括vector vsarray的优缺点对比;vector元素何时会被移动而非复制;迭代器失效的情况及如何避免;vectorbool的特殊实现和问题;如何减少vector内存占用shrink_to_fit;vector线程安全性考虑能够深入讨论这些细节表明对STL实现有良好理解关于static和const的高级话题包括const在编译期优化中的作用;constexpr vsconst;静态初始化顺序问题及解决方案如Meyers Singleton;const成员函数中的mutable关键字;线程安全考虑C++11起static变量初始化是线程安全的;const正确性const correctness设计原则面试官通常期望看到对这些概念如何应用于实际编程的理解,而不仅是语法定义常见编程题实战
(一)二分查找实现C++二分查找是一种高效的搜索算法,要求数组有序通过不断缩小查找范围,每次比较中间元素,实现Olog n时间复杂度实现时需注意边界条件和整数溢出问题int binarySearchconstvector nums,int target{int left=0,right=nums.size-1;while left=right{int mid=left+right-left/2;if nums[mid]==targetreturn mid;else ifnums[mid]targetleft=mid+1;elseright=mid-1;}return-1;//未找到}单链表反转代码模板链表反转是常见的数据结构操作,要求改变节点的next指针方向可以使用迭代或递归方法实现迭代法使用三个指针追踪当前、前一个和下一个节点,空间复杂度O1ListNode*reverseListListNode*head{ListNode*prev=nullptr;ListNode*curr=head;while curr{ListNode*next=curr-next;curr-next=prev;prev=curr;curr=next;}return prev;}常见编程题实战
(二)统计单词频率表达式排序算法组合应用map lambdaSTL使用std::map或std::unordered_map统计文本中单词出现频率是常见的编程任务map提供lambda表达式可以简化自定义排序逻辑std::sort算法接受一个比较函数作为第三个参数,高效的C++编程通常结合多个STL算法解决复杂问题例如,统计词频后,可以使用键值对存储,非常适合频率统计示例代码展示了如何读取文本,分割单词,并统计每个单词lambda表达式提供了简洁的方式定义这个函数示例展示了如何使用lambda表达式按长度、std::transform和std::sort将映射转换为向量并按频率排序STL提供的算法如for_each,的出现次数unordered_map基于哈希表实现,平均查找复杂度为O1,而map基于红黑树,字典序或自定义复合条件排序lambda还可以捕获局部变量,使排序逻辑更加灵活transform,find_if,accumulate等可以组合使用,创建简洁高效的解决方案,避免手动循环和查找复杂度为Olog n但保持有序条件判断map统计单词频率的核心代码如下,展示了如何使用STL容器和算法解决实际问题unordered_map wordCount;string word;while cinword{//可选转小写并移除标点transformword.begin,word.end,word.begin,::tolower;word.eraseremove_ifword.begin,word.end,[]char c{return!isalphac;},word.end;if!word.empty++wordCount[word];}//转换为vector以便排序vector wordFreqwordCount.begin,wordCount.end;sortwordFreq.begin,wordFreq.end,[]const autoa,const autob{return a.secondb.second;};疑难解答与答疑汇总内存管理误区类与对象陷阱使用疑难STL初学者常见误区包括混淆栈和堆的使用场景;忘记面向对象编程常见问题包括忘记虚析构函数导致内STL使用中的常见困惑包括迭代器失效条件;容器释放动态分配的内存;使用已释放的内存悬垂指存泄漏;浅拷贝引起的double free;对象切片丢失选择依据;算法复杂度预期;自定义类型作为键的要针;double free错误;数组与指针混淆解决方案多态行为;滥用虚函数导致性能问题;构造函数中调求;线程安全考虑熟悉STL文档、理解容器内部实是采用RAII原则和智能指针,避免直接管理内存理用虚函数的问题深入理解C++对象模型和SOLID设现和迭代器类别是解决这些问题的关键避免在循环解对象生命周期和析构顺序对避免资源泄漏至关重计原则有助于避免这些问题中修改容器导致迭代器失效是常见建议要处理C++疑难问题的经验技巧
1.使用静态分析工具如cppcheck,clang-tidy发现潜在问题
2.使用内存检测工具如Valgrind,AddressSanitizer查找内存错误
3.编译警告视为错误-Wall-Werror,强制解决潜在问题
4.遵循RAII原则,资源获取和释放配对
5.防御性编程,检查边界条件和异常情况
6.编写单元测试,特别是针对边界条件
7.代码评审,让其他人帮助发现潜在问题现代C++编程建议优先使用auto避免类型错误;使用初始化列表避免未初始化变量;通过std::move优化性能;优先使用范围for循环简化迭代;合理使用const提高代码安全性和可读性;了解编译器优化原理,编写对优化友好的代码;采用一致的编码规范提高可维护性持续学习是C++开发者的必备品质,语言标准和最佳实践不断演进,保持更新知识至关重要总结与学习建议高级主题探索模板元编程、设计模式、多线程优化实战项目锻炼参与开源项目、构建个人作品集系统性理论学习深入理解STL、内存模型、性能优化扎实基础掌握语法、数据结构、面向对象编程C++学习是一个持续的过程,建议采用以下学习路线首先掌握基础语法和概念,如变量类型、控制结构、函数和基本面向对象编程;然后深入了解指针、引用、内存管理等C++核心特性;接着学习STL库的使用,掌握各种容器、算法和迭代器;最后探索高级主题,如模板元编程、多线程并发、性能优化等推荐学习资源包括•经典书籍《C++Primer》《Effective C++》《深度探索C++对象模型》•现代C++书籍《Effective ModernC++》《C++Concurrency inAction》•在线资源cppreference.com、isocpp.org、CppCon会议视频•实践平台LeetCode、GitHub开源项目、个人项目•工具精通调试器、静态分析工具、性能分析工具持续进步的建议建立编码习惯,每天至少编写一些C++代码;阅读优质开源项目代码,学习最佳实践;参与社区讨论,与其他开发者交流;保持对新标准和特性的关注;解决实际问题,理论结合实践记住,C++的精通需要时间和耐心,但这种投资将为你的编程能力带来长期回报。
个人认证
优秀文档
获得点赞 0