【面试常问】——C++系列

【面试常问】——C++系列

参考博客

问题1:

    头文件中的ifndef/define/endif有什么作用?

    答:这是C++预编译头文件保护符,防止头文件被重复引用,即保证即使文件被多次包含,头文件也只定义一次。

    #ifndef/#define/#endif使用详解

问题2:

    #include<file.h> 与 #include "file.h"的区别?

    答:前者是从标准库路径寻找和引用file.h,而后者是从当前工作路径搜寻并引用file.h。

问题3:

    评价一下C/C++各自的特点?

    答:C语言是一种结构化语言,面向过程,基于算法和数据结构,所考虑的是如何通过一个过程或者函数从输入得到输出;C++面向对象,基于类、对象和继承,所考虑的是如何构造一个对象模型,让这个模型能够契合与之对应的问题,通过获取对象的状态信息得到输出或实现过程控制。

问题4:

    const 有什么用途?

    答:在C/C++中,可以定义const常量,修饰函数的返回值和形参;在C++中,还可以修饰函数的定义体,定义类的const成员函数。被const修饰的东西受到强制保护,可以预防意外的变动,提高了程序的健壮性。

问题5:

    const(常量)和#define(宏)有什么区别?

    答:

    1)const常量有数据类型,而宏常量没有数据类型;const是在编译、运行的时候起作用,而define是在编译的预处理阶段起作用;编译器可以对const进行类型安全检查,而对#define只进行字符替换,没有类型安全检查,并且在字符替换时可能会产生意料不到的错误。

    2)使用常量可能比使用#define导致产生更小的目标代码,这是因为预处理器“盲目地将宏名称BUFSIZE定义为100”可能会导致目标代码出现多分100的备份,但常量就不会出现这种情况。

    3)同时,const还可以执行常量折叠(常量折叠是在编译时间简单化常量表达的一个过程,,简单来说就是将常量表达式计算求值,并用求来的值来替换表达式,放入常量表),也就是说,编译器在编译时可以通过必要的计算把一个复杂的常量表达式缩减成简单的。

    4)const常量可以进行调试的,define是不能进行调试的,因为在预编译阶段就已经替换掉了;const不能重定义,而#define可以通过#undef取消某个符号的定义,再重新定义。

问题6:

    sizeof与strlen的区别?

答:

(1)sizeof的返回值类型为size_t(unsigned int);

(2)sizeof是运算符,而strlen是函数;

  (3)sizeof可以用类型做参数,其参数可以是任意类型的或者是变量、函数,而strlen只能用char*做参数,且必须是以’\0’结尾;

(4)数组作sizeof的参数时不会退化为指针,而传递给strlen是就退化为指针;

(5)sizeo是编译时的常量,而strlen要到运行时才会计算出来,且是字符串中字符的个数而不是内存大小。

问题7:

    引用(reference)和指针的区别?

    答:

    1)引用不能为空,当引用被创建时,必须被初始化。而指针可以为空值,可以在任何时候被初始化;

    2)一旦一个引用被初始化为指向一个对象,它就不能被改变为对另一个对象的引用。指针则可以在任何时候指向另一个对象;

    3)不可能有null引用。必须确保引用是和一块合法的存储单元关联;

    4)sizeof(引用)得到的是所指向的变量(对象)的大小,而sizeof(指针)得到的是指针本身的大小;

    5)给引用赋值修改的是该引用所关联的对象的值,而并不是使引用与另一个对象相关联;

    6)引用使用时不需解引用,而指针需解引用,引用和指针的自增(++)操作运算意义不一样;

    7)如果返回动态分配的对象或内存,必须使用指针,引用可能引起内存泄露;

    8)当使用&运算符取一个引用的地址时,其值为所引用的变量的地址;而对指针使用&运算,取的是指针变量的地址。

问题8:

    数组和指针的区别?

    答:

    (1)数组要么在全局数据区被创建,要么在栈上被创建;指针可以随时指向任意类型的内存块;

    (2)修改内容上的差别:

    char a[] = “hello”;
    a[0] = ‘X’;
    char *p = “world”; // 注意p 指向常量字符串
    p[0] = ‘X’; // 编译器不能发现该错误,运行时错误

    (3) 用运算符sizeof 可以计算出数组的容量(字节数)。sizeof(p),p 为指针得到的是一个指针变量的字节数,而不是p 所指的内存容量。C++/C 语言没有办法知道指针所指的内存容量,除非在申请内存时记住它。注意当数组作为函数的参数进行传递时,该数组自动退化为同类型的指针。

问题9:

    空指针和悬垂指针的区别?

    答:空指针是指被赋值为NULL的指针;delete指向动态分配对象的指针将会产生悬垂指针。

    (1) 空指针可以被多次delete,而悬垂指针再次删除时程序会变得非常不稳定;

    (2) 使用空指针和悬垂指针都是非法的,而且有可能造成程序崩溃,如果指针是空指针,尽管同样是崩溃,但和悬垂指针相比是一种可预料的崩溃。

问题10:

    什么是智能指针?

    当有几个对象 共同用一个资源时,则它们同时指向了这个资源,如果用普通的指针,则在其中一个对象析构时就会将这个共用的资源销毁,有了智能指针就不用担心,它里面有对象计数,代表有几个对象在用它,销毁一个就减1,直到为0,为0时就表示没有对象用这个资源了,就会自动释放资源存储区!

    C++为什么要用智能指针

    C++智能指针

问题11:

    C++中有malloc/free,为什么还有new/delete?

    答:malloc/free是C/C++标准库函数,new/delete是C++运算符。他们都可以用于动态申请和释放内存。

    对于内置类型数据而言,二者没有多大区别。malloc申请内存的时候要制定分配内存的字节数,而且不会做初始化;new申请的时候有默认的初始化,同时可以指定初始化;

    对于类类型的对象而言,用malloc/free无法满足要求的。对象在创建的时候要自动执行构造函数,消亡之前要调用析构函数。由于malloc/free是库函数而不是运算符,不在编译器控制之内,不能把执行构造函数和析构函数的任务强加给它,因此,C++还需要new/delete。

问题12:

    面向对象技术的三个基本特征是什么?

    答:封装、继承、多态。

   


    封装

    封装是面向对象的特征之一,是类和对象概念的主要特性。

    封装,也就是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。

    继承

    面向对象编程 (OOP) 语言的一个主要功能就是“继承”。

    继承是指这样一种能力:它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。

    通过继承创建的新类称为“子类”或“派生类”; 被继承的类称为“基类”、“父类”或“超类”。

    继承的过程,就是从一般到特殊的过程。

    多态

    多态性(polymorphisn)是允许你将父对象设置成为和一个或更多的他的子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作。简单的说,就是一句话:将基类类型的指针或者引用指向派生类型的对象。

    实现多态,有二种方式,覆盖(重写),重载。

    覆盖,是指子类重新定义父类的虚函数的做法。

    重载,是指允许存在多个同名函数,而这些函数的参数表不同(或许参数个数不同,或许参数类型不同,或许两者都不同)。

    总结

    封装可以隐藏实现细节,使得代码模块化;继承可以扩展已存在的代码模块(类);它们的目的都是为了——代码重用。而多态则是为了实现另一个目的——接口重用

问题13:

   C++空类默认有哪些成员函数?

    答:默认构造函数、析构函数、复制构造函数、赋值函数

 问题14:

    哪一种成员变量可以在一个类的实例之间共享?

    答:static静态成员变量

问题15:

    继承层次中,为什么基类析构函数是虚函数?

    答:

    在实现多态时,当用基类操作派生类,在析构时防止只析构基类而不析构派生类的状况发生。

    编译器总是根据类型来调用类成员函数。但是一个派生类的指针可以安全地转化为一个基类的指针。这样删除一个基类的指针的时候,C++不管这个指针指向一个基类对象还是一个派生类的对象,调用的都是基类的析构函数而不是派生类的。如果你依赖于基类的析构函数的代码来释放资源,而没有重载析构函数,那么会有资源泄漏。


问题16:

    为什么构造函数不能为虚函数?

    答:虚函数采用一种虚调用的方法。虚调用是一种可以在只有部分信息的情况下工作的机制。如果创建一个对象,则需要知道对象的准确类型,因此构造函数不能为虚函数。

    或者:

    虚函数是在不同类型的对象产生不同的动作,现在对象还没产生,如何使用虚函数来完成你想完成的动作。

问题17:

    如果虚函数是有效的,那为什么不把所有函数设为虚函数?

    答:不行。首先,虚函数是有代价的,由于每个虚函数的对象都要维护一个虚函数表,因此在使用虚函数的时候都会产生一定的系统开销,这是没有必要的。

问题18:

    重载和覆盖(重写)有什么区别?

    答:虚函数是基类希望派生类重新定义的函数,派生类重新定义基类虚函数的做法叫做覆盖(重写);

    重载就在允许在相同作用域中存在多个同名的函数,这些函数的参数表不同。重载的概念不属于面向对象编程,编译器根据函数不同的形参表对同名函数的名称做修饰,然后这些同名函数就成了不同的函数。

    重载的确定是在编译时确定,是静态的;虚函数则是在运行时动态确定。

问题19:

    公有继承、受保护继承、私有继承?

    答:

    (1)公有继承时,派生类的成员函数可以访问基类中的公有和受保护成员,派生类对象可以访问基类中的公有成员

    (2)私有继承时,基类的成员只能被直接派生类的成员访问,无法再往下继承;

    (3)受保护继承时,基类的成员也只被直接派生类的成员访问,无法再往下继承。

问题20:

    在类中,有哪几种情况只能用构造函数初始化列表而不能用赋值初始化?

    (1) 常量成员,因为常量只能初始化不能赋值,所以必须放在初始化列表里面;

    (2)引用类型,引用必须在定义的时候初始化,并且不能重新赋值,所以也要写在初始化列表里面;

    (3)没有默认构造函数的类类型,因为使用初始化列表可以不必调用默认构造函数来初始化,而是直接调用拷贝构造函数初始化。

问题21:

    什么是虚函数指针?

    答:虚指针或虚函数指针是虚函数的实现细节。带有虚函数的每一个对象都有一个虚指针指向该类的虚函数表。

问题22:

    C++如何阻止一个类被实例化?一般在什么时候将构造函数声明为private?

    答:(1)将类定义为抽象基类或者将构造函数声明为private;

           (2)不允许类外部创建类对象,只能在类内部创建对象。

问题23:

    经常要操作的内存分为哪几个类别?

    答:(1)栈区(stack):由编译器自动分配和释放,存放函数的参数值、局部变量的值等;

           (2)堆(heap):一般由程序员分配和释放,存放动态分配的变量,malloc和new都在这里分配内存;

           (3)全局区(静态区):全局变量和静态变量存放在这一块,初始化的和未初始化的分开放;

           (4)文字常量区:常量字符串就放在这里,程序结束自动释放;

           (5)程序代码区:参访函数体的二进制代码。

问题24:

    请讲述堆和栈的区别。

    参考博客

    答:(1)申请方式不同。栈上由系统自动分配和释放;堆上由程序员自己申请并指明大小,也是由程序员负责销毁;

          (2)栈是向低地址扩展的数据结构,大小很有限;堆是向高地址扩展,是不连续的内存区域,空间相对大且灵活;

          (3)栈由系统分配和释放,速度快;堆由程序员控制,一般较慢,且容易产生碎片。

问题25:

    如何引用一个已经定义过的全局变量?

    答:可以用引用头文件的方式,也可以用extern关键字。如果用引用头文件方式来引用某个在头文件中声明的全局变量,假定你将那个变量写错了,那么在编译期间会报错,如果你用extern方式引用时,假定你犯了同样的错误,那么在编译期间不会报错,而在连接期间报错。

    参考博客

问题26:

    多态类中的虚函数表是 Compile-Time,还是 Run-Time时建立的?

    答:虚拟函数表是在编译期就建立了,各个虚拟函数这时被组织成了一个虚拟函数的入口地址的数组。而对象的隐藏成员--虚拟函数表指针是在运行期--也就是构造函数被调用时进行初始化的,这是实现多态的关键。

问题27:

    一个父类写了一个 virtual 函数,如果子类覆盖它的函数不加 virtual ,也能实现多态?

    在子类的空间里,有没有父类的这个函数,或者父类的私有变量? (华为笔试题)

    答:只要基类在定义成员函数时已经声明了 virtue关键字,在派生类实现的时候覆盖该函数时,virtue关键字可加可不加,不影响多态的实现。子类的空间里有父类的所有变量(static除外)。

问题28:

    应用程序在运行时的内存包括代码区和数据区,其中数据区又包括哪些部分?

    答:对于一个进程的内存空间而言,可以在逻辑上分成 3个部份:代码区,静态数据区和动态数据区。

    动态数据区一般就是“堆栈”。 栈是一种线性结构,堆是一种链式结构。进程的每个线程都有私有的“栈”。

    全局变量和静态变量分配在静态数据区,本地变量分配在动态数据区,即堆栈中。程序通过堆栈的基地址和偏移量来访问本地变量。

问题29:

    C++里面是不是所有的动作都是main()引起的?如果不是,请举例。

    答:比如全局变量的初始化,就不是由main函数引起的。

    举例: class A{};

               A a; //a的构造函数先执行

               int main() {};

问题30:

    对于一个频繁使用的短小函数,在C语言中应用什么实现,在C++中应用什么实现?

    答:c用宏定义,c++用inline。

问题31:

    static全局变量与普通的全局变量有什么区别?static局部变量和普通局部变量有什么区别?static函数与普通函数有什么区别?

    答:static全局变量与普通全局变量区别:static全局变量只初使化一次,防止在其他文件单元中被引用;

    static局部变量和普通局部变量区别:static局部变量只被初始化一次,下一次依据上一次结果值;

    static函数与普通函数区别:static函数在内存中只有一份,普通函数在每个被调用中维持一份拷贝。









猜你喜欢

转载自blog.csdn.net/qq_27022241/article/details/80389070
今日推荐