华为od 面试八股文_C++_01_含答案 骑猪看日落 2024-04-20 14:13 84阅读 0赞 **目录** 1:聊聊你对C++ 内存管理的理解。 2:栈区和堆区的区别在哪? 3:讲一讲封装、继承、多态是什么? 4:多态的实现原理(实现方式)是什么?以及多态的优点(特点)? 5:new和malloc的区别 6:为什么有了malloc/free,后还需要new/delete ? 7:指针和引用的区别 8:什么是深拷贝和浅拷贝? 9:重载和重写、重定义的区别 10:什么是虚函数?什么是纯虚函数? -------------------- #### 1:聊聊你对C++ 内存管理的理解。 #### ![84e7f9b6cf034c9e8cc7a80cc807090d.png][] 从高地址到低地址,一个程序由 内核空间、栈区、堆区、BSS段、数据段(data)、代码区组成。 常说的C++ 内存分区:栈、堆、全局/静态存储区、常量存储区、代码区。 可执行程序在运行时会多出两个区域: 栈:存放函数的局部变量、函数参数、返回地址等,由编译器自动分配和释放。栈从高地址向低地址增长。是一块连续的空间。栈一般分配几M大小的内存。 堆:动态申请的内存空间,就是由 malloc 分配的内存块,由程序员控制它的分配和释放,如果程序执行结束还没有释放,操作系统会自动回收。堆从低地址向高地址增长。一般可以分配几个G大小的内存。 在堆栈之间有一个 共享区(文件映射区)。 全局区/静态存储区(.BSS 段和 .data 段):存放全局变量和静态变量,程序运行结束操作系统自动释放,在 C 语言中,程序中未初始化的全局变量和静态变量存放在.BSS 段中,已初始化的全局变量和静态变量存放在 .data 段中,C++ 中不再区分了。 常量存储区(.data 段):存放的是常量,不允许修改,程序运行结束自动释放。 代码区(.text 段):存放程序执行代码的一块内存区域。只读,不允许修改,但可以执行。编译后的二进制文件存放在这里。代码段的头部还会包含一些只读的常量,如字符串常量字面值(注意:const变量虽然属于常量,但是本质还是变量,不存储于代码段) #### 2:栈区和堆区的区别在哪? #### **申请方式**:栈是系统自动分配,堆是程序员主动申请。 **申请后系统响应**:分配栈空间,如果剩余空间大于申请空间则分配成功,否则分配失败栈溢出;申请堆空间,堆在内存中呈现的方式类似于链表(记录空闲地址空间的链表),在链表上寻找第一个大于申请空间的节点分配给程序,将该节点从链表中删除,大多数系统中该块空间的首地址存放的是本次分配空间的大小,便于释放,将该块空间上的剩余空间再次连接在空闲链表上。 栈在内存中是连续的一块空间(向低地址扩展)最大容量是系统预定好的,堆在内存中的空间(向高地址扩展)是不连续的。 **申请效率**:栈是有系统自动分配,申请效率高,但程序员无法控制;堆是由程序员主动申请,效率低,使用起来方便但是容易产生碎片。 **存放的内容**:栈中存放的是局部变量,函数的参数;堆中存放的内容由程序员控制。 1、申请方式的不同。 栈由系统自动分配,而堆是人为申请开辟; 2、申请大小的不同。 栈获得的空间较小,而堆获得的空间较大; 3、申请效率的不同。 栈由系统自动分配,速度较快,而堆一般速度比较慢; 4、 存储的内容不同。 栈在函数调用时,第一个进栈的是主函数中后的下一条指令(函数调用语句的下一条可执行语句)的地址,然后是函数的各个参数,在大多数的C编译器中,参数是由右往左入栈的,然后是函数中的局部变量。注意静态变量是不入栈的。 当本次函数调用结束后,局部变量先出栈,然后是参数,最后栈顶指针指向最开始存的地址,也就是主函数中的下一条指令,程序由该点继续运行。 堆:一般是在堆的头部用一个字节存放堆的大小。堆中的具体内容由程序员安排。 #### 3:讲一讲封装、继承、多态是什么? #### 封装:将具体实现过程和数据封装成一个函数,只能通过接口进行访问,降低耦合性,使类成为一个具有内部数据的自我隐藏能力、功能独立的软件模块。意义:保护或防止代码在无意之中被破坏,保护类中的成员,不让类中以外的程序直接访问或者修改,只能通过提供的公共接口访问。 继承:子类继承父类的特征和行为,复用了基类的全体数据和成员函数,具有从基类复制而来的数据成员和成员函数(基类私有成员可被继承,但是无法被访问),其中构造函数、析构函数、友元函数、静态数据成员、静态成员函数都不能被继承。基类中成员的访问方式只能决定派生类能否访问它们。增强了代码耦合性,当父类中的成员变量或者类本身被final关键字修饰时,修饰的类不能被继承,修饰的成员变量不能重写或修改。意义:基类的程序代码可以被派生类服用,提高了软件复用的效率,缩短了软件开发的周期 多态:不同继承类的对象对同一消息做出不同的响应,基类的指针指向或绑定到派生类的对象,使得基类指针呈现不同的表现形式。意义:对已存在的代码具有可替代性,对代码具有可扩充性,新增子类不会影响已存在类的各种性质,在程序中体现了灵活多样的操作,提高了使用效率,简化了对应用代码的编写和修改过程。 #### 4:多态的实现原理(实现方式)是什么?以及多态的优点(特点)? #### 实现方式:多态分为动态多态(动态多态是利用虚函数实现运行时的多态,即在系统编译的时候并不知道程序将要调用哪一个函数,只有在运行到这里的时候才能确定接下来会跳转到哪一个函数。)和静态多态(又称编译期多态,即在系统编译期间就可以确定程序将要执行哪个函数),其中动态多态是通过虚函数实现的,虚函数是类的成员函数,存在存储虚函数指针的表叫做虚函数表,虚函数表是一个存储类成员虚函数的指针,每个指针都指向调用它的地方,当子类调用虚函数时,就会去虚表里面找自己对应的函数指针,从而实现“谁调用、实现谁”从而实现多态。而静态多态则是通过函数重载(函数名相同,参数不同,两个函数在同一作用域),运算符重载,和重定义(又叫隐藏,指的是在继承关系中,子类实现了一个和父类名字一样的函数,(只关注函数名,和参数与返回值无关)这样的话子类的函数就把父类的同名函数隐藏了。隐藏只与函数名有关,与参数没有关系.)来实现的。 优点:加强代码的可扩展性,可替换性,增强程序的灵活性,提高使用效率,简化对应用代码的编写和修改过程。 #### 5:new和malloc的区别 #### (1)、new和delete是C++关键字,而malloc和free是C语言库函数。都用于申请动态内存和释放内存。new的底层实现,也是基于malloc实现的。 (2)、new在调用时,是先分配内存,再调用构造函数,delete在调用时先使用析构函数,再释放内存,而malloc只分配内存,free只释放内存。故而,new比malloc更安全,因为,他会调用构造和析构函数。 (3)、new申请内存不需要计算申请内存的大小,且不需要强制类型转换,其返回的类型就是对应申请类型的指针,而malloc申请时,需要手动计算申请内存的大小,并且还需要强制类型转换为需要的类型,因为malloc返回的类型为 void\*。 (4)、new申请内存并初始化,malloc申请内存,但不初始化。 (5)、new申请内存失败,返回NULL,而malloc申请内存失败,则返回异常。 #### 6:为什么有了malloc/free,后还需要new/delete ? #### 因为对于非内部数据类型而言,光malloc/free并不能满足动态对象的要求,对象在创建和消亡时,会自动调用构造函数和析构函数,而malloc/free是库函数而非运算符,并不在编译器的控制权限内,故不能把构造和析构的任务交给malloc/free,故而有了new/delete。 #### 7:指针和引用的区别 #### (1)、指针和引用都是一种内存地址的概念,区别在于,指针是一个实体,而引用只是一个别名。 (2)、指针:指针指向的是一块内存地址,所指向的内容是内存的地址,但指针所指向的值是可以改变的,允许拷贝和赋值,有const和非const的区别,可以为空,sizeof指针得到的也是指针类型的大小。 (3)、引用:对于引用而已,引用只是一块内存的别名,引用必须在定义时绑定到一块内存上,即引用必须初始化,后续不可更改,也不能为空,且没有const和非const的区别。sizeof引用得到的是所初始化对象的大小。 (4)、指针必须在解引用后才能对对象进行操作,而引用可以直接对对象进行操作。做为参数来说,指针实质上是值传递,传递的是地址在,而引用实在上是地址传递,传递的变量是地址值。 #### 8:什么是深拷贝和浅拷贝? #### c++默认的拷贝构造函数是浅拷贝 浅拷贝就是对象的数据成员之间的简单赋值,如你设计了一个类而没有提供它的复制构造函数,当用该类的一个对象去给另一个对象赋值时所执行的过程就是浅拷贝。当数据成员中没有指针时,浅拷贝是可行的;但当数据成员中有指针时,如果采用简单的浅拷贝,则两类中的两个指针将指向同一个地址,当对象快结束时,会调用两次析构函数,而导致指针悬挂现象,所以,此时,必须采用深拷贝。 深拷贝与浅拷贝的区别就在于深拷贝会在堆内存中另外申请空间来储存数据,而不是一个简单的赋值过程,从而也就解决了指针悬挂的问题。 #### 9:重载和重写、重定义的区别 #### (1)、重载:重载指的是函数重载或运算符重载,指同一访问区内,被声明的几个参数列表不同的同名函数,C没有函数重载,C++能实现重载,主要是C++中对函数名的修饰和C不一样,C++对函数名的修饰,会把函数的参数类型加到函数名中,从而使得在程序中函数名一样,但在访问区中函数名不一样,返回值类型不能作为函数重载的依据。(属于静态多态) (2)、重写:主要指派生类中重新定义父类中的出函数体外其他都完全相同的虚函数,重写的一定是虚函数,在子类中重写函数,其访问权限可以随便由程序员自己定义。(属于动态多态) (3)、重定义:重定义,指在派生类中,重新定义和父类名字相同的非virtual函数,其参数列表和返回值都可以不同。则父类中的同名函数被子类所隐藏,如果想要调用父类中的同名函数,则需要加上父类的作用域。 #### 10:什么是虚函数?什么是纯虚函数? #### 虚函数:被 virtual 关键字修饰的成员函数,就是虚函数。 纯虚函数: 纯虚函数在类中声明时,加上 =0; 含有纯虚函数的类称为抽象类(只要含有纯虚函数这个类就是抽象类),类中只有接口,没有具体的实现方法; 继承纯虚函数的派生类,如果没有完全实现基类纯虚函数,依然是抽象类,不能实例化对象。 说明: 抽象类对象不能作为函数的参数,不能创建对象,不能作为函数返回类型; 可以声明抽象类指针,可以声明抽象类的引用; 子类必须继承父类的纯虚函数,并全部实现后,才能创建子类的对象。 [84e7f9b6cf034c9e8cc7a80cc807090d.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/20/f3f0c7f919e841209d1f6b7f9712107d.png
还没有评论,来说两句吧...