Fork me on GitHub
打赏

C++笔试面试题整理

朋友给出的一些常见的C++面试题,特整理如下,后期遇到新的再更新。

面试题

列举并解释C++中的四种运算符转化,说明它们的不同点:

  • static_cast: 在功能上基本上与C风格的类型转换一样强大,含义也一样。它也有功能上限制。例如,你不能用static_cast象用C风格的类型转换一样把struct转换成int类型或者把double类型转换成指针类型,另外,static_cast不能从表达式中去除const属性,因为另一个新的类型转换操作符const_cast有这样的功能。
  • const_cast: 用于类型转换掉表达式的constvolatileness属性。通过使用const_cast,你向人们和编译器强调你通过类型转换想做的只是改变一些东西的constness或者 volatileness属性。这个含义被编译器所约束。如果你试图使用const_cast来完成修改constness 或者volatileness属性之外的事情,你的类型转换将被拒绝。
  • dynamic_cast: 它被用于安全地沿着类的继承关系向下进行类型转换。这就是说,你能用dynamic_cast把指向基类的指针或引用转换成指向其派生类或其兄弟类的指针或引用,而且你能知道转换是否成功。失败的转换将返回空指针(当对指针进行类型转换时)或者抛出异常(当对引用进行类型转换时)。
  • reinterpret_cast: 使用这个操作符的类型转换,其的转换结果几乎都是执行期定义(implementation-defined。因此,使用reinterpret_casts的代码很难移植。reinterpret_casts的最普通的用途就是在函数指针类型之间进行转换。

 

什么是封装?C++中是如何实现的?

封装来源于信息隐藏的设计理念,是通过特性和行为的组合来创建新数据类型让接口与具体实现相隔离

C++中是通过类来实现的,为了尽量避免某个模块的行为干扰同一系统中的其它模块,应该让模块仅仅公开必须让外界知道的接口。

 

什么是动态特性?

在绝大多数情况下,程序的功能是在编译的时候就确定下来的,我们称之为静态特性。

反之,如果程序的功能是在运行时刻才能确定下来的,则称之为动态特性。

C++中,虚函数,抽象基类,动态绑定和多态构成了出色的动态特性。

 

对象在内存中是怎么存放的?

在类对象的内存布局中,如果有虚函数,首先是该类的vtbl指针,然后才是对象数据,对象数据都是顺序存放,当然会涉及到字节对齐,这样会带来存取效率的提升

 

给你一个指针,并用new动态申请空间,在另一个函数中释放,不知道是申请的一个元素还是一个数组的情况下,怎么确定用delete还是delete []

不同的编译器有不同的实现机制,比较常用的有两种:

  1. new ,在第一个对象前面记录分配了多少对象.
  2. 使用键值,key-value
For ex:

int *p = new int[n];

//p为key,n为value.

  

C++const有什么作用?至少说明3

  • const用于定义常量:const定义的常量编译器可以对其进行数据静态类型安全检查。
  • const修饰函数形式参数:当输入参数为用户自定义类型和抽象数据类型时,将“值传递”改为“const&传递”可以提高效率。
  • const修饰函数的返回值:如果给“指针传递”的函数返回值加const,则返回值不能被直接修改,且该返回值只能被赋值给const修饰的同类型指针。
  • const修饰类的成员函数(函数定义体):任何不需要修改数据成员的函数都应该使用const修饰,这样即使不小心修改了数据成员或调用了非const成员函数,编译器也会报错。当const函数中使用到的变量被mutable修饰后,在const函数中可以对其进行修改。常函数只能调用类中的常函数,不能调用非常函数。

 

 

C语言的volatile的含义是什么。使用时会对编译器有什么暗示。

volatile的意思是易变的,也就是说,在程序运行过程中,有一些变量可能会被莫名其妙的改变,而优化器为了节约时间,有时候不会重读这个变量的真实值,而是去读在寄存器的备份,这样的话,这个变量的真实值反而被优化器给“优化”掉了,用时髦的词说就是被“和谐”了。如果使用了这个修饰词,就是通知编译器别犯懒,老老实实去重新读一遍!可能我说的太“通俗”了,那么我引用一下“大师”的标准解释: volatile的本意是“易变的”。由于访问寄存器的速度要快过RAM,所以编译器一般都会作减少存取外部RAM的优化,但有可能会读脏数据。当要求使用volatile声明的变量的值的时候,系统总是重新从它所在的内存读取数据,即使它前面的指令刚刚从该处读取过数据。而且读取的数据立刻被保存。精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。下面是volatile变量的几个例子:

1).并行设备的硬件寄存器(如:状态寄存器)

2).一个中断服务子程序中会访问到的非自动变量(Non-automatic variables)

3).多线程应用中被几个任务共享的变量嵌入式系统程序员经常同硬件、中断、RTOS等等打交道,所用这些都要求volatile变量。不懂得volatile内容将会带来灾难

 

线程同步的方式

互斥锁、条件变量和信号量

 

说下你对内存的了解?

1.-由编译器自动分配释放

2.-一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收

3.全局区(静态区),全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。-程序结束释放

4.另外还有一个专门放常量的地方。-程序结束释放

5程序代码区,存放2进制代码。

在函数体中定义的变量通常是在栈上,用malloccallocrealloc等分配内存的函数分配得到的就是在堆上。在所有函数体外定义的是全局量,加了static修饰符后不管在哪里都存放在全局区(静态区),在所有函数体外定义的static变量表示在该文件中有效,不能extern到别的文件用,在函数体内定义的static表示只在该函数体内有效。另外,函数中的"adgfdf"这样的字符串存放在常量区。

 

 

在什么时候需要使用“常引用”?

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

 

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

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

好处:在内存中不产生被返回值的副本;(注意:正是因为这点原因,所以返回一个局部变量的引用是不可取的。因为随着该局部变量生存期的结束,相应的引用也会失效,产生runtime error!

注意事项:

1)不能返回局部变量的引用。这条可以参照Effective C++[1]Item 31。主要原因是局部变量会在函数返回后被销毁,因此被返回的引用就成为了"无所指"的引用,程序会进入未知状态。

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

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

4)流操作符重载返回值申明为“引用”的作用:

流操作符<<>>,这两个操作符常常希望被连续使用,例如:cout << "hello" << endl;因此这两个操作符的返回值应该是一个仍然支持这两个操作符的流引用。可选的其它方案包括:返回一个流对象和返回一个流对象指针。但是对于返回一个流对象,程序必须重新(拷贝)构造一个新的流对象,也就是说,连续的两个<<操作符实际上是针对不同对象的!这无法让人接受。对于返回一个流指针则不能连续使用<<操作符。因此,返回一个流对象引用是惟一选择。这个唯一选择很关键,它说明了引用的重要性以及无可替代性,也许这就是C++语言中引入引用这个概念的原因吧。

赋值操作符=。这个操作符象流操作符一样,是可以连续使用的,例如:x = j = 10;或者(x=10)=100;赋值操作符的返回值必须是一个左值,以便可以被继续赋值。因此引用成了这个操作符的惟一返回值选择。

 

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

生命周期不同:

全局变量随主程序创建和创建,随主程序销毁而销毁;局部变量在局部函数内部,甚至局部循环体等内部存在,退出就不存在;

使用方式不同:通过声明后全局变量程序的各个部分都可以用到;局部变量只能在局部使用;分配在栈区。

操作系统和编译器通过内存分配的位置来知道的,全局变量分配在全局数据段并且在程序开始运行的时候被加载。局部变量则分配在堆栈里面。

与全局对象相比,使用静态数据成员有什么优势?

主要有以下所述两种优势。

静态数据成员没有进入程序的全局名字空间,因此不存在程序中其他全局名字冲突的问题。

使用静态数据成员可以隐藏信息。因为静态成员可以是Private成员,而全局对象不能。

 

有哪几种情况只能用初始化列表,而不能用赋值?

当类中含有const、引用成员变量以及基类的构造函数都需要初始化列表。

 

虚函数是怎么实现的?

简单地说,虚函数是通过虚函数表实现的。事实上,如果一个类中含有虚函数,则系统会为这个类分配一个指针成员指向一张虚函数表(vtbl),表中每一项指向一个虚函数的地址,实现上就是一个函数指针的数组。

 

什么是多态?

答:多态是指相同的操作或函数、过程可作用于多种类型的对象上并获得不同的结果。不同的对象,收到同一消息可以产生不同的结果,这种现象称为多态。

 

请用简单的语言告诉我C++是什么?

C++是在C语言的基础上开发的一种面向对象编程语言,应用广泛。C++支持多种编程范式--面向对象编程、泛型编程和过程化编程。其编程领域众广,常用于系统开发,引擎开发等应用领域,是最受广大程序员受用的最强大编程语言之一,支持类:类、封装、重载等特性!

 

newdeletemallocfree关系

delete会调用对象的析构函数,new对应free只会释放内存,

new调用构造函数。

mallocfreeC++/C语言的标准库函数,new/deleteC++的运算符。它们都可用于申请动态内存和释放内存。对于非内部数据类型的对象而言,光用maloc/free无法满足动态对象的要求。对象在创建的同时要自动执行构造函数,对象在消亡之前要自动执行析构函数。由于malloc/free是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加于malloc/free。因此C++语言需要一个能完成动态内存分配和初始化工作的运算符new,以及一个能完成清理与释放内存工作的运算符delete。注意new/delete不是库函数。

 

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

全局对象的构造函数会在main函数之前执行。

 

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

生命周期不同:

全局变量随主程序创建和创建,随主程序销毁而销毁;局部变量在局部函数内部,甚至局部循环体等内部存在,退出就不存在;

使用方式不同:通过声明后全局变量程序的各个部分都可以用到;局部变量只能在局部使用;分配在栈区。

操作系统和编译器通过内存分配的位置来知道的,全局变量分配在全局数据段并且在程序开始运行的时候被加载。局部变量则分配在堆栈里面。

 

指针和引用的区别?

1.指针是一个变量,只不过这个变量存储的是一个地址,指向内存的一个存储单元;而引用仅是个别名;

2.引用使用时无需解引用(*),指针需要解引用;

3.引用只能在定义时被初始化一次,之后不可变;指针可变;

4.引用没有 const,指针有 const

5.引用不能为空,指针可以为空;

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

7.指针和引用的自增(++)运算意义不一样;

8.指针可以有多级,但是引用只能是一级(int **p;合法而 int &&a是不合法的)

9.从内存分配上看:程序为指针变量分配内存区域,而引用不需要分配内存区域。

 

静态函数存在的意义?

静态私有成员在类外不能被访问,可通过类的静态成员函数来访问;

当类的构造函数是私有的时,不像普通类那样实例化自己,只能通过静态成员函数来调用构造函数。

 

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

virtual修饰符会被隐形继承的。

virtual可加可不加,子类覆盖它的函数不加virtual,也能实现多态。

 

函数重载是什么意思?它与虚函数的概念有什么区别?

函数重载是一个同名函数完成不同的功能,编译系统在编译阶段通过函数参数个数、参数类型不同,函数的返回值来区分该调用哪一个函数,即实现的是静态的多态性。但是记住:不能仅仅通过函数返回值不同来实现函数重载。而虚函数实现的是在基类中通过使用关键字virtual来申明一个函数为虚函数,含义就是该函数的功能可能在将来的派生类中定义或者在基类的基础之上进行扩展,系统只能在运行阶段才能动态决定该调用哪一个函数,所以实现的是动态的多态性。它体现的是一个纵向的概念,也即在基类和派生类间实现。

 

deletedelete[]区别:

delete只会调用一次析构函数,而delete[]会调用每一个成员的析构函数。

 

继承的优缺点。

1、类继承是在编译时刻静态定义的,且可直接使用,

2、类继承可以较方便地改变父类的实现。

缺点:

1、因为继承在编译时刻就定义了,所以无法在运行时刻改变从父类继承的实现

2、父类通常至少定义了子类的部分行为,父类的任何改变都可能影响子类的行为

3、如果继承下来的实现不适合解决新的问题,则父类必须重写或被其他更适合的类替换。这种依赖关系限制了灵活性并最终限制了复用性。

 

在什么时候需要使用“常引用”?

如果既要利用引用提高程序的效率,又要保护传递给函数的数据不在函数中被改变,就应使用常引用。

 

什么函数不能声明为虚函数?

构造函数(constructor

 

不能做switch()的参数类型是:

switch的参数不能为实型。

 

CMemoryState主要功能是什么

查看内存使用情况,解决内存泄露问题。

 

简述全局变量的优缺点?

全局变量也称为外部变量,它是在函数外部定义的变量,它属于一个源程序文件,它保存上一次被修改后的值,便于数据共享,但不方便管理,易引起意想不到的错误。

 

什么是指针?谈谈你对指针的理解?

指针是一个变量,该变量专门存放内存地址;

指针变量的类型取决于其指向的数据类型,在所指数据类型前加*

指针变量的特点是它可以访问所指向的内存。

 

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

1。举个反例,如果是零的话,声明一个classA[10]对象数组,而每一个对象占用的空间是零,这时就没办法区分A[0],A[1]…了。

 

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

c用宏定义,c++inline

 

描述实时系统的基本特性

在特定时间内完成特定的任务,实时性与可靠性。

 

友元关系有什么特性?

单向的,非传递的,不能继承的。

 

成员函数通过什么来区分不同对象的成员数据?为什么它能够区分?

答:通过this指针指向对象的首地址来区分的。

 

对象都具有的两方面特征是什么?分别是什么含义?

答:对象都具有的特征是:静态特征和动态特征。

静态特征是指能描述对象的一些属性(成员变量),动态特征是指对象表现出来的行为(成员函数)

 

在头文件中进行类的声明,在对应的实现文件中进行类的定义有什么意义?

这样可以提高编译效率,因为分开的话只需要编译一次生成对应的.obj文件后,再次应用该类的地方,这个类就不会被再次编译,从而大大的提高了编译效率。

 

在类外有什么办法可以访问类的非公有成员?

友元,继承,公有成员函数。

 

C++是在什么时候给变量分配的内存

函数内的局部变量是在函数体执行前分配空间。函数执行完毕后回收空间。

静态变量是在程序运行时分配空间。程序结束时回收空间。

使用new或者malloc创建的变量是在调用newmalloc函数的时候进行分配。调用deletefree函数的时候进行回收。

 

posted @ 2018-09-18 22:56  Zoctopus_Zhang  阅读(3172)  评论(0编辑  收藏  举报
// function btn_donateClick() { var DivPopup = document.getElementById('Div_popup'); var DivMasklayer = document.getElementById('div_masklayer'); DivMasklayer.style.display = 'block'; DivPopup.style.display = 'block'; var h = Div_popup.clientHeight; with (Div_popup.style) { marginTop = -h / 2 + 'px'; } } function MasklayerClick() { var masklayer = document.getElementById('div_masklayer'); var divImg = document.getElementById("Div_popup"); masklayer.style.display = "none"; divImg.style.display = "none"; } setTimeout( function () { document.getElementById('div_masklayer').onclick = MasklayerClick; document.getElementById('btn_donate').onclick = btn_donateClick; var a_gzw = document.getElementById("guanzhuwo"); a_gzw.href = "javascript:void(0);"; $("#guanzhuwo").attr("onclick","follow('33513f9f-ba13-e011-ac81-842b2b196315');"); }, 900);