C++经典面试问答
结构成员的默认访问权限是public,而类成员的访问权限是private。
2.析构函数和虚函数的用法和功能
析构函数是一个在对象生命周期结束时自动调用的函数,用于释放在构造函数中分配的内存。
虚函数是指关键字Virtual所描述的函数,它的作用是利用C++语言的多态特性。
3.全局变量和局部变量有什么区别?是如何实现的?操作系统和编译器是怎么知道的?
1)全局变量在这个程序块中使用,而局部变量在当前函数中使用。
2)前者分配在内存的全局数据区,后者分配在堆栈区。
3)不同的生命周期:全局变量随主程序的创建而创建和销毁,随主程序的销毁而销毁;局部变量存在于局部函数内部,甚至存在于局部循环体内部,但是exit不存在。
4)使用方式不同:声明后,全局变量程序的各个部分都可以使用,局部变量只能在局部使用。
4.有n个大小不同的自然数(1?n),请按从小到大排序。需要程序算法:时间复杂度为O(n),空间复杂度为O(1)。
void sort(int e[],int n)
{
int I;
int t;
for(I = 1;我{
t = e[e[I]];
e[e[I]]= e[I];
e[I]= t;
}
}
5.堆和栈的区别
A.不同的应用方法
Stack是系统自动分配的,而heap需要程序员自己申请并指定大小。
B.应用后系统的响应有所不同。
堆栈:只要堆栈剩余空间大于应用空间,系统就会为程序提供内存,否则会抛出堆栈溢出异常。
Heap:当系统接收到一个程序应用时,首先遍历操作系统中空闲内存地址的链表,找到第一个大于应用空间的堆节点,然后从空间节点链表中删除该节点,将该节点的空间分配给程序。此外,大多数系统还会在这个内存空间的第一个地址记录这个分配的大小,以便delete语句能够正确地释放空间。而且,因为找到的堆节点的大小不一定正好等于应用的大小,所以系统会自动把多余的部分放回空闲链表中。
C.不同的应用程序大小限制
堆栈:在windows下,堆栈的大小是2M(或1M,是编译时确定的常数)。如果请求的空间超过堆栈的剩余空间,将提示溢出。因此,可以从堆栈中获得更少的空间。
Heap: Heap是扩展到高位地址的数据结构,是一个不连续的内存区域。这是因为系统使用链表存储空闲内存地址,这种内存地址自然是不连续的,链表的遍历方向是从低位地址到高位地址。堆的大小受计算机系统中可用的虚拟内存的限制。可见堆获得的空间更灵活,更大。
D.应用效率比较:
堆栈由系统自动分配,速度更快。但是程序员无法控制它。
Heap是new分配的内存,一般比较慢,容易出现内存碎片,但是用起来最方便。
另外,在WINDOWS下,最好的办法是用VirtualAlloc分配内存,不在堆里,也不在栈里,直接在进程的地址空间里,虽然用起来最不方便。但它是快速且最灵活的。
E.堆和堆栈中的存储
堆栈:当一个函数被调用时,首先要堆栈的是主函数中下一个指令的地址(函数调用语句的下一个可执行语句),然后是函数的参数。在大多数C编译器中,参数从右到左堆叠,然后是函数中的局部变量。请注意,静态变量是不堆叠的。当这个函数调用完成后,首先弹出局部变量,然后是参数,最后栈顶的指针指向最开放的地址,也就是主函数中的下一条指令,程序从这里继续运行。
堆:一般在堆里。头使用一个字节来存储堆的大小。堆中的具体内容由程序员安排。
6.带参数的宏和函数的优缺点
宏:优点:在预处理阶段完成,不占用编译时间,同时节省了函数调用的开销,运行效率高。
缺点:没有类型检查,多次宏替换会导致代码量变大,而且由于宏本质上是字符串替换,可能会因为某些参数的副作用而得到错误的结果。
功能:优点:没有带参数的宏可能带来的副作用,通过类型检查更能保证计算的正确性。
缺点:函数调用需要参数和返回地址的入栈和出栈开销,效率没有带参数的高。
PS:宏和内联函数的区别
内联函数和宏都在程序出现的地方展开。内联函数不是通过函数调用实现的,而是在调用函数的程序处扩展的(编译时完成)。宏也;
不同的是,内联函数可以在编译时完成类型检测、语句是否正确等编译功能;宏没有这样的功能,宏的扩展时间和内联功能也不一样(运行时扩展)
7.Windows程序的入口在哪里?写Windows消息机制的过程。
Windows程序的入口是WinMain()函数。
Windows应用程序消息处理机制:
a .操作系统接收应用程序的窗口消息,并将该消息投递到应用程序的消息队列中。
B.应用程序在消息循环中调用GetMessage函数,从消息队列中取出一条又一条消息。取出消息后,应用程序可以对消息进行一些预处理。
C.应用程序调用DispatchMessage将消息发送回操作系统。
d系统通过使用WNDCLASS结构的lpfnWndProc成员保存的窗口过程函数的指针调用窗口过程来处理消息。
8.如何定义并实现一个类的成员函数作为回调函数?
A.什么是回调函数?
简而言之,回调函数就是被调用者回调给调用者的函数。
使用回调函数,其实就是在调用一个函数(通常是API函数)的时候,把自己的一个函数(这个函数是回调函数)的地址作为参数传递给被调用的函数。被调用函数在必要时用传递的地址调用回调函数。
回调函数是自己写的。你需要调用另一个函数,这个函数的参数之一就是你的回调函数名。这样系统会在必要的时候调用你写的回调函数,让你在回调函数里做你想做的事情。
如何定义和实现一个类的成员函数作为回调函数
要将类的成员函数定义和实现为回调函数,您需要做三件事:
A.宣言;
B.定义;
C.设置触发条件意味着将回调函数的名称作为函数中的参数,以便系统可以调用它。
比如:
首先,声明回调函数类型
typedef void(* FunPtr)(void);
其次,定义回调函数
A级
{
公共:
a();
Static void callBackFun(void) //回调函数,必须声明为Static。
{
cout & lt& lt" callBackFun " & lt
}
虚拟~ A();
};
第三,设置触发条件
无效函数类型(函数参数)
{
p();
}
无效总管(无效)
{
funtype(A::callback fun);
}
C.回调函数和API函数
回调和API非常接近,其* * *本质是一个跨层调用的函数。但不同的是,API是下层提供给上层的调用,一般这个函数是上层知道的;回调正好相反。它是顶层提供给底层的调用。底层不知道,必须由顶层安装。这个安装功能其实就是底层提供的一个API。安装后底层不知道这个回调的名字,但是通过一个函数指针保存这个回调函数。需要调用的时候,只需要引用这个函数指针和相关的参数指针。
其实回调就是函数写在高层,低层通过函数指针保存函数。在事件的触发下,低层通过函数指针调用高层中的函数。
?