1. 函数重载与重写
重载:
概念:同一作用域内,定义相同名称的函数,参数列表必须不同(个数或类型),返回值可相同可不相同。
作用:命名一组功能相似的函数,避免名字空间污染,增加程序可读性。例如:构造函数重载
重写:
概念:也称为覆盖,子类重新定义父类中有相同名称和参数的虚函数,返回类型可以不同但必须是父类返回类型的子类。
作用:实现面向对象的三大特性之一的多态!
tips:
1. 不是虚函数也能进行重写,但在一个类中声明一个非虚函数实际上为这个类建立一种特殊性上的不变性,没必要重写这个非虚函数。
2. 父实子虚(子类无法更改父类函数)和父虚子实(子类屏蔽父类函数)都是设计错误,虽然都不会报错。
2. 多态的实现
多态的定义:同一操作作用于不同对象,可以有不同的解释,从而产生不同的执行结果。
多态两种类型:编译型多态(重载)和运行时多态(重写)。
作用:增强程序的可扩充性,降低后期修改代码的难度。
如何实现:拥有虚函数的类,通过基类指针或引用动态调用基类函数或是继承类函数,每个虚函数的类都有个虚函数表(占4字节),该类任何对象都有虚函数指针。
3. 赋值符重载与拷贝构造函数的区别
赋值符重载:重载operator = 函数,给一个对象赋值时会调用重载过的赋值运算符。
拷贝构造函数:以拷贝的方式初始化一个对象,调用拷贝构造函数。
区别:
1. 赋值运算符没有新的对象生成,拷贝构造函数会生成新的对象。
4. 拷贝构造函数何时调用
1. 用类的一个对象初始化类的另一个对象时。
2. 函数的形参或函数的返回值是类的对象时。
5. 四种类型转换运算符
static_cast:用于良性转换。
const_cast:用于const和非const、volatile和非volatile之间的转换。
reinterpret_cast:高度危险转换,对二进制位的重新解释。
dynamic_cast:用于基类对象的指针或引用转换为继承类对象的其他指针或引用。
6. const的用法
const:常量限定符,用来限定特地变量,使其不可被编译器修改。
用法:
1. const修饰基本数据类型,例如 const int a = 1;
2. const修饰指针:const int* p(指针常量)、int* const p(常量指针)、const int* const p(常量指针-常量)
3. const修饰引用
4. const修饰函数形参,修饰函数返回值,修饰成员变量、const修饰整个函数,
例如:T method() const {}; 意义为不能修改该类任何变量。
7. static的作用
1. 修饰全局变量,使其他文件不可见。
2. 修饰局部变量,使其变量不会因为函数终止而删除。
3. 修饰函数,使其他文件不可见。
4. 修饰类的数据成员,该类所有对象这个数据只有一个实例。
5. 修饰类成员函数,该函数只能访问它的参数,类静态数据,以及全局变量。
8. vector的push_back原理
vector有预存内存(大小为capacity),当存入元素溢出,就重新分配一个比原来大2倍或1.5倍的内存。
10. 析构函数可以是虚函数吗?
析构函数可以是虚函数,有时必须声明为虚函数,否则会出现:父类析构,子类没析构的情况,从而产生析构不完全的情况。
11. 构造函数为什么不能是虚函数 ?
1. 创建对象时需要确定对象类型,而虚函数在运行时动态确定其类型,而构造对象时,由于对象还未创建成功,编译器无法知道对象类型,因此虚函数动态确定不了其类型。
2. 虚函数调用需要虚函数表指针,而该指针存放在对象内存空间中,对象还没创建,没有内存存放虚函数表,就无法用虚函数构造函数。
12. 引用和指针的差别
1. 指针是变量,存储地址,指向内存的存储单元。引用是原变量的别名,与原变量是同一个东西。
2. 指针可以多级指针,引用只能一级。
3. 指针可以不初始化,引用必须初始化。
4. 指针可以指向NULL,引用不可以。
5. 指针可以改变,引用不可以。(引用的值改变,被引用的值也会被改变)
13. STL是什么?
STL:标准模板库,一些容器、算法和一些组件的集合,容器包括vector、list、map、set等。
五大类组件:容器、迭代器、算法、函数对象、适配器。
作用:可以更加方便灵活地处理数据。
map:STL(标准模板库)的一个关联容器,提供一对一的hash。
特性:
1. 内部实现是红黑树结构,具有自动排序的功能。
vector:封装了动态大小数组的顺序容器。
特性:
1. 容器内元素按严格的线性顺序排序,支持索引访问,随机存取时间复杂度O(1),查找时间复杂度为O(n)。
2. 添加元素时会进行动态分配内存。
常用函数:push_back()、pop_back()、empty() 、sort()等
llist:用双向环形链表实现,内存空间不连续。插入和删除时间复杂度O(1)。
haspmap:基于哈希表,具有快速存储和查找的优点,但消耗内存多。
原理:使用一个下标范围比较大的数组存储元素,存储key时,将其哈希化,获得对应桶号,将value存入桶中。
14. C++内存分配的方式
静态存储区分配内存:程序编译时就分配好,程序地整个运行期间都存在,例如全局变量,静态变量。
栈上分配内存:系统自动分配,例如函数内部的局部变量,随着函数结束而释放。
堆上分配内存:程序员动态分配内存,例如new、malloc。
15. malloc/free和new/delete的区别
共同点:都是从堆上申请空间,并且需要用户手动释放。
区别:
1. malloc和free是函数,new和delete是操作符。
2. malloc申请空间要手动计算空间大小,new只需在其后输入空间类型。
3. malloc申请空间不会初始化,new可以初始化。
4. malloc申请空间失败,返回NULL,new申请失败,抛出异常。
5. malloc的返回值是void*,new返回空间类型。
17. C++11新特性
1. auto(值推导) & decltype(表达式推导):类型推导
2. 左值右值:
左值:能取地址且有名字。
右值:不能取地址且没有名字。
3. 列表初始化。
4. function & bind & lambda表达式。
5. 智能指针。
6. nullptr。
7. const / constexpr。
8. sizeof。
9. 内存对齐。
18. 智能指针(动态指针)
智能指针:利用对象生命周期控制程序资源的技术。
作用:防止内存泄漏、二次释放等问题。
指针类型:
1. shared_ptr:多个指针指向相同的对象。
2. unique_ptr:“唯一”拥有其所指对象,同一时刻只能有一个unique_ptr指向给定对象。
3. weak_ptr:配合shared_ptr而引入的一种智能指针。
19. lambda表达式
lambda表达式:定义一个匿名函数,可以捕获一定范围内的变量。
优点:
1. 可维护性高。
2. 简洁:避免代码膨胀,功能分散。
3. 在需要的时间和地点实现功能闭包,使程序更灵活。
缺点:
1. 代码可读性变差。
2. 学习难度高。
3. 不容易调试。
20. 强枚举类型(enum class)
传统枚举类型:由用户定义的若干枚举常量的集合。枚举值对应整型数值,默认从零开始。
存在问题:
1. 同作用域同名枚举报错。
2. 无法自定义枚举常量定义类型。(c++11可以)
3. 枚举常量占用存储空间及符号不确定。
因此强枚举类型(enum class)诞生,优点:
1. 强作用域:不同枚举集合可取同名枚举。
2. 转换限制:不再与整型发生隐式转换。
3. 自定义枚举常量定义类型。
21. C++编译连接过程
源代码(预编译检查) -》 编译器 -》 汇编代码 -》 汇编器 -》目标代码(机器指令) -》 连接器 -》 可执行程序。
22. 一个空的class类里有什么
包含六个默认函数:
1. 默认构造函数
2. 拷贝构造函数
3. 析构函数
4. 赋值运算符重载
5. 取地址操作符重载
6. const修饰的取地址操作符重载
23. 内存对齐
内存对齐:结构体内数据将对齐最长的数据。
原因:
1. 性能原因:未对齐的内存,处理器可能要做两次访问,而对齐的内存只要一次访问。
2. 空间原因:防止空间浪费。
常见类型长度:
char(1)、int(4)、double(8)、float(4)、string(40)、指针(8)。
24. 什么是优先队列?
优先队列(priority_queue):一种抽象数据类型,每个元素都有优先级,优先级高的先出队。
25. 值类型和引用类型(C#)
值类型:变量直接存储数据,包括:简单类型、枚举、结构体。
引用类型:变量持有数据的引用,数据存储在数据堆中,包括:类、委托、接口、数组、字符串。
26. 内联函数的含义?(C++)
内联函数:用关键字inline修饰的函数,每次调用,都写一次函数在对应位置。
优点:避免指针跳转,加快程序执行速度。
缺点:导致程序代码量庞大,占用更多内存空间。
27. 数组越界要怎么知道?
1. 发现是否有数组越界问题,没有特殊的好办法,一般采用静查法。静查就是通过认真阅读,检查程序是否按照设计的要求编写。
2. 调试法,通过测试例子,测试结果是否与预期结果一致,进行判断。
28. C#装箱拆箱
装箱将值类型转换为引用类型;拆箱将引用类型转换为值类型。
29. 野指针
野指针:没有指向有效内存的指针。
造成野指针的原因:
1. 指针变量未初始化。
2. 指针指向的内存被释放,而指针没有处理。
3. 指针超过变量作用范围。例如函数局部变量的地址值被传出来,而局部变量会在函数执行完自动清理,此时的地址值是不安全的,随时都可能被覆盖。
30. 函数运行全过程的底层机制
1. 主函数正常运行,遇到被调用函数。
2. 分配被调用函数所需的栈空间。
3. 保存当前函数运行状态和返回地址。
4. 传递参数给被调用函数,并将控制权转交给被调函数。
5. 被调函数执行完毕,通过返回地址返回之前函数的状态,释放栈空间。
31. typedef和define有什么区别
typedef:常用来定义一个标识符及关键字的别名,语言编译过程的一部分,不分配内存空间。可以增强程序的可读性,标识符的灵活性。常用于:
1. 定义类型别名
2. 辅助struct定义别名(c++11不需要)
3. 定义与平台无关的类型(不同平台支持的类型不同)
define:宏定义语句,定义常量,在预编译过程就完成。
区别:
1. 宏定义只是简单的字符串代换(原地扩展),typedef具有一定的封装性。例如:
typedef (int*) pINT; pINT a,b效果等于int *a, *b;
#define pINT int*; pINT a,b效果等于int *a, b;
32. define与const的区别
共同作用:定义常量
区别:
1. 编译器处理方式:define作用于预处理,const作用于编译阶段。
2. 类型检查:define无类型,不进行类型安全检查,const有数据类型,进行类型检查。
3. 内存空间:define不分配内存,const在静态存储区分配内存。
33. vector中resize()和reserve()区别
resize(): 重新申请并改变当前vector对象的有效空间大小,改变有效空间大小:小于空间大小,裁剪多出来的数据,capacity不变,大于空间大小,赋值为定义数据,capacity大小与改变大小相同。
reserve():重新申请并改变当前vector对象的总空间(capacity)大小,不改变有效数据大小。
34.