C++面试突击

C++面试突击

第一章:编译内存相关

一、C++ 程序编译过程

面试高频指数:★★★★☆
编译过程分为四个过程:编译(编译预处理、编译、优化),汇编,链接。
编译预处理:处理以 # 开头的指令;
编译、优化:将源码 .cpp 文件翻译成.s汇编代码;
汇编:将汇编代码 .s 翻译成机器指令.o文件;
链接:汇编程序生成的目标文件,即.o文件,并不会立即执行,因为可能会出现:.cpp 文件中的函数引用了另一个.cpp
文件中定义的符号或者调用了某个库文件中的函数。那链接的目的就是将这些文件对应的目标文件连接成一个整体,从而生成可执行的程序 .exe 文件。

二、C++内存管理

面试高频指数:★★★★★
C++内存分区:栈、堆、全局/静态存储区、常量存储区、代码区
代码区(.text段):编译后的二进制文件存放在这里。存放代码,不允许修改,但可以执行。
全局区/静态存储区 (.bss 段和.data段):存放全局变量和静态变量,程序运行结束操作系统自动释放。在C语言中,未初始化的放在.bss 段中,初始化的放在.data段中,C++ 中不再区分了。
常量存储区(.data 段):存放的是常量,不允许修改,程序运行结束编译器自动释放。
:由编译器自动分配和释放、存放函数的局部变量、函数参数等,
:由程序员控制它的分配和释放,如果程序执行结束还没有释放,操作系统会自动回收。动态申请的内存空间,就是由 malloc分配的内存块。
总结:C++中在程序运行前分为代码区和全局区;代码区的特点是共享;全局区中存放全局变量、静态变量;常量区存放常量、const修饰的全局常量和字符串常量。
从操作系统的本身来讲,以上存储区在内存中的分布是如下形式(从低地址到高地址):代码区(.text 段)–>常量存储区(.data 段) --> 全局区 (.bss)–> 堆 --> unused --> 栈 --> env 程序实例.

三、栈和堆的区别

面试高频指数:★★★★★
申请方式:栈是系统自动分配,堆是程序员主动申请。
申请后系统响应:分配栈空间,如果剩余空间大于申请空间则分配成功,否则分配失败栈溢出;申请堆空间,堆在内存中呈现的方式类似于链表(记录空闲地址空间的链表),在链表上寻找第一个大于申请空间的节点分配给程序,将该节点从链表中删除,大多数系统中该块空间的首地址存放的是本次分配空间的大小,便于释放,将该块空间上的剩余空间再次连接在空闲链表上。
栈在内存中是连续的一块空间(向低地址扩展)最大容量是系统预定好的,堆在内存中的空间(向高地址扩展)是不连续的。
申请效率:栈是有系统自动分配,申请效率高,但程序员无法控制;堆是由程序员主动申请,效率低,使用起来方便,但是容易产生碎片。
存放的内容:栈中存放的是局部变量,函数的参数;堆中存放的内容由程序员控制。

四、变量的区别

面试高频指数:★★★☆☆
全局变量、局部变量、静态全局变量、静态局部变量的区别
C++ 变量根据定义的位置的不同的生命周期,具有不同的作用域,作用域可分为 6种:全局作用域,局部作用域,语句作用域,类作用域,命名空间作用域和文件作用域。
从作用域看:
全局变量:具有全局作用域。全局变量只需在一个源文件中定义,就可以作用于所有的源文件。当然,其他不包含全局变量定义的源文件需要用 extern关键字再次声明这个全局变量。
静态全局变量:具有文件作用域。它与全局变量的区别在于如果程序包含多个文件的话,它作用于定义它的文件里,不能作用到其它文件里,即被static 关键字修饰过的变量具有文件作用域。这样即使两个不同的源文件都定义了相同名字的静态全局变量,它们也是不同的变量。
局部变量:具有局部作用域。它是自动对象(auto),在程序运行期间不是一直存在,而是只在函数执行期间存在,函数的一次调用执行结束后,变量被撤销,其所占用的内存也被收回。
静态局部变量:具有局部作用域。它只被初始化一次,自从第一次被初始化直到程序运行结束都一直存在,它和全局变量的区别在于全局变量对所有的函数都是可见的,而静态局部变量只对定义自己的函数体始终可见。
从分配内存空间看
静态存储区:全局变量,静态局部变量,静态全局变量。
:局部变量。
说明:静态变量和栈变量(存储在栈中的变量)、堆变量(存储在堆中的变量)的区别:静态变量会被放在程序的静态数据存储区(.data段)中(静态变量会自动初始化),这样可以在下一次调用的时候还可以保持原来的赋值。而栈变量或堆变量不能保证在下一次调用的时候依然保持原来的值。静态变量和全局变量的区别:静态变量用 static 告知编译器,自己仅仅在变量的作用范围内可见。

五、C++封装,继承,多态的理解

封装

封装是什么?
在C++中,比较狭隘的解释就是将数据与操作数据的方法放在一个类中,而后给每个成员设置相应的权限。从大一点的角度来说,封装就是将完成一个功能所需要的所有东西放在一起,对外部只开放调用它的接口。
为什么要封装?
模块化设计是封装的本质原因。
对软件设计或其他工程设计,特别是比较复杂的工程,一般都是模块化设计。模块化设计的好处就是可以将一个复杂的系统拆分成不同的模块。每一个模块又可以独立的设计,调试,这就让多人一起做一个复杂的工程成为现实。如果想做到模块化设计,封装是不可缺少的一部分。一个好的模块,比如一块inter的CPU芯片,它有强大的功能,虽然我们不知道它内部是如何实现的,但却可以很好的使用它。

继承

我认为使用继承最重要的原因是继承可以使整个程序设计更符合人们的逻辑,从而方便的设计出想要表达的意思。比如我们要设计一堆苹果,橘子,梨等水果类,使用面向对象的方法,我们首先会抽象出一个水果的基类,而后继承这个基类,派生出具体的水果类。如果要设计的水果很多,我们还可以在水果基类基础上,继续生成新的基类,比如热带水果类,温带水果类,寒带水果类等,而后再继承这些基类。这样的设计思想就相当于人类的分类思想,简单易懂,而且设计出来的程序层次分明,容易掌握。
既然继承这么好,那该如何使用继承?
继承虽好但不能滥用,否则设计出来的程序会杂乱不堪。根据上面的介绍,可以发现继承主要用来定义一个东西是什么,比如热带水果是水果,菠萝是热带水果等,即继承主要用来设计一个程序的类的框架,将所要设计的东西用继承来设立一个基本结构。如果想为一个类添加一个行为或格外的功能,最好是使用组合的方式。

多态

多态简单的说就是“一个函数,多种实现”,或是“一个接口,多种方法”。多态性表现在程序运行时根据传入的对象调用不同的函数。
C++的多态是通过虚函数来实现的,在基类中定义一个函数为虚函数,该函数就可以在运行时,根据传入的对象调用不同的实现方法。而如果该函数不设为虚函数,则在调用的过程中调用的函数就是固定的。

你可能感兴趣的:(C++面试突击,c++,面试)