当前位置:   article > 正文

c++实战知识点

c++实战知识点
  • 数据的分类

    • 数字、字符、字符串
  • 变量的作用域

    • 全局变量:在全部函数外面定义。在定义位置之后的任意函数中都能访问。
    • 局部变量:在函数和语句块内部定义。在函数返回或语句块结束时由系统回收。
    • 函数的参数是该函数的局部变量。
    • 函数内部用static修饰的是静态局部变量。只执行初始化一次,直到程序运行结束回收。

    静态局部变量作用域被限制在定义它们的函数内。这是因为静态局部变量的存在是为了保存函数内的状态,而不影响全局或外部状态。如果一个函数既可以用全局变量也可以用静态局部变量满足该函数要求,优先考虑静态局部变量,因为更安全。

  • 字符的本质

    • 字符的本质是整数,取值范围是0~127。
    • 在书写的时候可以用单引号包含,也可以用整数。
    • 可以与整数进行任何运算,运算的时候,可以用符号,也可以用整数。
    • 在ASCII码表中,0~31是控制字符,只能用十进制表示,不能用符号表示。因此用转义字符来书写它们。
    • image-20240508151707576
  • C++11的原始字面量

    • image-20240508175649301

    • 如果在字符串中存入路径,需要两个\\,以免一个\后面的字符被认为是控制字符。

    • 如果路径过长,再输入一个\比较麻烦,因此用原始字面量。

    • image-20240508175920896

    • 此外,如果字符串过长,可以用原始字面量避免用连接符连接字符串,还可以顺便对齐格式。

    • image-20240508180213324

  • 数据类型的别名

    • image-20240508190658806
  • const修饰指针

    • 常量指针:const 数据类型 *变量名
      • 不能通过解引用的方法修改内存地址中的值,可以用原始的变量名修改。
      • image-20240509083021834
    • 指针常量:数据类型 * const 变量名
      • 指向的变量(对象)不可以改变。
      • 在定义的同时必须初始化。
      • 可以通过解引用修改内存地址中的值。
    • 常指针常量:const 数据类型 * const 变量名
      • 指向的对象不可改变,不能通过解引用的方法修改内存地址中的值
  • void关键字

    • 函数的形参用void*,表示接受任意数据类型的指针。传入函数的指针不需要进行强制类型转换。
    • 不能用void声明变量,它不代表一个真实的变量。
    • 不能对void*指针直接解引用,需要转换为其他类型的指针。
    • 把其他类型的指针赋值给void*指针不需要转换。
    • 把void*指针赋值给其他类型的指针需要转换。
    • 在C++中,在控制台打印字符型变量的地址会有bug。因此转换为void*类型再打印。
    • image-20240509111440105
  • 内存模型

    • image-20240509111920968
  • 二级指针

    • 指针是指针变量的简称,也是一种变量,是变量就有地址。
    • 指针用于存放普通变量的地址,二级指针用于存放指针变量的地址。
    • image-20240509191056915
    • 把普通变量的地址传入函数后可以在函数中修改变量的值,把指针的地址传入函数后可以在函数中修改指针的值。
    • image-20240509191500173
  • 函数指针和回调函数

    • 使用函数指针:
      • 声明函数指针
      • 让函数指针指向函数的地址
      • 通过函数指针调用函数
    • image-20240509201930380
    • 实际业务:回调函数。我们写回调函数的时候,只确定回调函数的种类,不关心具体的功能实现。
    • image-20240509204145002
    • image-20240509204707884
  • 数组

    • 关于为什么数组当函数参数传入函数,同时也要传数组的长度?数组作为函数参数时被解释为指针。
      • image-20240510202644792
    • 数组的排序(qsort)
      • void qsort(void *base, size_t nitems, size_t size, int (*compar)(const void *, const void *))
      • 参数:起始地址、元素个数、元素大小、回调函数地址
      • image-20240510210051983
      • image-20240510210314646
  • C风格字符串

    • C语言:如果char类型的数组的末尾包含了\0,那么该数组中的内容就是一个字符串。
      • image-20240510211235293
    • 清空字符串:memst
      • image-20240510211633561
    • 字符查找strchr
    • 字符串查找strstr
    • image-20240510212442591
  • 二维数组用于函数的参数

    • 行指针(数组指针)

      数据类型 (*行指针名) [行的大小]

    • image-20240514153203511

    • 一维数组名被解释为数组第0个元素的地址。

    • 对一维数组名取地址得到的是数组的地址,是行地址。

    • image-20240514165547496

    • image-20240514165942475

    • image-20240514170012806

    • image-20240514181056367

    • image-20240514181226114

  • 引用

    • 引用作为函数参数,可以避免使用二级指针。

    • 引用的形参与const

      • 如果函数的实参不是左值或与const引用形参的类型不匹配,那么c++将创建正确类型的匿名变量,将实参的值传递给匿名变量,让形参来引用该变量。

        image-20240515092032349

      • image-20240515091317849

      • image-20240515092202386

  • 各种形参的使用场景

    • 不需要在函数中修改实参
      • 如果实参很小,是基本数据类型或很小的结构体,用值传递。
      • 如果实参是数组,则使用const指针,这是唯一选择。(没有为数组建立引用的说法)
      • 如果实参是较大的结构体,则使用const指针或const引用。
      • 如果实参是类,则使用const引用,传递类的标准方式是引用传递。
    • 需要在函数中修改实参。
      • 如果实参是内置数据类型,则使用指针。
      • 如果实参是数组,只能使用指针。
      • 如果实参是结构体,使用指针或引用。
      • 如果实参是类,使用引用。
  • 重载operator new 和 operator delete

    • image-20240517094353979
    • image-20240517094656095
    • image-20240517095010896
  • 内存池

    ​ 该类占有8个字节大小,因此设计一个18字节大小的内存池,分为两块,每块第一个字节表示该块是否被占用。

    • image-20240517105905250

    • 重载的operator new 和 operator delete函数都是静态函数,因此无法使用类的普通成员变量。

    • image-20240517104706367

    • image-20240517111057735

    • image-20240517111124775

    • image-20240517111152132

    • image-20240517111028777

  • 继承

    • 继承方式
      • image-20240520214537795
    • 不管继承方式如何,基类中的private成员在派生类中始终不能使用。
    • 使用using关键字可以改变基类成员在派生类的访问权限。
      • image-20240520215131918
  • 继承的对象模型

    • 创建派生类时,先调用基类的构造函数,再调用派生类的构造函数。
    • 基类的构造函数负责初始化被继承的数据成员,派生类的构造函数主要用于初始化新增的数据成员。
    • 创建派生类对象时,只会申请一次内存,派生类对象包含了基类对象的内存空间,this指针是相同的。
      • image-20240521000740428
      • image-20240521000751467
      • image-20240521000758450
      • image-20240521000727405
  • 名字遮蔽与类作用域

    • 如果派生类中的成员(成员变量和成员函数)和基类中的成员重名,通过派生类对象或者在派生类的成员函数中使用该成员时,将使用派生类新增的成员,而不是基类的。注意:如果派生类中有同名函数,将会遮蔽基类中所有的同名函数。
  • 继承的特殊关系

    • 可以把派生类对象赋值给基类对象,但是会舍弃非基类的成员。
    • 基类指针可以在不进行显示转换的情况下指向派生类对象。
    • 基类引用可以在不进行显示转换的情况下引用派生类对象。
    • 基类指针和引用只能调用基类的方法,不能调用派生类的方法。
      • image-20240521003139614
      • image-20240521003205262
      • image-20240521003030199
      • image-20240521003227514
  • 运行阶段类型识别-dynamic cast

    • 基类指针可以指向派生类对象,如何知道基类指针指向的是哪种派生类的对象,想调用派生类的非虚函数。
    • dynamic cast用指向基类的指针生成派生类指针。
    • image-20240523085408882
    • dynamic cast只适用于包含虚函数的类。
  • typeid运算符和type_info类

    • typeid运算符用于获取数据类型的信息。
    • 语法
      • typeid(数据类型)
      • typeid(变量名或表达式)
    • typeid运算符返回type_info类(#include)的对象的引用。
    • image-20240523090102995
    • image-20240523090351848
    • typeid重载了==和!=运算符,因此可以用于多态的场景,在运行阶段识别对象的数据类型。
      • image-20240523091352385
      • image-20240523091448385
  • 函数模板

    • 可以为类的成员函数、构造函数创建模版,但是不能为虚函数和析构函数创建模板。
    • 关于函数模板的特化,对于自定义数据类型。
      • 比如swap交换两个对象,但是自定义数据类型,我们只想交换其中某一种变量,比如说age,而不是整个对象。为此就需要特化。
      • image-20240524094948462
      • image-20240524095336148
      • 有两种方法,推荐使用类成员函数+非成员函数方法:
        • image-20240524095534650
        • image-20240524095846554
    • 关于函数模板的分文件编写
      • 普通函数的声明在.h中,实现在.cpp中。
      • 函数模板的声明和实现都在.h中。
      • 特化函数模板的声明在.h中,实现在.cpp中。
    • 假如有一个场景:
      • image-20240526193411571
      • 怎么定义tmp的类型?
        • image-20240526193745572
    • decltype关键字
      • decltype用于查询表达式的数据类型
        • decltype(expression) var;
          • decltype分析表达式并得到它的类型,并用该类型声明一个变量var。
      • 如果expression是没有用括号扩起来的标识符,则var的类型与该标识符的类型相同,包括const限定符。
        • image-20240526194453169
      • 如果expression是函数调用,则var的类型与函数的返回值类型相同。
        • image-20240526194621246
        • image-20240526194645901
        • image-20240526194709780
      • 如果expression是左值(能取地址)、或者用括号扩起来的标识符,那么var的类型是expression的引用。
        • image-20240526194911177
        • image-20240526194950934
    • 函数后置返回类型。
      • int fun(int x , double y) == auto fun(int x, double y) -> int
        • auto是一个占位符,为函数返回值占了一个位置。
      • 回到最开始的func函数中,假设我们想要返回tmp。
        • image-20240526195325424
      • 因为tmp的类型我们不知道,因此必须使用后置返回类型才可以返回tmp。
        • image-20240526195526279
  • 模板类

    • image-20240527093055605

    • image-20240527093115435

    • 嵌套和递归使用模版类

      • 再写一个自定义的Vector
        • image-20240527111336564
        • image-20240527111344654
      • image-20240527111754432
      • 接下来我们尝试用vector的自动扩容函数,会发现有个问题。
        • image-20240527111902394
        • image-20240527111919617
        • 这个问题在于,resize函数中会将指向原来的内存空间指针释放,如果发生浅拷贝,那么新指针中的内容也会被释放,造成悬空指针。如果不delete之前的指针,会造成内存泄漏,治标不治本。
          • image-20240527112710753
        • 既然tmp[i] = data_[i] 会调用类的拷贝赋值函数,之前Stack的拷贝赋值函数没有重写,导致浅拷贝,那么增加深拷贝代码即可解决问题。
          • image-20240527114146037
        • 或者使用移动语义,Stack提供移动构造和赋值函数。
          • image-20240527114842029
          • image-20240527114858680
    • 模板类与继承

      • 模板类继承普通类
        • image-20240527194713329
      • 普通类继承模板类
        • image-20240527194954713
      • 普通类继承模板类实例版本
        • image-20240527194837173
      • 模板类继承模板类
    • 模板类与函数

      • 类的实例化版本
        • image-20240528101742640
      • 函数模板
        • image-20240528104621930
        • image-20240528181314593
        • image-20240528181456823
      • image-20240528182035041
    • 模板类与友元

      • 约束模板友元:模板类实例化时,每个实例化的类对应一个友元函数。
        • image-20240528184147187
      • 非约束模板友元:模板类实例化时,每个实例化的类对应n个友元函数。
        • image-20240528184352992
    • 模板类嵌套模板类

      • image-20240528185248042
  • 编译预处理

    • C++中常用的宏
      • 当前源代码文件名:_FILE_
      • 当前源代码函数名:_FUNCTION_
      • 当前源代码行号:_LINE_
      • 编译的日期:_DATE_
      • 编译的时间:_TIME_
      • 编译的时间戳:_TIMESTAMP_
      • 当用C++编译程序时,宏__cplusplus就会被定义,用于区分c和c++
  • 编译和链接

    • image-20240523201219563
    • 对单个文件进行编译
      • image-20240523202246290
      • image-20240523202309041
    • main函数依赖两个文件
      • image-20240523202337864
    • 把所有需要的文件编译完
      • image-20240523202426481
    • 所有文件编译完成后,开始生成.exe
      • image-20240523202500206
      • image-20240523202554084
    • 分开编译的好处:每次只编译修改过的源文件,然后再链接,效果最好。
    • 编译单个*.cpp文件的时候,编译器只需要知道名称的存在,不会把它们的定义一起编译。
    • 但是函数或类的定义不存在,编译不会报错,链接会出现无法解析的外部符号。
    • 链接的时候,变量、函数和类的定义只能有一个,否则会出现重定义的错误。
    • 把变量、函数和类的定义放在*.h中是不规范的做法,如果*.h被多个*.cpp包含,会出现重定义。
    • 尽可能不使用全局变量,如果一定要用,要在*.h文件中声明(需要加extern关键字),在*.cpp中定义。
      • image-20240523203459321
      • image-20240523203507034
      • image-20240523203514263
      • image-20240523203523690
    • 全局的const常量只在单个文件中有效。在头文件中定义。
  • C++类型转换

    • C++认为C风格的类型转换可能会带来隐患,提供了四个关键字:static_cast,const_cast,reinterpret_cast和dynamic_cast用于类型转换。

      • static_cast

        • 用于内置数据类型之间的转换
          • image-20240528201319501
        • 用于指针之间的转换
          • image-20240528201422397
      • reinterpret_cast

        • static_cast不能用于转换不同类型的指针,reinterpret_cast可以。
        • image-20240528201925352
        • image-20240528202259241
      • const_cast

        • static_cast不能丢掉指针(引用)的const和volitale属性,const_cast可以。
        • image-20240528202954758
      • dynamic_cast

        • dynamic_cast是 C++ 中用于在运行时进行类型安全的强制类型转换的一种运算符。它主要用于在类层次结构中,将基类指针或引用转换为派生类指针或引用。dynamic_cast 需要运行时类型信息 (RTTI) 的支持,因此它只能在包含虚函数的多态类型中使用。

        • 源类型必须是指针或引用,目标类型必须是多态类型。

        • 假设有以下类层次结构:

          class Base {
              virtual void foo() {} // 必须有虚函数
          };
          
          class Derived : public Base {
              void bar() {}
          };
          
          class AnotherDerived : public Base {
              void baz() {}
          };
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 向下转型
          Base* basePtr = new Derived();
          Derived* derivedPtr = dynamic_cast<Derived*>(basePtr);
          if (derivedPtr) {
              derivedPtr->bar(); // 转换成功,可以调用 Derived 类的方法
          } else {
              // 转换失败,derivedPtr 为 nullptr
          }
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 失败的情况
          cpp复制代码Base* basePtr = new AnotherDerived();
          Derived* derivedPtr = dynamic_cast<Derived*>(basePtr);
          if (derivedPtr) {
              derivedPtr->bar(); // 不会执行,因为转换失败
          } else {
              // 转换失败,derivedPtr 为 nullptr
          }
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 引用转换
          Base& baseRef = *basePtr;
          try {
              Derived& derivedRef = dynamic_cast<Derived&>(baseRef);
              derivedRef.bar(); // 如果转换成功,可以调用 Derived 类的方法
          } catch (const std::bad_cast& e) {
              // 转换失败,抛出 std::bad_cast 异常
              std::cerr << "bad_cast: " << e.what() << std::endl;
          }
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
  • 容器

    • string容器

      • 构造和析构

        • 静态常量成员string::npos为字符数组的最大长度。
        • image-20240529115158241
        • image-20240529134000526
      • string容器操作

        • image-20240529183411961
        • image-20240529183442020
        • image-20240529183617718
        • image-20240529183718197
        • image-20240529183751138
        • image-20240529183930209
        • image-20240529183945027
        • image-20240529184034204image-20240529184114211
        • image-20240529184150829
        • image-20240529184211878
    • vector容器

      • 构造和析构
        • image-20240529185552583
      • vector容器操作
        • image-20240529190121236
        • image-20240529190147010
        • image-20240529190251518
        • image-20240529190401944
        • image-20240529190437373
        • image-20240529190548303
        • image-20240529190729763
    • 迭代器

      • 正向迭代器
        • image-20240529194546460
        • image-20240529194558889
      • 双向迭代器
        • image-20240529194940222
        • image-20240529195027494
      • 随机访问迭代器
        • image-20240529195046436
    • 关于容器中的范围for循环、

      • 如果容器中的元素是结构体和类,迭代器变量应该申明为引用,只读加const约束。
      • image-20240529201206130
      • image-20240529201335054
      • 如果不加&,每次都是把v中的元素拷贝给i,开销很大。
      • image-20240529201419198
    • list容器

      • list容器封装了双链表。
      • 构造函数
        • image-20240529202403819
        • image-20240529202727438
      • 容器的操作
        • image-20240530084117076
        • image-20240530084140753
        • image-20240530084216469
        • image-20240530084305124
        • image-20240530084447815
        • image-20240530084608868
    • pair键值对

      • image-20240530090729690
      • 编译器做了优化,匿名对象和临时对象不调用拷贝构造函数
        • image-20240530090620095
        • image-20240530090654381
    • map

      • map容器的元素是pair键值对。
      • 构造函数
        • image-20240530190010671
      • 容器的操作
        • image-20240530190211001
        • image-20240530190234480
          • 关于[]运算符:如果指定键不存在,会向容器中添加新的键值对,如果指定键存在,则读取或修改容器中指定键的值。
          • at()成员函数:如果指定键不存在,不会向容器中添加新的键值对,抛出out_of_range异常。
        • image-20240530190630744
        • image-20240530190705235
        • image-20240530190714946
        • image-20240530190746740
        • image-20240530191703496
        • map的分段构造。
          • image-20240530195121034
          • image-20240530195754786
    • unordered_map

      • 封装了哈希表,查找、插入和删除元素时,只需要比较几次key的值。
      • image-20240531090816057
      • 构造函数(和map一样)
        • image-20240531090843281
      • 容器操作
        • image-20240531091303075
        • image-20240531091444188
        • image-20240531091535489
        • image-20240531091556118
        • image-20240531091619541
        • image-20240531091733334
        • image-20240531092143839
    • queue容器

      • 逻辑结构是队列,物理结构可以是数组或链表,主要用于多线程之间的数据共享。
      • 不支持迭代器
      • 构造函数
        • image-20240531092639202
        • 不能用vector容器构造queue。
          • image-20240531093214693
      • 容器操作
        • image-20240531092717631
        • image-20240531092748561
    • 其他容器

      • array

        • image-20240531093453934
        • image-20240531093507454
        • image-20240531093822616
        • image-20240531094830168
        • image-20240531131457670
        • image-20240531131518099
    • set & multiset

      • set中的元素只有关键字。
    • priority_queue

      • 优先级队列相当于一个有权值的单向队列,在这个队列中,所有元素是按照优先级排序的。
  • STL算法

    • for_each
      • 模版编程,通过传迭代器来支持多种类型。
      • image-20240603134306132
      • 如果需要遍历的时候,使用的方法自定义,一种是用函数指针传给foreach具体的方法,一种是仿函数。
      • image-20240603140204395
      • image-20240603141910938
      • 这两个方法除了foreach第三个参数,其他地方都是一样的,也可以用模板来修改为通用。
      • image-20240603142206185
      • 现在有一个问题,不管是函数还是仿函数,都只支持string类型,现在修改为模板函数。
      • image-20240603142521639
      • 如果使用stl的for_each函数,结果也是一样,说明我们实现的foreach和for_each是一样的。
      • image-20240603142659610
    • find_if
      • image-20240603143455647
      • image-20240603144211397
      • image-20240603144220919
      • 关于为什么stl算法可以用仿函数?仿函数写起来感觉比普通函数麻烦,那么仿函数有什么用?
      • 假设我们需要自定义想要的元素,那么普通函数应该这么修改。
        • image-20240603145535502
        • image-20240603145558869
      • 假如我们需要的参数越来越多,那么普通函数所需要的参数也就会越来越多,我们的框架(算法findif)也需要一同修改,这个不符合代码规范。除非用回调函数传参数的方法。如果用仿函数就不存在这个问题了。
        • image-20240603150034090
        • image-20240603150046698
    • sort
      • image-20240603152749327
      • 适用于数组容器vector、string、deque,list容器有成员函数sort,红黑树和哈希表容器没有排序的说法。

img-Bz3ETlOV-1717401431032)]
- 关于为什么stl算法可以用仿函数?仿函数写起来感觉比普通函数麻烦,那么仿函数有什么用?
- 假设我们需要自定义想要的元素,那么普通函数应该这么修改。
- [外链图片转存中…(img-Y1bScXWz-1717401431032)]
- [外链图片转存中…(img-FQhJVxCR-1717401431032)]
- 假如我们需要的参数越来越多,那么普通函数所需要的参数也就会越来越多,我们的框架(算法findif)也需要一同修改,这个不符合代码规范。除非用回调函数传参数的方法。如果用仿函数就不存在这个问题了。
- [外链图片转存中…(img-2VvZokPa-1717401431032)]
- [外链图片转存中…(img-EXWMn19p-1717401431032)]

  • sort
    • [外链图片转存中…(img-gIJ6nfSP-1717401431032)]
    • 适用于数组容器vector、string、deque,list容器有成员函数sort,红黑树和哈希表容器没有排序的说法。
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Monodyee/article/detail/691476
推荐阅读
相关标签
  

闽ICP备14008679号