考研复试专业课面试——C++

记:此篇博客是关于考研复试中专业课面试的相关知识点,按个人理解以及网上查资料来总结的,用来锻炼自己的逻辑思维,可能不太准确,希望指正。

1、什么是虚函数和纯虚函数?

    基类指针可以指向其公有派生类对象,但当用基类指针访问其指向的派生类对象时只能访问该派生类从基类继承而来的对象而不能访问该派生类中定义的对象,此时就需要虚函数来解决这个问题。

     虚函数是在基类中用virtual关键字说明并在派生类中被重新定义的成员函数,引入数函数的目的是为了动态绑定。引入纯虚函数的目的是为了派生接口,即在基类中为其派生类保留一个函数接口,以便于其派生类根据自己的需要进行重新定义。纯虚函数没有函数体,不具备函数功能,不能被调用。声明纯虚函数时令其等于0只是起到一个形式上的作用,并不代表其返回值为0,若一个类至少包含一个纯虚函数,则称该类为抽象类。

2、基类为什么要有虚析构函数?

    如果没有虚析构函数,在销毁一个由基类指针指向的派生类对象时,只能调用基类的析构函数而不能调用派生类的析构函数,导致派生类对象所申请的空间无法释放而造成内存泄漏,此时就可以通过定义虚析构函数,以便通过基类指针销毁派生类对象。

3、当i是一个整数时,i++和++i哪一个更快?他们的区别是什么?

    当i是一个整数时,他们基本一样快。他们的区别在于:i++是先使用变量i,再使i的值加1,而++i是先使i的值加1,再使用i。也就是说i++返回的是i的值,而++i返回的是i+1的值。

4、指针和引用的区别?

    指针和引用的本质区别在于:指针是一个新的变量,只不过这个变量存储的是另一个变量的地址,可以通过该地址即指针变量来访问原来的变量,这种方式成为间接访问,需要用间接运算符*。引用只是另一个变量的别名,它和原本的变量拥有相同的地址,对引用的操作即是对该变量的操作,这种访问方式称为直接访问,不需要间接运算符,所以使用引用可以简化操作。

    指针在做为函数参数时,内部是值传递,指针的值是不能改变的,要通过解引用才能够对原变量进行操作。引用在做函数参数时,内部传递的实际是变量的地址。

5、static关键字的作用?

    修饰局部变量:局部变量本来存储在动态区,被static修饰后为静态变量,改变了其存储位置,存储在静态区。其生命周期与程序相同,在main函数之前初始化,在程序退出时销毁。

    修饰全局变量:全局变量本来就存储在静态区,所以static修饰并不能改变其存储位置,但是可以改变其作用域,只有包含该变量定义的文件才能访问。

    修饰函数:static修饰函数作用也是改变其作用域,至于包含该函数定义的文件才能调用。对于静态函数来说,其声明和定义应该在同一个文件中。

    修饰成员变量:static修饰的类成员变量是类的全局变量,被该类的所有对象所共有,包括派生类对象,即所有的对象都只维持同一个实例。所以static修饰的成员变量要在类外进行初始化,而不能在类内初始化。

    修饰成员函数:static修饰的成员函数是该类所有对象所共享的函数,使这个类只存在这一个函数,没有this指针且被static修饰的成员函数只能访问static成员变量。静态成员可以独立访问,即不必创建对象即可进行访问。

    当同时编译多个文件时,非static的全局变量或函数都是具有全局可见性的,也可以被其他的源文件来访问。对于函数来讲,static修饰只是起到了限制其作用域的作用。

6、为什么static和const不能同时修饰成员函数?

    c++编译器在实现const的成员函数时为了使其不改变类的实例的状态,就添加了参数const this*,但当声明一个static成员函数时是不存在this指针的,也就是二者的用法是相冲突的。

7、const的作用?

    const可以用来定义变量使其为只读变量,不能做修该,使用const修饰的变量一定要进行初始化。const还可修饰函数的参数或返回值。

    const成员函数:也就是在成员函数的参数列表后面添加const关键字,const成员函数可以访问const的成员变量和非const的成员变量,但是不能修改任何变量。因此,在声明成员函数时,如果该函数不对数据成员做修改操作,则可能把其声明为const成员函数。

    const对象只能访问const成员函数不能访问非const成员函数,非const对象可以访问任意成员函数,包括const成员函数。

8、C++的多态性?

    多态性可以简单的概括为“一个接口,多种方法”,分为编译时多态性和运行时多态性,编译时多态性通过重载函数(重载)实现,运行时多态性通过虚函数(重写)实现。重载要求函数名相同但函数的参数列表不相同,重写要求函数名,函数类型,函数参数列表都相同。

    多态和非多态的区别就是函数地址是早绑定还是晚绑定。如果函数调用在编译时就能确定函数的调用地址则是静态的,即函数地址是早绑定,若函数调用在编译时不能确定函数的调用地址需要在运行时才能确定则是动态的,即函数地址是晚绑定。

    常见的用法是声明基类的指针,用该指针指向派生类对象,调用相应的虚函数,根据指针指向的派生类对象的不同而实现不同的方法。

9、面向对象和面向过程的区别?

    面向过程:是分析出解决问题的步骤,然后用函数一步一步实现这些步骤,在使用时就依次调用函数。优点是:比面向对象的性能高,因为类需要实例化,开销比较大,比较消耗资源,缺点是:没有面向对象易扩展,易维护,易复用。

    面向对象:是把要解决的问题划分成各个对象,建立对象的目的是为了描述某个事物在整个问题解决步骤中的行为。优点是:易维护,易复用,易扩展,因为面向对象具有封装、继承和多态性,可以设计出低耦合的系统,使系统更加灵活,易于维护。缺点是:性能较低。

10、C和C++的区别?

    C语言是面向过程的语言,是结构化的语言,考虑如何通过一个过程对输入进行处理得到输出。C++是面向对象的语言,考虑如何根据具体问题域建立一个合适的对象模型,具有“封装、继承和多态”的特性。封装隐藏了实现细节,使代码模块化;派生类继承父类的方法和数据,扩展原有的模块,实现了代码的复用;多态性是“一个接口,多个方法”,通过派生类重写父类的虚函数,实现接口的复用。

    c语言用malloc和free动态管理内存,不支持函数重载,没有引用;c++用new和delete动态管理内存,支持函数重载,有引用的概念。 

11、虚函数表是针对类的还是针对对象的?同一个类的两个对象的虚函数表是怎么维护的?

    编译器为一个类维护一个虚函数表,每个对象的首地址保存着该虚函数表的指针,同一个类的不同对象指向同一张虚函数表。在类内部添加一个指向该虚函数表的指针,该虚函数表保存所有对象的虚函数入口地址,可以根据此虚函数表找到自己的函数的入口,每个类的虚函数表都不一样。对于纯虚函数来说,相当于一个占位符,纯虚函数在虚函数表中占一个位置,当派生类实现后再把真正的函数指针填进去。

12、内联函数和宏定义的区别?const和#define的区别?

    宏定义是在预编译时把所有的宏名用宏体来替换,简单来说就是字符串的替换。内联函数是在编译时进行代码插入,在调用内联函数的地方直接把内联函数的内容展开,省去了函数调用时的压栈和出栈操作,提高了效率。内联函数是嵌入代码,在调用函数时不进行跳转而是把内联函数的代码插入到相应位置。内联函数在编译时要进行参数类型检查,因此内联函数更安全、可靠,但这是牺牲空间来换取的性能提升。

    const是在编译时替换,define是在预处理的时候替换;define没有类型,不进行类型检查,const有类型,要进行类型检查。

13、堆和栈的区别?

    管理方式:栈是由编译器自动管理而堆需要手动释放。

    增长方向:栈向下增长,以降序分配存储空间;堆是向上增长,以升序分配存储空间

    是否产生碎片:进栈和出栈都是按照顺序依次进行的,不会产生碎片;堆要频繁的进行new和delete,造成存储空间不连续,易产生碎片。

    分配方式:栈能够进行动态分配也能静态分配;堆只能进行动态分配。

    分配效率:栈是系统提供的数据结构,计算机在底层对其进行支持,进栈和出栈都有专门的指令;堆是由c或c++函数库提供的,且机制比较复杂,栈的效率比堆的效率要高,但是却没有堆灵活。

14、c++中类与结构体的区别?

    最大的区别就是默认访问控制,struct作为数据结构的实现体,其默认的数据访问控制为public,class作为类的实现体,其默认的成员访问控制为private的。并且class可以用于定义模板参数,类似于typename,而struct则不能定义模板参数。

15、析构函数的作用?

    析构函数用于释放定义的对象的指针,默认的析构函数不是显示调用的,自定义的析构函数要进行显示调用。

    当类里面只用到了基本数据类型,如int double等时,系统的默认析构函数不进行任何操作;当用到如vector和string等数据类型时,会自己调用系统默认的析构函数。

    如果是自定义的析构函数,且占用了内存空间等资源时,在程序结束时需要调用自定义的析构函数来释放掉占用的资源,防止内存泄漏。

16、操作系统和编译器如何区分全局变量和局部变量?

    操作系统只管进程调度,编译器根据变量存储空间的分配位置来判断,全局变量分配在全局数据段(静态存储区)并在程序运行前加载,局部变量分配在(动态存储区)栈中。

17、结构体和联合体的区别?

    结构体和联合体都是由不同类型的数据成员所构成的,联合体中在某一个时间点只能有一个变量成员存在,所有的变量成员共享一段内存空间,对联合体不同成员赋值时会对其他成员进行重写,其他成员的值就不存在了。结构体中在某一个时间点所有变量成员均存在,且所有变量的内存地址不同,对结构体的成员赋值时是不会产生相互影响的。

18、重载和重写的区别?

    从定义上来说:重载时允许存在多个同名函数,但这些函数的参数表不同(可能是参数个数不同,可能是参数类型不同,可能两者都不相同),重写是子类对父类中的虚函数重新定义。

    从实现原理上来说:重载时编译器根据函数不同的参数表,对函数的名称进行处理,使这些同名函数成为不同的函数。重写是当子类对父类虚函数进行重新定义后,父类指针根据赋给其不同的子类指针,动态的调用该子类的函数,在编译时不能确定调用的是哪个函数。

19、有关纯虚函数的理解?

    纯虚函数是为你的程序制定的一种标准,它只是一个接口,需要在子类中进行实现,且纯虚函数没有函数体,不具备函数功能,不能被调用。包含纯虚函数的类为抽象类,这种类不能直接生成对象,需要在子类中重写虚函数之后才能够使用。

    虚函数是为了继承接口和默认行为,实现动态绑定。纯虚函数只是为了继承接口,行为必须重新定义。

20、内存溢出、内存泄漏的原因?

    内存溢出是指在申请内存空间时出现没有足够的内存空间供其使用的情况,内存溢出的原因大致有如下4种:

    (1) 内存中加载的数据量过于庞大,例如一次从数据库中取较多的数据

    (2) 代码中出现死循环或者循环产生过多重复的对象实体

    (3) 递归调用的层次太深导致堆栈溢出

    (4) 内存泄漏导致内存溢出

    内存泄漏是指在向系统申请了内存空间等资源,在使用完毕后没有进行资源释放,而造成占用有效内存。

21、为什么要声明虚基类?

    在当基类被多个子类继承时,在这些继承路径的汇合处基类会产生多个实例,从而导致二义性。如果想要这个基类只产生一个实例供所有子类共享使用,则要用virtual关键字把该公共基类声明为虚基类。

22、c++文件编译与执行的四个阶段?

    (1)根据文件中的预处理指令来修改源文件

    (2)编译成汇编代码

    (3)将汇编代码翻译成机器指令

    (4)链接目标代码生成可执行程序

23、定义和声明的区别?

    变量声明:用于告诉程序变量的类型和名字

    变量定义:为变量分配内存空间,还可以为变量赋初值,变量有且只有一个定义。在变量定义的同时也是声明了变量的类型和名字。

    extern关键字:用extern关键字是声明变量而不是定义变量。

24、c++内存分配的方式?

    (1)在静态存储区分配内存,编译时分配,在程序运行的整个期间都存在

    (2)在栈中分配内存,系统自动进行分配和释放,例如局部变量就是在栈中分配内存空间

    (3)在堆中分配内存,需要程序员手动进行分配和释放,并指定大小。

    栈可以进行动态分配和静态分配,而堆只能够进行动态分配,栈的效率比堆的高但没有堆灵活。

25、继承体系下同名成员函数的关系?

    (1)重载:在同一作用域,函数名相同,参数列表不同,返回值类型可以相同也可以不相同

    (2)重写:在不同的作用域(分别在父类和子类),函数名相同,参数列表相同,返回值类型相同,基类的函数需要有virtual关键字声明。

    (3)重定义:在不同的作用域(分别在父类和子类),函数名相同,在父类和派生类中只要不符合重写的均是重定义

26、什么情况下会调用拷贝构造函数(三种情况)?

    类会自动生成构造函数:普通构造函数和拷贝构造函数,当生成一个普通的类对象的时候会自动调用普通构造函数,当用一个类对象去初始化另一个对象时会调用拷贝构造函数;

    调用拷贝构造函数的三种情况:

    (1)当用一个类的对象去初始化另一个新的对象时

    (2)当函数的参数为类的对象且为值传递的时候,当为引用时不会调用

    (3)当函数的返回值为类的对象或者引用的时候

27、虚函数是怎么实现的?

    每一个含有虚函数的类都有一个虚函数表,虚函数表中保存着该类的所有虚函数的函数指针(地址),类的实例对象不包含虚函数表,只有虚函数指针。

c++到此就更新完毕了~

原创文章 54 获赞 99 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_38938670/article/details/105274352