【cpp进阶-04】C++的底层实现细节

CPP底层细节

面向对象

类与对象

类在编译时不占用内存空间,只是对象的模板。

对象创建时在栈或堆分配内存。

成员变量与成员函数

image-20230222103428783

成员变量分别存储在堆或栈中;成员函数(编译时经过名字编码为对象无关的全局函数,传入this指针)共享内存。

  • 静态变量

    静态成员变量存储在静态区,类的对象共享。

    静态成员函数存储在代码区,但不传入this指针。

构造函数

  • 自动生成

    用户未定义构造函数,编译器自动生成默认构造函数;用户定义构造函数后,编译器不再自动生成。

  • 初始化列表:对象类型,const类型,引用类型必须初始化构造。

    step1:分配内存空间

    step2:初始化成员变量

    step3:调用构造方法实现体

析构函数

对象类型会自动调用,指针类型需要delete调用。

三大特性

继承

  • 虚函数

    • 成员变量:

      若含有虚函数表指针 _vptr,虚函数表指针→基类变量→派生类变量

      若不含虚函数表指针_vptr,基类变量→派生类变量。

    • 成员函数:

      case 普通函数:
      	根据指针的类型,调用指针类的函数。
      case 虚函数:
      	通过指针指向的对象的虚函数表找到函数,根据指针指向对象的类调用函数。
      	
      注意点:
      ​		派生类无法重载基类,同名遮蔽。
      ​		初始化列表不能写继承变量,需要借助基类的构造函数初始化。
      
      在这里插入图片描述
    • 构造函数与析构函数

      构造前未初始化虚函数表,构造函数不能设置为虚函数。

      析构函数设置为虚函数,防止内存泄漏。

  • 多继承:内存模型如下

    img
  • 虚继承

    非虚继承存在二义性,重复定义的问题,虚继承用于解决菱形继承的问题。

    实现:例如A->B1,B2->C

    B1,B2分别保存A的虚基类指针,则C在取变量时不存在二义性。

多态

  • 介绍

    静态多态:编译时多态,重载、模板实现。

    动态多态:运行时多态,虚函数。

内存模型小结
若无虚函数,虚继承->普通对象内存模型
含有虚函数->增加虚函数表
含有虚继承->增加虚基类表
  • 模板与泛型

    1. 编译器的自动类型推导

    2. 两次编译:声明处对模板编译,调用处参数替换后编译

    3. 优势:提高复用性;编译时检查,安全性高

  • RTTI(Run-Time Type Identification)

    typeid运算符:返回表达式或类型的实际类型。

    dynamic_cast运算符:将基类指针转化为派生类指针。

    虚函数表中存放type_info来判断类型,上述运算符会通过查虚函数表获取类型。

强类型语言

类型转化

static_cast

静态转化,编译时进行,异常抛出编译时错误。

一般说良性转化,风险度低。

const_cast

去掉表达式中的volatile和const修饰符

volatile:多线程使用,每次从内存中读取数据,保证不被修改。

reinterpret_cast

仅对二进制位重新解释,不会根据规则转化,风险较大

dynamic_cast

继承层次之间的转化

向上转化无条件;向下转化需要RTTI机制辅助。

STL

容器

  • vector:数组;支持快速随机访问
  • list:双向链表;支持快速删除插入
  • deque:指针数组+多个缓冲区;支持首位插入删除,快速随机访问
  • set/map:红黑树;有序,不可重复
  • multiset/multimap:红黑树;有序,可重复
  • hashset/hashmap:hash表,无序,不可重复

适配器

  • stack:list 或 deque实现,不用vector是扩容耗时。
  • queue:list 或 deque实现,不用vector是扩容耗时。
  • priority_queue:vector+heap

扩容机制

每次以2倍或1.5倍扩容,g++2倍,MSVC是1.5倍。每次扩容在堆上开辟新空间。