C++面试出现频率最高的面试题

来源:互联网 发布:青云云计算 编辑:程序博客网 时间:2024/05/01 00:58

http://blog.csdn.net/lwgechen/article/details/77152319
http://blog.csdn.net/wdzxl198/article/details/9102759/

1.new delete malloc free的关系

  • 区别:
    • new、delete是c++的运算符,malloc、free是c/c++的标准库函数。
    • new调用对象的构造函数,delete调用对象的析构函数。
    • 对于非内部数据类型的对象(非原子类型),光用malloc和free无法满足对象动态内存的申请。
    • 对象在创建的同时要自动执行构造函数(c++类的一种机制),对象在消亡之前要自动执行析构函数(c++类的一种机制)
    • malloc、free是库函数不是运算符,不在编译器控制权限内,不能够把执行构造函数和析构函数的任务(在编译阶段就要完成)强加于malloc和free。因此c++语言需要一个能完成动态内存分配和初始化工作的运算符new和一个能完成清理与释放内存工作的运算符delete。
    • 运算符在编译节点执行,库函数在程序运行阶段执行。
  • 相同:
    • 都用于申请动态内存和释放内存

2.delete delete[]的区别

  • delete只会调用一次析构函数。
  • delete[]会调用每个成员的析构函数。
  • 当delete运算符用于数组时,它为每个数组元素调用析构函数,然后调用operator delete来释放内存。
  • new和delete配套,new[]和delete[]配套。
memTest *mTest1 = new memTest[10];//memTest是非原子类型,memTest[10]调用10次非原子类型memTest *mTest2 = new memTest;int *pInt1 = new int[10];//int a[10],int[10]是类型,调用10次int原子类型int *pInt2 = new int;delete[]pInt1;//1delete[]pInt2;//2delete[]mTest1;//3delete[]mTest2;//4//报错
  • 对于内建简单数据类型(原子类型),delete和delete[]功能是一样的。
  • 对于自定义的复杂数据类型,delete和delete[]不能互用。delete[]删除一个指针,delete删除一个指针。
  • 用new分配的内存用delete删除,用new[]分配的内存用delete[]删除。
  • 原子类型没有析构函数,所以delete/delete[]互用没有问题。

3.C++有哪些性质

  • 继承、封装、多态

4.子类析构时要调用父类的析构函数吗

  • 析构函数调用的次序是先调用派生类的析构函数,后调用基类的析构函数。
  • 在基类的析构函数被调用的时候,派生类的信息已经全部销毁了。
  • 定义一个对象时先调用基类的构造函数,然后调用派生类的构造函数。

5.多态、虚函数、纯虚函数

  • 多态:是对于不同对象接受相同信息时产生不同的动作。
  • c++的多态性具体体现在运行时编译时两个方面,在程序运行时的多态性通过继承和虚函数来实现。
  • 编译时 多态: 函数重载
  • 运行时 多态: 俗称多态, 虚函数的 重写overwrite覆盖
  • 编译时的多态:函数的参数列表不同,这样区分不同的函数。它们在编译后就自动变成两个不同的函数名。像函数的重载这样编译时根据参数列表就知道了函数地址了。
  • 运行时的多态:用到动态绑定技术,在程序运行前不知道,会调用那个方法,而到运行时通过运算程序,动态的算出被调用的地址。像子类中的虚函数覆盖基类中的虚函数。

6.求下面函数的返回值(微软)

int func(x){    int countx = 0;    while(x)    {        printf("%d\n",x);        countx++;        x = x&(x-1);//核心代码    }    return countx;}//假定x = 9999,答案:8//思路:将x转化成2进制,看含有1的个数

输出:
9999
9998
9996
9992
9984
9728
9216
8192
8


7.什么是引用,申明和使用引用要注意的问题

  • 首先强调一点引用是C++中的一种机制,在c语言中没有。
  • 引用就是某个目标变量的“别名alias”,对应用的操作与对变量直接操作效果完全相同。
  • 引用的地址就是原变量的地址,引用的值就是原变量的值。
  • 申请一个引用的时候,切记要对其进行初始化。
  • 声明一个引用,不是新定义一个变量,它本身不是一种数据类型,因此引用本身不占存储单元,系统也不给引用分配存储单元。
  • 1.不能建立引用数组,但是可以建立数组的引用。
    • c++中,引用可以说只是某个变量的别名,所谓别名,是和指针类型区分开的,指针类型可以指向某个变量,而且指针类型本身也是一个变量,而引用本身实际上不是一个变量。更本质来说,可以理解为引用没有自身的地址,不占用内存空间,因此,声明引用数组没有办法分配内存空间,因为根本就没有空间可以分配给引用,所以不能声明和定义引用数组。
int &arr[3] = {2,3,4};//声明"引用数组"是错误的,arr并没有自己的空间来存放后面的值//--------------------------------int arr[3] = {2,3,4};//arr是数组变量名,int[3]是类型int (&ref)[3] = arr;//正确//&ref是引用名,int[3]是类型
  • 2.数组中的元素不能是引用

    • 数组元素的引用: int& arr[3];可以有。
    • 但是引用不可以作为数组中的元素,因为引用不支持传统意义的复制。
    • 传统的复制:int a = b;
      • 这里a和b在内存中分别占用不同的内存空间,但是内容一致。
      • 如果int& a = b;这个时候,内存中a并不被分配内存,所以没有复制可言。
    • 所以对于数组元素是引用来说,没法完成元素的复制操作,所以数组中的元素不能是引用。

8.将引用作为函数参数有哪些特点

  • 传递引用给函数与传递指针的效果是一样的。 这时被掉函数的形参就成为原来主调函数中的实参变量或者对象的一个别名。所以在被调函数中对形参变量的操作就是对其相应的目标对象(在主调函数中)的操作。前提:被调函数的形参也要是引用,引用接收引用,如果被调函数是普通变量的化,那么那一刻将发生传值。
#include <iostream>using namespace std;void fun(int& num)//引用接受引用{    num++;    cout << num << endl;//11}int main(void){    int num = 10;    int& a = num;    fun(a);//把别名告诉别人    cout << num << endl;//11    return 0;}
  • 使用引用传递函数的参数,在内存中并没有产生实参副本,它是直接对实参操 作,而使用一般变量传递函数的参数,当发生函数调用时,需要给形参分配存储单元,形参变量是实参变量的副本,如果传递的是对象,还将调用拷贝构造函数,当参数传递的数据较大时,用引用比用一般变量传递的效率和所占的空间都好。
  • 使用指针作为函数的参数虽然也能达到与使用引用的效果,但是,在被掉函数中同样要给形参分配存储单元,在主调函数的调用处,必须用变量的地址作为实参。

9.在什么时候需要使用const引用

  • 如果既要利用引用提高程序的效率,又要保护传递给函数的数据不在函数中被改变,就应该使用常引用。
  • 常引用声明方式:const int& a = num;

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

  • 格式:int &func(int a){//函数体}
  • 好处:在内存中不产生被返回值的副本。
  • 注意:正是因为这点原因,所以返回一个局部变量的引用是不可取的,因为随着该局部变量生存期的结束,相应的引用也会失效,产生runtime error。
  • 注意事项:
    • 不能返回局部变量的引用。主要原因是局部变量会在函数返回后被销毁,因此被返回的引用就成为“无所指”的引用程序会进入未知状态。
    • 不能返回函数内部new分配的内存的引用。虽然不存在局部变量的被动销毁问题,可对于这种情况(返回函数内部new分配内存的引用),又面临其他尴尬局面。例如:被调函数返回的引用只是作为一个临时变量出现,而没有被赋予一个实际的变量,那么这个引用所指向的空间(由new分配)就无法释放,造成内存泄露。
    • 可以返回类成员的引用。但是最好是const。为了保护对象。主要原因是当对象的属性是与某种业务规则相关联的时候,其赋值常常与某些其他属性或者对象的状态有关,因此有必要将赋值操作封装在一个业务规则当中。如果其他对象可以获得该属性的非const引用(或指针),那么对该属性的单纯赋值就会破坏业务规则的完整性。
    • **流操作符重载返回值声明为引用的作用。
      • 流操作符<<和>>,这两个操作符常常希望被连续使用,例如:cout<<”hello”<< endl;因此这两个操作的返回值应该是一个仍然支持这两个操作符的流引用。可选的其他方案包括:返回一个流对象和返回一个流对象指针。但是对于返回一个流对象,程序必须重新(拷贝)构造一个新的对象,也就说,连续的两个<<操作符实际上是针对不同对象的,这无法让人接受。对于返回一个流指针则不能连续使用<<操作符。因此,返回一个流对象引用是唯一选择,这个唯一选择很关键,它说明了引用的重要性以及无可替代性。
      • 赋值运算符=,这个操作符和流操作符相似,也可以连续使用。例如:x = j = 10;或者(x=10)=100;赋值操作符的返回值必须是一个左值,以便可以被继续赋值。因此引用也成了这个操作符的唯一返回值选择。
      • 也许这就是c++语言引入引用这个概念的原因吧。
    • 在另外一些操作符中,却千万不能返回引用,+-*/四则运算符,不能返回引用。主要原因是这四个操作符没有side effect,因此,它们必须构造一个对象作为返回值,可选的方案包括:1返回一个对象、2返回一个局部变量的引用,3返回一个new分配的对象的引用、4返回一个静态对象的引用。根据前面提到的引用作为返回值的三个原则,第2,3两个方案都被否决了。静态对象的引用又因为((a+b)==(c+d))会永远为true而导致错误。所以可选的只剩下1返回一个对象了。

11.结构与联合有什么区别

12.程序题

13.重载和重写的区别

14.有哪几种情况只能用initialization list 而不能用assignment

15.c++是不是类型安全的

16.main函数执行以前,还会执行什么代码

17.描述内存分配方式以及它们的区别


18.分别写出bool int float 指针类型的变量a与“零”的比较语句

  • 零值 != 0
  • C++里零值的范围很大,可以是0,0.0,FALSE,空指针
  • 1、bool 与“零值”比较的 if 语句。
    • 根据布尔类型的语义,零值为“假”(记为FALSE),任何非零值都是“真”(记为TRUE)。TRUE 的值究竟是什么并没有统一的标准。例如Visual C++ 将TRUE 定义为1,而Visual Basic 则将TRUE 定义为-1。所以我们不可以将布尔变量直接与TRUE、FALSE 或者1、0 进行比较。
if(flag)if(!flag)//以下写法均属不良风格if(flag == TRUE)if(flag == 1)if(flag == FALSE)if(flag == 0)
  • 2、int型变量n与零值比较的if语句
if(n == 0)if(n != 0)//以下写法均属不良风格if(n)if(!n)
  • 3、 float x 与“零值”比较的 if 语句。
    • 千万要留意,无论是float还是duoble类型的变量,都有精度限制,都不可以用“==”或“!=”与任何数字比较,应该设法转化成“>=”或”<=”形式。
if(x == 0.0)//隐含错误的比较//转化为if((x>=iEPSINON)&&(x<=EPSIONO))//其中EPSINON是允许的误差(即精度)//标准做法const float EPSINON = 0.00001;//精度根据情况而定if((x>=-EPSINON)&&(x<=EPSINON))//在这个范围内,类似于极限//以下错误的学法if(x == 0.0)if(x != 0.0)
  • 4、指针变量与“零值”比较的 if 语句。
//标准做法if(p==NULL)if(p!=NULL)//如下写法均属不良风格if(p == 0)if(p != 0)if(p)if(!=p)
  • 以上的不良风格很多都能通过编译,但是语句并不能很好的表达与零值进行比较的逻辑依据。
  • 附:关于浮点型变量
    • 浮点型变量并不精确,所以不可将float变量用“==”或“!=”与数字比较,应该设法转化成“>=”或“<=”形式。如果写成if (x == 0.0),则判为错,得0分。以下给出详细原因。
    • 浮点数在内存中的存贮机制和整型数不同,有舍入误差,在计算机中用以近似表示任意某个实数。具体的说,这个实数由一个整数或定点数(即尾数)乘以某个基数(计算机中通常是2)的整数次幂得到,这种表示方法类似于基数为10的科学记数法。所以浮点数在运算过成功运算通常伴随着因为无法精确表示而进行的近似或舍入。但是这种设计的好处是可以在固定的长度上存储更大范围的数。例如,一个指数范围为±4的4位十进制浮点数可以用来表示43210,4.321或0.0004321,但是没有足够的精度来表示432.123和43212.3(必须近似为432.1和43210)。当然,实际使用的位数通常远大于4。所以浮点数不能够判断相等,像 if(x==0)的这样的编码是不总是正确的,我们在判断浮点数相等时,推荐用范围来确定,若x在某一范围内,我们就认为相等,至于范围怎么定义,要看实际情况而已了,float,和double 各有不同。

19.请说出const与#define相比,有何优点

20.简述数组与指针的区别


21.int(*s[10])(int)表示的是什么

  • int(*s[10])(int)叫函数指针数组,每个指针指向一个int func(int param)的函数。

22.栈内存与文字常量区

23.将程序跳转到指定内存地址要对绝对地址0x100000赋值,我们可以用(unsigned int*)0x100000 = 1234;那么要是想让程序跳转到绝对地址是0x100000去执行,应该怎么做?

24.int id[sizeof(unsigned long)];这个对吗,为什么

25.引用与指针有什么区别

26.const和#define比较,const有什么优点

27.复杂声明

28.内存的分配方式有几种

29.基类的析构函数不是虚析构,会带来什么问题

30.全局变量和局部变量有什么区别,是怎么实现的,操作系统和编译器是怎么知道的

原创粉丝点击