C++ 笔试、面试可能出的题

版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_39731083/article/details/81262149
  • 重载(overload)和覆盖(override)的区别?

    1、从定义上来说:

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

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

    2、从实现原理上来说:

    重载:编译器根据函数不同的参数表,对同名函数的名称做修饰,然后这些同名函数就成了不同的函数(至少对于编译器来说是这样的)。对于这两个函数的调用,在编译器间就已经确定了,是静态的。也就是说,它们的地址在编译期就绑定了(早绑定),因此,重载和多态无关

    覆盖:和多态真正相关。当子类重新定义了父类的虚函数后,父类指针根据赋给它的不同的子类指针,动态的调用属于子类的该函数,这样的函数调用在编译期间是无法确定的(调用的子类的虚函数的地址无法给出)。因此,这样的函数地址是在运行期绑定的(晚绑定)。

 

  • C和C++的特点与区别?
    一、C语言特点:
    1.作为一种面向过程的结构化语言,易于调试和维护;

    2.表现能力和处理能力极强,可以直接访问内存的物理地址;

    3.C语言实现了对硬件的编程操作,也适合于应用软件的开发;

    4.C语言还具有效率高,可移植性强等特点。

    二、C++语言特点:

    1.在C语言的基础上进行扩充和完善,使C++兼容了C语言的面向过程特点,又成为了一种面向对象的程序设计语言;

    2.可以使用抽象数据类型进行基于对象的编程;

    3.可以使用多继承、多态进行面向对象的编程;

    4.可以担负起以模版为特征的泛型化编程。

    三、C++与C语言的本质差别:
    在于C++是面向对象的,而C语言是面向过程的。或者说C++是在C语言的基础上增加了面向对象程序设计的新内容,是对C语言的一次更重要的改革,使得C++成为软件开发的重要工具。

 

  • 求函数返回值

    假定x = 9999。 答案:8。思路:将x转化为2进制,看含有的1的个数。

    int func(x) 
    { 
        int countx =0; 
        while(x) 
        { 
              countx ++; 
              x = x&(x-1); 
         } 
        return countx; 
    }

     

  • 引用相关问题

    一、什么是“引用”?申明和使用“引用”要注意哪些问题?

    引用就是某个目标变量的“别名”(alias),对应用的操作与对变量直接操作效果完全相同。申明一个引用的时候,切记要对其进行初始化。引用声明完毕后,相当于目标变量名有两个名称,即该目标原名称和引用名,不能再把该引用名作为其他变量名的别名。声明一个引用,不是新定义了一个变量,它只表示该引用名是目标变量名的一个别名,它本身不是一种数据类型,因此引用本身不占存储单元,系统也不给引用分配存储单元。不能建立数组的引用。

    二、将“引用”作为函数参数有哪些特点?

    传递引用给函数与传递指针的效果是一样的(改变它的值,函数结束后该值被改变了)。这时,被调函数的形参就成为原来主调函数中的实参变量或对象的一个别名来使用,所以在被调函数中对形参变量的操作就是对其相应的目标对象(在主调函数中)的操作。

    ②使用引用传递函数的参数,在内存中并没有产生实参的副本,它是直接对实参操作;而使用一般变量传递函数的参数,当发生函数调用时,需要给形参分配存储单元,形参变量是实参变量的副本,如果传递的是对象,还将调用拷贝构造函数(一般变量传递)。因此,当参数传递的数据较大时,用引用比用一般变量传递参数的效率和所占空间都好。

    ③使用指针作为函数的参数虽然也能达到与使用引用的效果,但是,在被调函数中同样要给形参分配存储单元,且需要重复使用"*指针变量名"的形式进行运算,这很容易产生错误且程序的阅读性较差;另一方面,在主调函数的调用点处,必须用变量的地址作为实参。而引用更容易使用,更清晰。

    三、在什么时候需要使用“常引用”?
    如果既要利用引用提高程序的效率,又要保护传递给函数的数据不在函数中被改变,就应使用常引用。
    常引用声明方式:const 类型标识符 &引用名=目标变量名;

    string foo( );
    void bar(string&s)
    // 那么下面的表达式将是非法的:
    bar(foo( ));
    bar("hello world");

    foo( )和"hello world"串都会产生一个临时对象。而在C++中,这些临时对象都是const类型的。因此上面的表达式就是试图将一个const类型的对象转换为非const类型,这是非法的。所以引用型参数应该在能被定义为const的情况下,尽量定义为const 。

    四、将“引用”作为函数返回值类型的格式、好处和需要遵守的规则?

    类型标识符 &函数名(形参列表及类型说明)
    { 
      //函数体
    }

    好处:在内存中不产生被返回值的副本
    注意点:

    不能返回局部变量的引用。主要原因是局部变量会在函数返回后被销毁,因此被返回的引用就成为了"无所指"的引用,程序会进入未知状态。

    不能返回函数内部new分配的内存的引用。 虽然不存在局部变量的被动销毁问题,可对于这种情况(返回函数内部new分配内存的引用),又面临其它尴尬局面。例如,被函数返回的引用只是作为一个临时变量出现,而没有被赋予一个实际的变量,那么这个引用所指向的空间(由new分配)就无法释放,造成memory leak

    可以返回类成员的引用,但最好是const。 主要原因是当对象的属性是与某种业务规则(business rule)相关联的时候,其赋值常常与某些其它属性或者对象的状态有关,因此有必要将赋值操作封装在一个业务规则当中。如果其它对象可以获得该属性的非常量引用(或指针),那么对该属性的单纯赋值就会破坏业务规则的完整性。

 

  • .h头文件中的ifndef/define/endif 的作用?
    防止该头文件被重复引用。
    #ifndef name_h
    #define name_h
    //头文件内容
    
    #endif

 

  • 在C++程序中调用被C 编译器编译后的函数,为什么要加extern "C"?
    extern是C/C++语言中表明函数和全局变量作用范围(可见性)的关键字,该关键字告诉编译器,其声明的函数和变量可以在本模块或其它模块中使用。

    extern "C"是连接申明(linkage declaration),被extern "C"修饰的变量和函数是按照C语言方式编译和连接的。

    因为C++和C是两种完全不同的编译链接处理方式,C++语言支持函数重载,C语言不支持函数重载。函数被C++编译后在库中的名字与C语言的不同。所以如果直接在C++里面调用C函数,这样链接起来是通不过的,会报链接错误,找不到函数体,所以要在 C++文件里面显示声明以下一些函数是C写的,要用C的方式来处理,这个在C++设计初期就考虑到兼容性的问题,所以是可以解决的。

    具体做法:

    ①引用头文件前需要加上 extern “C”,如果引用多个,那么就如下所示
    extern “C”
    {
    #include “A.h”
    #include “B.h”
    #include “C.h”
    #include “D.h”
    };
    然后在调用这些函数之前,需要将函数也全部声明一遍。
    ②C++调用C函数的方法,将用到的函数全部重新声明一遍
    extern “C”
    {
    extern void A_app(int);
    extern void B_app(int);
    extern void C_app(int);
    extern void D_app(int);

    }

 

  • 面向对象编程的三个基本特征,并简单叙述之?

    ①封装:将客观事物抽象成类,每个类对自身的数据和方法实行 protection(private, protected,public)

    ②继承:广义的继承有三种实现形式:实现继承(指使用基类的属性和方法而无需额外编码的能力)、可视继承(子窗体使用父窗体的外观和实现代码)、接口继承(仅使用属性和方法,实现滞后到子类实现)。前两种(类继承)和后一种(对象组合=>接口继承以及纯虚函数)构成了功能复用的两种方式。

    ③多态:系统能够在运行时,能够根据其类型确定调用哪个重载的成员函数的能力,称为多态性。(见:C++中类的多态与虚函数的使用)

 

  • 多态的作用?

    ①隐藏实现细节,使得代码能够模块化;扩展代码模块,实现代码重用;

    ②接口重用:为了类在继承和派生的时候,保证使用家族中任一类的实例的某一属性时的正确调用。

 

 

  • new delete 与malloc free 的联系与区别?
    都是在堆(heap)上进行动态的内存操作。用malloc函数需要指定内存分配的字节数并且不能初始化对象,new 会自动调用对象的构造函数。delete 会调用对象的析构函数,而free 不会调用对象的析构函数。

 

  • C++是不是类型安全的?
    不是。两个不同类型的指针之间可以强制转换(用reinterpret cast)。C#是类型安全的。

 

  • struct  class 的区别

    struct 的成员默认是公有的,而类的成员默认是私有的。struct 和 class 在其他方面是功能相当的。(没错,struct 也可以定义成员函数,也就是说struct也能定义类

 

  • 当一个类A 中没有声明任何成员变量与成员函数,这时sizeof(A)的值是多少?如果不是零,请解释一下编译器为什么没有让它为零?

    肯定不是零。举个反例,如果是零的话,声明一个class A[10]对象数组,而每一个对象占用的空间是零,这时就没办法区分A[0],A[1]…了。
    一定要有空间,不然我们声明实例的时候,没法使用。空间多大由编译器决定。VS中默认1byte,加入构造和析构函数还是1byte。如果析构函数声明为虚函数,由于存在虚函数表的指针,32位上sizeof是4byte,64位是8byte。
    更多sizeof详情可看这篇:sizeof 相关问题

 

  • 编程时变量存放区域
    堆区(heap):程序员分配和释放,malloc、callloc、new 等
    栈区(stack):编译器自动分配和释放、局部的const变量也在这
    全局区(静态区):放全局变量和静态变量(static)
    文字常量区
    程序代码区

 

  • 请说出const与#define 相比,有何优点(区别)?
    ①const 常量有数据类型,而宏常量没有数据类型。编译器可以对前者进行类型安全检查。而对后者只进行字符替换,没有类型安全检查,并且在字符替换可能会产生意料不到的错误。
    ②有些集成化的调试工具可以对const 常量进行调试,但是不能对宏常量进行调试。
    ③define在预处理阶段进行,const在编译阶段

 

  • 简述数组与指针的区别?
    数组要么在静态存储区被创建(如全局数组),要么在栈上被创建。指针可以随时指向任意类型的内存块。
    ①修改内容上的差别
    char a[] = “hello”;
    a[0] = ‘X’;
    char *p = “world”; // 注意p 指向常量字符串
    p[0] = ‘X’; // 编译器不能发现该错误,运行时错误
    ②用运算符sizeof 可以计算出数组的容量(字节数)。sizeof(p),p 为指针得到的是一个指针变量的字节数,而不是p 所指的内存容量。注意当数组作为函数的参数进行传递时,该数组自动退化为同类型的指针。

 

  • 请说出static和const关键字尽可能多的作用。

    static关键字至少有下列n个作用:
    ①函数体内static变量的作用范围为该函数体,不同于auto变量,该变量的内存只被分配一次,因此其值在下次调用时仍维持上次的值;
    ②在模块内的static全局变量可以被模块内函数访问,但不能被模块外其它函数访问;
    ③在模块内的static函数只可被这一模块内的其它函数调用,这个函数的使用范围被限制在声明它的模块内;
    ④在类中的static成员变量属于整个类所拥有,对类的所有对象只有一份拷贝;
    ⑤在类中的static成员函数属于整个类所拥有,这个函数不接收this指针,因而只能访问类的static成员变量

    const关键字至少有下列n个作用:
    ①欲阻止一个变量被改变,可以使用const关键字。在定义该const变量时,通常需要对它进行初始化,因为以后就没有机会再去改变它了;
    ②对指针来说,可以指定指针本身为const,也可以指定指针所指的数据为const,或二者同时指定为const;
    ③在一个函数声明中,const可以修饰形参,表明它是一个输入参数,在函数内部不能改变其值;
    ④对于类的成员函数,若指定其为const类型,则表明其是一个常函数,不能修改类的成员变量;
    ⑤对于类的成员函数,有时候必须指定其返回值为const类型,以使得其返回值不为“左值”。例如:
    const classA operator*(const classA& a1,const classA& a2);
      operator*的返回结果必须是一个const对象。如果不是const,这样的变态代码也不会编译出错:

    classA a, b, c;
    (a * b) = c; // 对a*b的结果赋值

 

  • 关于虚函数的问题
    如果没有关键字virtual,程序将根据引用或指针的类型来选择方法;而如果使用了virtual程序会根据引用或指针指向的对象类型来选择方法。

    根据引用或指针类型选择方法(无virtual):基类指针可以在不进行显式类型转换的情况下指向派生类对象,基类引用也一样。但基类指针或引用只能用于调用基类的方法。不可以将基类对象和地址赋给派生类引用和指针。这样做的目的:可以让派生类对象去用基类的方法(可以由他来实现多态)。
    //Brass为父类,BrassPlus为子类。在ViewAcct()没有声明为virtual的情况下:
    Brass dom;
    BrassPlus dot;
    Brass &b1_ref=dom;
    Brass &b2_ref=dot;
    b1_ref.ViewAcct();    //调用父类方法
    b2_ref.ViewAcct();    //调用父类方法
    根据引用或指针指向的对象类型(有virtual):在同时需要管理父类和子类对象的时候,可以把两者共同拥有的成员函数声明为虚函数。这样可以声明一个父类的指针数组,既可以指向父类,又可以指向子类。指向父类时,调用父类的方法;指向子类时,调用子类的方法。
    //Brass为父类,BrassPlus为子类。在ViewAcct()声明为virtual的情况下:
    Brass dom;
    BrassPlus dot;
    Brass &b1_ref=dom;
    Brass &b2_ref=dot;
    b1_ref.ViewAcct();    //调用父类方法
    b2_ref.ViewAcct();    //调用子类方法

    注意问题:
    ①构造函数不能为虚函数
    ②关键字 virtual 声明用于类声明的方法原型中,没有在具体方法定义中
    ③析构函数可以并且应当为虚函数

 

  • 局部变量能否和全局变量重名?
    能,局部会屏蔽全局。要用全局变量,需要使用 ":: " 
    局部变量可以与全局变量同名,在函数内引用这个变量时,会用到同名的局部变量,而不会用到全局变量。对于有些编译器而言,在同一个函数内可以定义多个同名的局部变量,比如在两个循环体内都定义一个同名的局部变量,而那个局部变量的作用域就在那个循环体内。 

 

  • 初始化问题:
    对于内置类型而言,new仅仅是分配内存,除非后面显示加(),相当于调用它的构造函数,对于自定义类型而言,只要一调用new,那么编译器不仅仅给它分配内存,还调用它的默认构造函数初始化,即使后面没有加()。例如:
    int *p1 = new int[10]; //随机值
    int *p2 = new int[10]();    //初始化了

     

  • 访问控制:protected
    protect和private相似,在类外无法直接访问。它们两者的区别在于派生类:派生类的成员可以直接访问基类成员,但不能直接访问基类的私有成员。    

 

  • const指针 和 指向const的指针
    ①指向const的指针,它的意思是指针指向的内容是不能被修改的。它有两种写法。
    const int* p; (推荐)
    int const* p;
    ②const指针,它的意思是指针本身的值是不能被修改的。它只有一种写法。
    int* const p=一个地址;           (因为指针本身的值是不能被修改的所以它必须被初始化)
    ③还有一种情况是这个指针本身和它指向的内容都是不能被改变的。
    const int* const p=一个地址;
    int const* const p=一个地址;

 

  • C/C++的main函数传参:

    int main(int argc, char** argv)

    argc是一个自动记数的数字,你传了3个参数就会是3
    argv是一个字符串数组,例如argv[0]表示第一个字符串


    示例:
    程序名称为 input,在其目录下运行程序
    ./input test1 test2
    这种情况下:
    argc为3
    argv[0]是"./input"
    argv[1]是"test1"
    argv[2]是"test2"

 

  • C++三种类成员、三种继承方式
    ①类成员:
      类外部 子类 本类
    public 允许访问 允许访问 允许访问
    protected 禁止访问 允许访问 允许访问
    private 禁止访问 禁止访问 允许访问










    ②继承方式:

 

  • 以下枚举,输出x会是什么:
     
    enum string{    
        x1,    
        x2,    
        x3=10,    
        x4,    
        x5,    
    } x;
    在全局域定义enum,输出x是0,但是在局部域定义enum,输出x是随机数。

 

  • 除号的正负取舍和一般的算数一样,符号相同为正,相异为负   -3/16=0    16/-3=-5
    求余符号的正负取舍和被除数符号相同   -3%16=-3      16%-3=1

 

  • 函数模板与类模板有什么区别?
    函数模板的实例化是由编译器在处理函数调用时自动完成的,而类模板的实例化必须由程序员在程序中显式地指定。

 

 

 

展开阅读全文

没有更多推荐了,返回首页