1. 面试官让说一些C++中一些重要的点
友元关系继承问题:友元关系不能继承。基类的友元对派生类的成员没有特殊访问权限。如果基类被授予友元关系,则只有基类具有特殊访问权限,该基类的派生类不能访问授予友元关系的类。(摘取:《c++ primer 第四版》)
静态成员继承问题:如果基类定义 static 成员,则整个继承层次中只有一个这样的成员。无论从基类派生出多少个派生类,每个 static 成员只有一个实例。(摘取:《c++ primer 第四版》)
2. 面向对象的思想,封装,继承,多态,分别描述一下,举例说一下运行时多态
2.1 面向对象的思想:
面向对象是基于面向过程而言的,就是说面向对象是将功能等通过对象来实现,将功能封装进对象之中,让对象去实现具体的细节;这种思想是将数据作为第一位,而方法或者说是算法作为其次,这是对数据一种优化,操作起来更加的方便,简化了过程。
2.2 封装: 隐藏对象的属性和实现细节,仅对外提供公共访问方式
- 将变化隔离、便于使用、提高复用性、提高安全性
2.3 继承: 提高代码复用性;继承是多态的前提
注: ①子类中所有的构造函数都会默认访问父类中的空参数的构造函数,默认第一行有super();若无空参数构造函数,子类中需指定;另外,子类构造函数中可自己用this指定自身的其他构造函数。
2.4 多态: 只要实现或继承了同一个接口或类,那么就可以使用父类中相应的方法。
- 好处:提高了程序的扩展性
- 弊端:当父类引用指向子类对象时,虽提高了扩展性,但只能访问父类中具备的方法,不可访问子类中的方法;即访问的局限性。
2.5 运行时的多态性: 就是指直到系统运行时,才根据程序运行中产生的信息确定需要调用哪个同名的函数。在C++中,运行时多态是通过继承和虚函数来实现的。
3. strcpy函数的功能
答: strcpy(字符数组1,字符串2) 作用是将字符串2复制到字符数组1中去。
拓展:
- strncpy(str1,str2,2); 作用是将str2中最前面2个字符复制到 str1中,取代 str1 中原有的最前面2个字符。
- c和c++使用的内存拷贝函数,memcpy函数的功能是从源src所指的内存地址的起始位置开始拷贝n个字节到目标dest所指的内存地址的起始位置中。
用法:void *memcpy(void *dest, const void *src, size_t n);
- strcpy和memcpy的区别:
1).复制的内容不同。strcpy只能复制字符串,而memcpy可以复制任意内容,例如字符数组、整型、结构体、类等。
2).复制的方法不同。strcpy不需要指定长度,它遇到被复制字符的串结束符"\0"才结束,所以容易溢出。memcpy则是根据其第3个参数决定复制的长度。
3).用途不同。通常在复制字符串时用strcpy,而需要复制其他类型数据时则一般用memcpy
4. 以下C程序的运行结果中,输出的加号的个数为 14
int main() {
for (int i = 0; i < 3; i++) {
fork();
printf("+");
fflush(stdout);
}
wait(NULL); // 等待子进程退出
wait(NULL);
wait(NULL);
return 0;
}
5. 请说明一下python中is和==的区别
is比较的是id,==比较的是value
拓展:
- python中对象有三个要素,id,type,value。 is比较的是ID,value比较的是value。
a = 1
b = 1
print(a == b) # True
a = 1
b = 1.0
print(a == b) # True
# ---------------------------------------
a = 3
b = 3.0
print(id(a)) # 1790209152
print(id(b)) # 6299984
print(a is b) # False
a = 3
b = 3
print(id(a)) # 1790209152
print(id(b)) # 1790209152
print(a is b) # True
6. 请你分别讲一下,怎样插入和删除大顶堆
大顶堆插入一个数:先将要插入的数存放在堆的最后(即容器的末尾,下标为index),再与其父节点(下标为(index-1)/2)的值进行比较,如果大于父节点的值,则交换两者的位置,直到循环结束;否则,位置不变,退出循环
大顶堆的删除操作:堆只能删除顶部元素。首先删除大顶堆的顶部元素,然后将大顶堆的最后一个元素放在顶部,再依次与大的子节点的值进行比较,如果小于大的子节点的值,则与这个大的孩子节点交换位置,如此直到循环结束;否则,位置不变,退出循环
7. 请你讲一下,如何将中缀表达式转为后缀表达式?
中缀表达式转后缀,其思想是将 运算符(+-*/)
往栈中放,数字就直接输出,然后通过每次扫描遇到的运算符优先级来判断出栈与入栈。
中缀表达式转为后缀表达式也有一定的规则,这个规则是根据操作符的运算优先级来定的转为后缀表达式的规则为:
- 如果遇到操作数,我们就直接将其输出。
- 如果遇到操作符,则我们将其放入到栈中,遇到左括号时我们也将其放入栈中。
- 如果遇到一个右括号,则将栈元素弹出,将弹出的操作符输出直到遇到左括号为止。注意,左括号只弹出并不输出。
- 如果遇到任何其他的操作符,如(“+”,“*”,“(”)等,从栈中弹出元素直到遇到发现更低优先级的元素(或者栈为空)为止。弹出完这些元素后,才将遇到的操作符压入到栈中。有一点需要注意,只有在遇到“)”的情况下我们才弹出“(”,其他情况我们都不会弹出“(”。
- 如果我们读到了输入的末尾,则将栈中所有元素一次弹出。
https://blog.csdn.net/ailunlee/article/details/86691224
8. 请你说一下,对一千万个整数排序,整数范围在[-1000,1000]间,用什么排序最快?
在以上的情景下最好使用计数排序,计数排序的基本思想为在排序前先统计这组数中其它数小于这个数的个数,其时间复杂度为O(n+k),其中n为整数的个数,k为所有数的范围,此场景下的n>>k,所以计数排序要比其他基于的比较排序效果要好。
9. 请你简要介绍一下python的生成器是什么
在Python中,一边循环一边计算的机制,称为生成器。
我们可以用列表储存数据,可是当我们的数据特别大的时候建立一个列表的储存数据就会很占内存的。这时生成器就派上用场了。它是一个不怎么占计算机资源的一种方法。
如果列表元素按照某种算法推算出来,那我们就可以在循环的过程中不断推算出后续的元素,这样就不必创建完整的list,从而节省大量的空间。
拓展: 如何创建生成器
方法1: 第一种方法很简单,只要把一个列表生成式的[]改成(),就创建了一个generator:
L = [x * x for x in range(10)]
print(L) # 输出为:[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
g = (x * x for x in range(10))
print(g) # 输出为:<generator object <genexpr> at 0x00000000021CB9E8>
创建L和g的区别仅在于最外层的[]和(),L是一个list,而g是一个generator。
方法2: 如果一个函数中包含yield关键字,那么这个函数就不再是一个普通函数,而是一个generator。调用函数就是创建了一个生成器(generator)对象。
10. 请你讲一下,如何将中缀表达式转为后缀表达式?
方法1:括号法
1:按照运算符的优先级对所有的运算单位加括号~
式子变成:((a+(b*c))+(((d*e+f)*g))
2:转换前缀与后缀表达式
前缀:把运算符号移动到对应的括号前面
则变成:+(+(a*(bc))*(+(*(de)+g))
把括号去掉:++a*bc*+*de+g 前缀式子出现
后缀:把运算符号移动到对应的括号后面
则变成拉:((a(bc)*)+(((de)*f)+g)*)+
把括号去掉:abc*+de*f+g *+后缀式子出现
https://blog.csdn.net/qianyayun19921028/article/details/89228263
方法2:基于堆栈的算法
方法3:语法树