C++知识点总结0.1
深拷贝浅拷贝
浅当类的对象需要拷贝时,拷贝构造函数将会被调用:
1. 一个对象以值传递的方式传入函数体
2. 一个对象以值传递的方式从函数返回
3. 一个对象需要通过另外一个对象进行初始化
如果在类中没有显示的声明一个拷贝构造函数的话,当拷贝发生时自动调用默认拷贝构造(浅拷贝)。
如果类拥有资源,当这个类的对象发生复制过程的时候,资源重新分配,这个过程就是深拷贝,反之即为浅拷贝。
虚函数表
虚函数工作原理
编译器给每个对象添加一个隐藏成员。隐藏成员中保存了一个指向函数地址数组的指针。这个数组就是虚函数表。虚函数表中存储了为类对象进行声明的虚函数的地址。
我的理解就是编译器给每个类分配了一张独立的表,有一个指针指向这个表,如果子类重写了父类的虚函数,则子类的重写的虚函数的地址会在虚函数表中覆盖原先的父类的虚函数地址,而如果子类新定义了一个虚函数,则此虚函数的地址也会被添加到其类的虚函数表中。
使用虚函数的成本
- 每个对象都将增大,增大量为存储地址的空间
- 每个类编译器都会穿件一个虚函数地址表
- 对于每个函数的调用都要额外多一个步骤——到表中查找地址
析构函数应当是虚函数,因为在子类对象构造时会自动调用父类的构造,同时在析构的时候如若父类的析构函数非虚函数则调用不到父类的析构函数,可能造成内存泄漏。
STL容器的区别和选择场景
\ | vector | deque | list | set | multiset | map | multimap |
---|---|---|---|---|---|---|---|
典型内存结构 | 单端数组 | 双端数组 | 双向链表 | 二叉树 | 二叉树 | 二叉树 | 二叉树 |
可随机存取 | 是 | 是 | 否 | 否 | 否 | 对key而言:不是 | 否 |
元素搜寻速度 | 慢 | 慢 | 非常慢 | 快 | 快 | 对key而言:快 | 对key而言:快 |
元素安插移除 | 尾端 | 头尾两端 | 任何位置 | - | - | - | - |
- vector的使用场景:比如软件历史操作记录的存储,我们经常要查看历史记录,比如上一次的记录,上上次的记录,但却不会去删除记录,因为记录是事实的描述。
- deque的使用场景:比如排队购票系统,对排队者的存储可以采用deque,支持头端的快速移除,尾端的快速添加。如果采用vector,则头端移除时,会移动大量的数据,速度慢。
vector与deque的比较:
一:vector.at()比deque.at()效率高,比如vector.at(0)是固定的,deque的开始位置 却是不固定的。
二:如果有大量释放操作的话,vector花的时间更少,这跟二者的内部实现有关。
三:deque支持头部的快速插入与快速移除,这是deque的优点。 - list的使用场景:比如公交车乘客的存储,随时可能有乘客下车,支持频繁的不确实位置元素的移除插入。
- set的使用场景:比如对手机游戏的个人得分记录的存储,存储要求从高分到低分的顺序排列。
- map的使用场景:比如按ID号存储十万个用户,想要快速要通过ID查找对应的用户。二叉树的查找效率,这时就体现出来了。如果是vector容器,最坏的情况下可能要遍历完整个容器才能找到该用户。
C++内存泄漏
- 没有匹配调用new/delete,malloc/free
- 使用多态的时候父类没有写虚析构,会导子类对象析构时不会调用子类的析构,如果子类在构造时分配了空间的话则会造成内存泄漏
- 释放对象数组时没有使用[]
- 嵌套对象指针时没有正确的清除 -?这个不是很明白,留坑
智能指针
很早之前自个有封装过一个指针类,当时貌似只实现了一些简单的功能。今天被问到如果有两个指针指向同一块内存,而用户将其中一个指针析构了,另外一个指针怎么防止程序崩溃,当时一下没想明白,其实只要用一个引用计数就很好解决了。
而智能指针C++11中有unique_ptr、shared_ptr与weak_ptr等智能指针,可以对动态资源进行管理。先留个坑。