C++三大特性
https://blog.csdn.net/qq_42021840/article/details/106141333
虚函数、虚指针、虚表
https://blog.csdn.net/qq_42021840/article/details/106150057
虚析构函数
用基类类型指针绑定派生类实例,析构的时候,如果基类析构函数不是虚函数,则只会析构基类,不会析构派生类对象,从而造成内存泄漏。为什么会出现这种现象呢,个人认为析构的时候如果没有虚函数的动态绑定功能,就只根据指针的类型来进行的,而不是根据指针绑定的对象来进行,所以只是调用了基类的析构函数;如果基类的析构函数是虚函数,则析构的时候就要根据指针绑定的对象来调用对应的析构函数了。
构造函数和析构函数中也不能调用虚函数,调用了也没用,一个是虚函数表指针还没有初始化好,一个是可能虚函数指针已经被析构了。
浅拷贝、深拷贝
深拷贝和浅拷贝最根本的区别就是是否真正的获取一个对象的复制体,而不是引用
假如有两个对象A和B,B复制了A,修改A的时候,看B是否发生变化:
如果B也跟着变,说明是浅拷贝(修改的是堆中同一块内存)
如果B没有改变,说明是深拷贝(修改的是堆内存中不同的值)
浅拷贝:增加了一个引用指针指向已存在的内存地址,如果原地址发生改变,那么浅拷贝出来的对象都会发生改变。
深拷贝:增加了一个引用指针指向新申请的内存,用于存放复制的对象,如果原地址发生改变,并不会导致深拷贝出来的对象发生改变。
浅拷贝会导致对象结束时,释放两次析构函数,释放同一块内存。
当数据成员中有指针的时候必须要用深拷贝。
重载、重写 、重定义(隐藏)
重载
同一个可访问区域内具有不同参数列表(个数、类型、顺序)的同名函数,根据参数列表调用不同的函数,重载不关心函数的返回值类型
重写
子类中对父类中的虚函数进行重新定义,其函数名、参数列表、返回值类型必须同基类中被重写的函数一致。
重定义
子类的函数屏蔽了与其同名的基类函数,只要时同名函数,不管参数列表是否相同,基类都会被隐藏。
重载和重写的区别:
- 范围区别:重写和被重写的函数在不同的类中,重载和被重载的函数在同一个类中
- 参数区别:重写与被重写的函数列表一定相同,重载和被重载的函数列表一定不相同
- virtual的区别:重写的基类必须要有virtual关键字,重载函数和被重载函数可以有virtual,也可以没有。
类对象的内存分布
成员变量类型:staic和非staic。成员函数类型:static、非static和virtual。
C++内存格局有四个存储区:全局数据区、代码区、栈区、堆区。
全局数据区:全局变量、静态数据和常量。
代码区:类成员函数代码和非成员函数代码
栈区:为运行函数分配的局部变量、函数参数、返回数据、返回地址等。
堆区:动态申请的内存
在类的定义时
- 类的成员函数放在代码区
- 类的静态成员变量放在全局数据段
- 非静态成员变量在类的实例内,实例在堆区或者栈区
- 虚函数指针、虚基类指针在类的实例内,实例在堆区或者栈区
- 虚表在只读数据区
一个类实例的内存分布:
- 有虚函数的类中,虚函数指针永远时第一成员变量
- 父类的成员变量
- 子类的成员变量
传参方式
- 值传递 :传递的变量本身的值。一般程序中的值传递都是基本数据类型,如int、char等等
在被调函数中只改变形参的值,而不会影响主调函数中实参的值
- 引用传递 :传递的变量是在内存中的地址,在程序中常用的是数组、类和接口
传递的是形参的地址,在被调函数中,对于形参的操作就是相当于操作实参本身
New和malloc的区别
new / delete malloc / free 属性 运算符 库函数 参数 无需指定内存大小
传入申请内存的大小 返回类型 对象类型 void* 分配失败 抛出bac_alloc异常 返回NULL 动作 new申请内存空间后,会调用构造函数,底层是用malloc 只申请内存 重载 可以重载 无法重载 内存区域 自由存储区,抽象的概念 堆区
指针和引用的区别
指针 引用 根本 一个变量,变量存储的是另一个变量的地址 一个别名,还是变量本身,操作引用就是操作变量本身 const 可以有const指针 没有const引用,只有常量引用 int const &a 级数 可以有多级指针 没有多级引用 值 指针的值可以为空,赋值之后指针的值可以改变 引用的值不能为空,定义时必须进行初始化,赋值之后不能改变 sizeof 指针大小 对象类型大小 运算含义 ++指针,所代表指针本身内存地址改变 ++引用,所代表变量本身改变
堆区和栈区的区别
栈区 堆区 管理方式 由编译器自动管理 由程序员管理 空间大小 1M,有限 x86 4G 生长方向 从高到低 从低到高 分配方式 静态、动态(alloca,且无需手动释放) 动态 分配效率 效率高,有专门的寄存器和指令 效率低,需要按照一i的那个算法分配
空类
空类可以进行实例,大小为1,每个实例在内存中都有一个独一无二的地址,为了达到这个目的,编译器往往会给一个空类隐含的加一个字节。
这样空类在实例化后在内存得到了独一无二的地址,所以空类所占的内存大小是1个字节。