1. const的含义及实现机制?
答:const用来说明所定义的变量是只读的。
这些在编译期间完成,编译器可能使用常数直接替换掉对此变量的引用。
2. 写出new和malloc、delete和free的区别?
答:
1.申请的内存所在位置
new操作符从自由存储区(free store)上为对象动态分配内存空间,而malloc函数从堆上动态分配内存。
2.返回类型安全性
new操作符内存分配成功时,返回的是对象类型的指针,类型严格与对象匹配,无须进行类型转换,故new是符合类型安全性的操作符。而malloc内存分配成功则是返回void * ,需要通过强制类型转换将void*指针转换成我们需要的类型。
3.内存分配失败时的返回值
new内存分配失败时,会抛出bac_alloc异常,它不会返回NULL;malloc分配内存失败时返回NULL。在使用C语言时,我们习惯在malloc分配内存后判断分配是否成功:
new根本不会返回NULL,而且程序能够执行到if语句已经说明内存分配成功了,如果失败早就抛异常了。正确的做法应该是使用异常机制。
4.是否需要指定内存大小
使用new操作符申请内存分配时无须指定内存块的大小,编译器会根据类型信息自行计算,而malloc则需要显式地指出所需内存的尺寸。
5.是否调用构造函数/析构函数
使用new操作符来分配对象内存时会经历三个步骤:
第一步:调用operator new 函数(对于数组是operator new[])分配一块足够大的,原始的,未命名的内存空间以便存储特定类型的对象。
第二步:编译器运行相应的构造函数以构造对象,并为其传入初值。
第三部:对象构造完成后,返回一个指向该对象的指针。
使用delete操作符来释放对象内存时会经历两个步骤:
第一步:调用对象的析构函数。
第二步:编译器调用operator delete(或operator delete[])函数释放内存空间。
总之来说,new/delete会调用对象的构造函数/析构函数以完成对象的构造/析构。而malloc则不会。
6.对数组的处理
C++提供了new[]与delete[]来专门处理数组类型:
使用new[]分配的内存必须使用delete[]进行释放:new对数组的支持体现在它会分别调用构造函数函数初始化每一个数组元素,释放对象时为每个对象调用析构函数。注意delete[]要与new[]配套使用,不然会找出数组对象部分释放的现象,造成内存泄漏。
至于malloc,它并知道你在这块内存上要放的数组还是啥别的东西,反正它就给你一块原始的内存,在给你个内存的地址就完事。所以如果要动态分配一个数组的内存,还需要我们手动自定数组的大小:
7.new与malloc是否可以相互调用
operator new /operator delete的实现可以基于malloc,而malloc的实现不可以去调用new。
8.是否可以被重载
opeartor new /operator delete可以被重载。标准库是定义了operator new函数和operator delete函数的8个重载版本:
我们知道我们有足够的自由去重载operator new /operator delete ,以决定我们的new与delete如何为对象分配内存,如何回收对象。而malloc/free并不允许重载。
9.能够直观地重新分配内存
使用malloc分配的内存后,如果在使用过程中发现内存不足,可以使用realloc函数进行内存重新分配实现内存的扩充。realloc先判断当前的指针所指内存是否有足够的连续空间,如果有,原地扩大可分配的内存地址,并且返回原来的地址指针;如果空间不够,先按照新指定的大小分配空间,将原有数据从头到尾拷贝到新分配的内存区域,而后释放原来的内存区域。new没有这样直观的配套设施来扩充内存。
10.客户处理内存分配不足
3. 写两个继承类,解释虚表指针和虚表的作用?
答:每一个类都有虚表。
虚表可以继承,如果子类没有重写虚函数,那么子类虚表中仍然会有该函数的地址,只不过这个地址指向的是基类的虚函数实现。如果基类有3个虚函数,那么基类的虚表中就有三项(虚函数地址),派生类也会有虚表,至少有三项,如果重写了相应的虚函数,那么虚表中的地址就会改变,指向自身的虚函数实现。如果派生类有自己的虚函数,那么虚表中就会添加该项。派生类的虚表中虚函数地址的排列顺序和基类的虚表中虚函数地址排列顺序相同。
4. 写出static的用法和作用?
答:static 是C++中很常用的修饰符,它被用来控制变量的存储方式和可见性。函数内部定义的变量,在程序执行到它的定义处时,编译器为它在栈上分配空间,大家知道,函数在栈上分配的空间在此函数执行结束时会释放掉,这样就产生了一个问题: 如果想将函数中此变量的值保存至下一次调用时,如何实现? 最容易想到的方法是定义一个全局的变量,但定义为一个全局变量有许多缺点,最明显的缺点是破坏了此变量的访问范围(使得在此函数中定义的变量,不仅仅受此函数控制)。 需要一个数据对象为整个类而非某个对象服务,同时又力求不破坏类的封装性,即要求此成员隐藏在类的内部,对外不可见。
5. 判断链表是否存在闭环?
答://using step1 and step2 here
//if exists a loop, then the pointer which use step2 will catch up with the pointer which uses step1
int HasLoop(LinkList L)
{
int step1 = 1;
int step2 = 2;
LinkList p = L;
LinkList q = L;
//while (p != NULL && q != NULL && q->next == NULL)
while (p != NULL && q != NULL && q->next != NULL)
{
p = p->next;
if (q->next != NULL)
q = q->next->next;
printf("p:%d, q:%d \n", p->data, q->data);
if (p == q)
return 1;
}
return 0;
}
6. 连连看游戏中,写出两种算法的大致原理,来判断两个图案是否能够连线。并详细解释,写出其中一个算法的伪代码?
答:程序的关键在于判断用户连续点击的两个图案能否消除。两个图片可以消除的条件有两个:
u 图片相同
u 图片间连线的转角数不得超过2 。
而判断能否通过小于两个转角的路径连通的算法有两种:
u 分类判断法
u 以转角数为标准的广度优先搜索
下面对两种算法分别讨论。