C++ 基础知识 问答题(五)

PART1

1.int型整数的最大值和最小值分别是什么?如何用二进制表示?使用最大值和最小值进行计算时会出现什么问题?

2.类声明成指针相对于声明成对象有什么好处?

3.迭代器可以加减常数吗?

4.动态绑定是如何实现的?

5.哈希函数是什么?

6.当发生哈希表冲突时,有哪些处理方法?

7.deque的底层结构是什么?

8.哪些迭代器有除了!=和==之外的关系运算?

9.迭代器it进行it++和++it的区别

10.Lambda表达式用来做什么?语法形式是什么?捕获列表有几种形式?

11.线性表是什么?

13.mutable关键字的作用是什么?

14.编译过程的四个步骤是什么?

15.希尔排序是什么?时间复杂度是多少?

16. Lambda表达式的底层实现是什么?

17. vector是如何扩容的?

18. vector的resize和reserve方法有什么区别?

19.bitset类如何使用?

20.deque类如何使用?

21.仿函数是什么?

22.函数指针是什么?作用是什么?

PART2

1.int型整数的最大值和最小值分别是什么?如何用二进制表示?使用最大值和最小值进行计算时会出现什么问题?

INT_MAX: 2^31-1     01111111111111111111111111111111

INT_MIN:-2^31       10000000000000000000000000000000

会发生的问题:

INT_MAX +1 = INT_MIN

INT_MIN - 1 = INT_MAX

abs(INT_MIN) = INT_MIN

2.类声明成指针相对于声明成对象有什么好处?

多态特性

可以用new操作符来申请动态内存

生命周期:由程序员决定何时释放对象

对象的共享和传递:传参时不用拷贝整个对象,提高效率

3.迭代器可以加减常数吗?

只用存储在连续空间的容器的迭代器可以加减常数,因为这些容器支持随机访问。

vetcor, deque, string的迭代器可以加减常数。

map, set, list, forward_list的迭代器不可以加减常数,只能自增自减。

4.动态绑定是如何实现的?

父类指针或引用指向子类对象,父类指针调用虚函数,在运行时实现动态绑定,根据虚函数表,确定调用哪个类的该函数。

5.哈希函数是什么?

将一组数据映射到一段有限的地址空间的函数。

6.当发生哈希表冲突时,有哪些处理方法?

开放寻址法

  • 线性探测法:若地址冲突,沿着该地址加一,直到找到空单元
  • 平方探测法:若地址冲突,沿着该地址加1^2, 2^2, 3^2....  直到找到空单元
  • 双散列函数探查法:若地址冲突,沿着该地址加上一个步长,步长由关键字在第二个哈希函数中计算得到(若地址空间在0到n-1之间,那么第二个哈希函数计算出的值在1到n-1之间,且该值与n互素),直到找到空单元。该探查序列具有随机性。
  • 伪随机探查法:建立一个伪随机数序列list,若地址冲突,关键字加上伪随机数序列中的值list[i],再次计算地址,直到地址不再冲突。

链地址法

将地址都为m的元素都存储在一个单向链表中,哈希表中的第m个单元指向该单向链表。

再哈希法

构造多个哈希函数,当第一个哈希函数产生地址冲突时,就使用第二个哈希函数,直到不再发生冲突。不易产生聚集,但是增加了计算时间。

建立公共溢出区

将哈希表分为公共表和溢出区,当发生地址冲突了,就存储到溢出区。

7.deque的底层结构是什么?

deque的底层结构是由一段一段连续的等长的存储空间组成的,每段存储空间之间不一定连续。

为了管理这些连续空间,deque用数组存储了每段存储空间的首地址,也就是指针。

8.哪些迭代器有除了!=和==之外的运算?

vector, string, queue

可以进行的运算:

iter1 += iter2

iter1 -= iter2

iter1 - iter2  获取两个迭代器之间的距离

> < >= <= 元素靠后的迭代器大于靠前的迭代器

9.迭代器it进行it++和++it的区别

++it返回的是引用

it++返回的是临时对象,开销大,每次自增都在创建和销毁对象

10.Lambda表达式用来做什么?语法形式是什么?捕获列表有几种形式?

Lambda表达式用于定义和创建匿名函数。

语法形式: [捕获列表](参数列表)mutable或exception异常->返回类型 {函数体}

捕获列表有多种形式:

[]:不捕获父作用域的任何遍量

[=]:用值传递的方式捕获父作用域的所有变量

[&]:用引用传递的方式捕获父作用域的所有变量

[=a,&]:用值传递的方式捕获父作用域的a变量,用引用传递的方式捕获父作用域的其他变量

[&a,=]:用引用传递的方式捕获父作用域的a变量,用值传递的方式捕获父作用域的其他变量

11.线性表是什么?

具有相同特性的数据元素组成的有限序列。

线性表包含顺序表(数组)和链表。

13.mutable关键字的作用是什么?

用于修饰成员变量,表示const成员函数可以修改该变量。

14.编译过程的四个步骤是什么?

预处理:替换预处理指令,展开宏定义

编译:完成词法分析、语法分析、语义分析等操作,将源代码编译成汇编指令

汇编:将汇编指令翻译成机器语言的目标文件,生成一个与机器硬件相关的目标文件

链接:将目标文件与所需的库文件链接,生成最终的可执行文件

15.希尔排序是什么?时间复杂度是多少?

希尔排序是缩小增量的插入排序。把下标按照一定的增量分组,对每组进行直接插入排序。随着增量减少,每组的元素越来越多,直到最后被分成一组。

时间复杂度:O(N^(2/3))

16. Lambda表达式的底层实现是什么?

编译器实现Lambda表达式,分三步:

创建一个Lambda匿名类,实现构造函数,用Lambda表达式的函数体重载operator()

创建一个Lambda匿名类的对象

使用该对象调用operator()

17. vector是如何扩容的?

为了避免每次插入都扩容,vector会在插入元素前,预估所需大小,提前将底层容量开辟好。

Windows: 按照当前容量的1.5倍扩容(可以充分利用之前用到过的内存块)

Linux: 按照当前容量的2倍扩容

18. vector的resize和reserve方法有什么区别?

resize: 改变vector的size大小,并对元素初始化。有可能会影响capacity。

若resize要求的大小小于vector当前的size,则舍弃多余元素(逻辑上舍弃,物理上还能访问)。

若resize要求的大小大于vector当前的size,则新添加元素,并对元素默认初始化。

若resize要求的大小大于vector当前的capacity,则先扩容,再添加元素,并对元素默认初始化。

reserve: 改变vector的capacity大小,不影响size。

若reserve要求的大小大于vector当前的capacity,那么会重新分配一块连续的存储空间,将vector原有的数据copy过去,这时vector的capacity增大了,但size不变。

若reserve要求的大小小于vector当前的capacity,则不会产生任何影响。

19.bitset类如何使用?

bitset 是二进制集合,必须指定模板参数N,表示bitset有几位。

初始大小4字节,大小每次两倍增长

bitset<1> : 4字节

bitset<32> : 4字节

bitset<33> : 8字节

bitset<65> : 16字节

20.deque类如何使用?

双端队列,可以在队列头和尾插入和删除元素。可以用下标遍历deque。

21.仿函数是什么?

仿函数又称函数对象,它并不是一个函数,但有类似函数的功能,可以像普通函数那样调用,传入参数,有返回值。

仿函数是实现了operator()的一个类,有自己的成员变量。

22.函数指针是什么?作用是什么?

函数指针指向一个函数在内存中的起始地址。

函数指针可以指向参数列表和返回值类型与它相同的函数:

        函数指针的声明: (返回值类型)(*函数指针名)(参数列表)int(*p)(string s);

函数指针的作用是将函数作为参数传递: process(0,0,p);p("hello");

函数指针不支持算数运算:+1, ++, --

你可能感兴趣的:(c++,开发语言)