C++ 知识点杂记

  •  STL vector 进行扩容时, 内部数据是通过移动到新的内存空间,这样节省了拷贝与释放原有数据的额外开销,实际上只是对原有申请的内存进行了释放;
  • 智能指针 shared_ptr : 循环引用 share_ptr循环引用产生原因及其解决方案_shared_ptr循环引用_路人甲同学的博客-优快云博客
  • 不能共享的资源: IO缓冲 or 指针
    • IO 类与unique_ptr类只能进行移动而不是拷贝;
    • 标准库容器,string 和shared_ptr可以进行拷贝与移动;
  • 不抛出异常的移动构造函数以及移动赋值运算符都指定noexcept;同时在头文件的声明与定义中noexcept都不能省略;
  • 面向对象程序设计的三大特性:
    • 继承:OOB 设计中代码复用的手段,在保留原本类特性的基础上进行扩展;
    • 封装:
    • 多态:
  • 菱形继承 && 菱形虚拟继承:菱形继承可能会导致数据冗余和二义性的问题;但可以通过虚拟继承进行解决
  • 友元关系是不能继承的,基类的友元函数不能访问子类的私有和保护成员;
  • 虚表与虚指针:
    • 编译器在编译时,当基类含有虚函数时,为每个含有虚函数的类创建虚表——一个存储虚函数地址的一维数组;而当一个子类继承并重写了基类的虚函数时,它也会有自己的虚表;
      • 先将基类的虚函数表内容拷贝一份到派生类的虚函数表中;
      • 如果派生类重写了基类中的某些虚函数,则用派生类自己的虚函数覆盖基类中的虚函数;
      • 派生类自己新增的虚函数,按其在派生类中的声明次序,增加到派生类虚函数表的最后;
    • 编译器会为每个带有虚函数的类的对象自动创建一个虚表指针,这个指针指向了对象所属类的虚表。
    • 在程序运行时,根据对象的类型去初始化虚表指针,从而让虚表指针正确的指向所属类的虚表。所以在调用虚函数时,就能够找到正确的函数;
  • 构造函数与虚函数:
    • vptr : 虚函数的调用是通过虚函数表来查找的,而虚函数表由类的实例化对象的vptr指针(vptr可以参考C++的虚函数表指针vptr)指向,该指针存放在对象的内部空间中,需要调用构造函数完成初始化。如果构造函数是虚函数,那么调用构造函数就需要去找vptr,但此时vptr还没有初始化!
    • 多态角度: 虚函数在程序运行时明确绑定对象后,调用绑定对象的虚函数;而构造函数是在创建对象时自己主动调用的,不可能通过父类的指针或者引用去调用,因此使用虚函数也没有实际意义。并且构造函数的作用是提供初始化,在对象生命期仅仅运行一次,不是对象的动态行为,没有必要成为虚函数。
  • 多重继承时,基类构造函数的调用顺序与子类声明时基类出现的顺序相关;
  • RAII 机制:资源获取即初始化
  • malloc/free 和 new/delete
    • 共同点是:都是从堆上申请空间,并且需要用户手动释放。
    • 不同的地方是:
      1. malloc 和 free 是函数, new 和 delete 是操作符;
      2. malloc 申请的空间不会初始化, new 可以初始化;
      3. malloc 申请空间时,需要手动计算空间大小并传递, new 只需在其后跟上空间的类型即可,如果是多个对象, [] 中指定对象个数即可;
      4. malloc 的返回值为 void* , 在使用时必须强转, new 不需要,因为 new 后跟的是空间的类型;
      5. malloc 申请空间失败时,返回的是 NULL ,因此使用时必须判空, new 不需要,但是 new 需要捕获异常;
      6. 申请自定义类型对象时, malloc/free 只会开辟空间,不会调用构造函数与析构函数,而 new在申请空间后会调用构造函数完成对象的初始化, delete 在释放空间前会调用析构函数完成空间中资源的清理。
  • 空指针:指针变量指向内存中编号为0的空间,用于初始化指针变量。空指针指向的内存不可以访问。
  • 野指针:
    • 指针变量中的值是非法的内存地址,进而形成野指针;野指针不是NULL指针,是指向不可用内存地址的指针。
    • 下列情况会出现野指针:
    1.  指针变量未初始化:任何指针变量刚被创建时不会自动成为NULL指针,它的缺省值是随机的,它会乱指一气,此时若未初始化,则产生野指针。

    2. 指针释放后未置空:有时指针在free或delete后未赋值 NULL,便会使人以为是合法的,此时指针指向一块未定义、未分配的内存。其实free和delete只是把指针所指的内存给释放掉,但并没有把指针本身干掉。指针指向的就是“垃圾”内存。所以释放后的指针应立即将指针置为NULL,防止产生“野指针”。

    3. 指针操作超越变量作用域:

  • struct 内存对齐原则:

    1. 有效对齐值:是对齐模数(指定对齐值)和结构体中最长数据类型长度(自身对齐值)中较小的那个。有效对齐值也叫对齐单位。

    2. 结构体第一个成员的偏移量(offset)为0,以后每个成员相对于结构体首地址的 offset 都是该成员大小与有效对齐值中较小那个的整数倍,如有需要编译器会在成员之间加上填充字节。

    3. 结构体的总大小为 有效对齐值 的整数倍,如有需要编译器会在最末一个成员之后加上填充字节。结构体内所有数据成员各自内存对齐后,结构体本身还要进行一次内存对齐,保证整个结构体占用内存大小是结构体内最大数据成员的最小整数倍

    4. 结构体作为成员:如果一个结构里有某些结构体成员,则结构体成员要从其内部"最宽基本类型成员"的整数倍地址开始存储.(struct a里存有struct b,b里有char,int ,double等元素,那b应该从8的整数倍开始存储.)。

  • 内存对齐的作用:

    1.  平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。

    2. 性能原因:经过内存对齐后,CPU的内存访问速度大大提升,减少寄存器操作次数;

  • std::move && std::forward

  • 空类:如果你只是声明一个空类,不做任何事情的话,编译器会自动为你生成一个默认构造函数、一个拷贝默认构造函数、一个默认拷贝赋值操作符、一个默认析构函数、取址运算符和一个取址运算符const。这些函数只有在第一次被调用时,才会别编译器创建。所有这些函数都是inline和public的。

  • 空类占据1个字节,实例化过程实质上是在内存上分配特定空间,必须有大小;

    1. 当该空白类作为基类时,该类的大小就优化为0了,这就是所谓的空白基类最优化。

    2. 空白基类最优化无法被施加于多重继承上只适合单一继承。

  • 类的大小与它当中的构造函数,析构函数,以及其他的成员函数无关,只与它当中的成员数据有关.

    1. 为类的非静态成员数据的类型大小之和.

    2. 有编译器额外加入的成员变量的大小,用来支持语言的某些特性(如:指向虚函数的指针).

    3. 为了优化存取效率,进行的边缘调整.

    4. 与类中的构造函数,析构函数以及其他的成员函数无关.

  • extern : 可以在一个文件中引用另一个文件中定义的变量或者函数;

    1. 注意只有当一个变量是一个全局变量时,extern变量才会起作用。

    2. extern 关键字实质上是声明,别的地方必须实现定义。

    3. extern关键字只需要指明类型和变量名就行了,不能再重新赋值,初始化需要在声明处进行。

  • static :  作用 :https://www.cnblogs.com/songdanzju/p/7422380.html

    1. 隐藏:当同时编译多个文件时,所有未加static前缀的全局变量和函数都具有全局可见性。加了static,就会对其它源文件隐藏。例如在a和msg的定义前加上static,main.c就看不到它们了。利用这一特性可以在不同的文件中定义同名函数和同名变量,而不必担心命名冲突。static可以用作函数和变量的前缀,对于函数来讲,static的作用仅限于隐藏.

    2. 保持变量内容的持久:存储在静态数据区的变量会在程序刚开始运行时就完成初始化,也是唯一的一次初始化。共有两种变量存储在静态存储区:全局变量和static变量,只不过和全局变量比起来,static可以控制变量的可见范围,说到底static还是用来隐藏的。

    3. 默认初始化为0(static变量): 其实全局变量也具备这一属性,因为全局变量也存储在静态数据区。在静态数据区,内存中所有的字节默认值都是0x00,某些时候这一特点可以减少程序员的工作量。比如初始化一个稀疏矩阵,我们可以一个一个地把所有元素都置0,然后把不是0的几个元素赋值。如果定义成静态的,就省去了一开始置0的操作。再比如要把一个字符数组当字符串来用,但又觉得每次在字符数组末尾加‘\0’;太麻烦。如果把字符串定义成静态的,就省去了这个麻烦,因为那里本来就是‘\0’;

  • static的最主要功能是隐藏,其次因为static变量存放在静态存储区,所以它具备持久性和默认值0. 

  • C++ 内存分区:栈、堆、全局/静态存储区、常量存储区、代码区。

    1. 栈: 存放函数的局部变量、函数参数、返回地址等,由编译器自动分配和释放。
    2. 堆: 动态申请的内存空间,就是由 malloc 分配的内存块,由程序员控制它的分配和释放,如果程序执行结束还没有释放,操作系统会自动回收。
    3. 全局区/静态存储区(.bss 段和 .data 段): 存放全局变量和静态变量,程序运行结束操作系统自动释放,在 C 语言中,未初始化的放在 .bss 段中,初始化的放在 .data 段中,C++ 中不再区分了。
    4. 常量存储区(.data 段): 存放的是常量,不允许修改,程序运行结束自动释放。
    5. 代码区(.text 段): 存放代码,不允许修改,但可以执行。编译后的二进制文件存放在这里。
       
  • const: c++中const的作用_const在c++中的作用_顺其自然~的博客-优快云博客
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值