面试题之C++(一)

从今天开始每天一道面试题持续更新:

1.指针和引用

  1. 引用总是指向一个对象,没有空引用,而指针可以指向一个对象也可以指向空(nullptr)
  2. 指针可以被重新赋值,而引用不行,引用永远指向初始化的值
  3. 指针指向一块内存,指针的内容是内存的地址;而引用是某块内存的别名(指针是一个实体,而引用是别名)
  4. 引用没有const,而指针有const,const指针不可以改变
  5. sizeof(引用)得到的是引用所绑定的对象的大小,而sizeof(指针)得到的是指针本身的大小
  6. 指针和引用的自增(++)运算意义不一样;指针是指向下一块内存的地址,引用是调用对象本身的自加运算符
  7. 引用比指针多了类型检查,所以引用是类型安全的,而指针不是
  8. 想要获得指针指向的对象必须用*操作符对指针解引用,而引用不需要

总结:从概念上讲。指针从本质上讲就是存放变量地址的一个变量,在逻辑上是独立的,它可以被改变,包括其所指向的地址的改变和其指向的地址中所存放的数据的改变。而引用是一个别名,它在逻辑上不是独立的,它的存在具有依附性,所以引用必须在一开始就被初始化。

指针传递参数本质上是值传递的方式,它所传递的是一个地址值。引用传递过程中,被调函数的形式参数虽然也作为局部变量在栈中开辟了内存空间,但是这时存放的是由主调函数放进来的实参变量的地址。被调函数对形参的任何操作都被处理成间接寻址,即通过栈中存放的地址访问主调函数中的实参变量。正因为如此,被调函数对形参做的任何操作都影响了主调函数中的实参变量。

程序在编译时分别将指针和引用添加到符号表上,符号表上记录的是变量名及变量所对应地址。指针变量在符号表上对应的地址值为指针变量的地址值,而引用在符号表上对应的地址值为引用对象的地址值。

2.堆和栈的区别

一个由C/C++编译的程序在内存中占几个部分:
1.栈区:由编译器自动分配释放,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。
2.堆区:一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表。
3.全局区(静态区):全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。程序结束后由系统释放。
4.文字常量区:常量字符串就是放在这里的,程序结束后由系统释放。
5.程序代码区:存放函数体的二进制代码。
区别:
1.栈由系统自动分配,堆由程序员自己申请,并指明大小
2.栈:只要栈的剩余空间大于所申请空间,系统将为程序提供内存,否则将报异常提示栈溢
出。
堆:首先应该知道操作系统有一个记录空闲内存地址的链表,当系统收到程序的申请时,
会遍历该链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表
中删除,并将该结点的空间分配给程序,另外,对于大多数系统,会在这块内存空间中的
首地址处记录本次分配的大小,这样,代码中的delete语句才能正确的释放本内存空间。
另外,由于找到的堆结点的大小不一定正好等于申请的大小,系统会自动的将多余的那部
分重新放入空闲链表中。
3.栈向低地址扩展,栈顶的地址和栈的最大容量是系统预先规定好的。栈的空间小。
堆向高地址扩展,是不连续的内存区域,堆的大小受限于计算机系统中有效的虚拟内存。
4.栈的速度快,堆的速度慢(申请效率),堆容易产生内存碎片。堆的空间大。
5. 栈:在函数调用时,第一个进栈的是主函数中后的下一条指令(函数调用语句的下一条可
执行语句)的地址,然后是函数的各个参数,在大多数的C编译器中,参数是由右往左入栈
的,然后是函数中的局部变量。注意静态变量是不入栈的。
堆:一般是在堆的头部用一个字节存放堆的大小。堆中的具体内容由程序员安排。
6. 存取速率,栈比堆块,但是栈中的值可以运行时赋值,而堆中的值是编译时确定。
总结:栈由系统静态分配,速度快,一般用在自动存函数的参数之类的东西,当然栈也可以用alloc动态分配,但不建议。堆由程序员动态分配,由程序员管理,用起来自由方便,不过会比较慢,易产生内存碎片。

3.new和delete是如何实现的,new和malloc的异同

new的实现分为三步:

1.自动计算所需空间,调用operator new函数进行动态内存分配
2.调用构造函数,初始化对象
3.返回正确的指针
delete的实现也分为三步
1.定位到指针所指向的内存空间,然后根据其类型,调用其自带的析构函数
2.然后释放其内存空间(将这块内存空间标志为可用,然后还给操作系统)
3.将指针标记为无效
注意:new的对象用完后必须用delete释放
new和malloc:

1.new/delete是C++关键字,需要编译器支持。malloc/free是库函数,需要头文件支持
2.new操作符申请内存分配时无须指定内存块的大小,malloc则需要显式地指出所需内存的尺寸
3.new操作符内存分配成功时,返回的是对象类型的指针,类型严格与对象匹配,无须进行类型转换,故new是符合类型安全性的操作符。而malloc内存分配成功则是返回void * ,需要通过强制类型转换将void*指针转换成我们需要的类型
4.new内存分配失败时,会抛出bac_alloc异常。malloc分配内存失败时返回NULL
5.new会先调用operator new函数,申请足够的内存(通常底层使用malloc实现)。然后调用类型的构造函数,初始化成员变量,最后返回自定义类型指针。delete先调用析构函数,然后调用operator delete函数释放内存(通常底层使用free实现)。malloc/free是库函数,只能动态的申请和释放内存,无法强制要求其做自定义类型对象构造和析构工作。
6.允许重载new/delete操作符,malloc不允许重载
7.new操作符从自由存储区(free store)上为对象动态分配内存空间,而malloc函数从堆上动态分配内存。

4.c和c++的区别

1.c++是c的超集,理论上所有的c程序都可以由c++编译器编译
2.c是面向过程的语言而c++是面向对象的语言
3.c++支持类并对c的struct做了扩展
4.C++支持函数重载,而C不支持函数重载
5.C++中有引用,而C没有
6.有C++全部变量的默认链接属性是外链接,而C是内连接
7.C 中用const修饰的变量不可以用在定义数组时的大小,但是C++用const修饰的变量可以
8.c++增加了c不具有的关键字
9. C程序员可以省略函数原型,而C++不可以,一个不带参数的C函数原型必须把void写出来。C++可以使用空参数列表。
10. C++中new和delete是对内存分配的运算符,取代了C中的malloc和free
11. c++提供了字符串类
12. c++用iostream做标准输入输出 等等

5.c++,java的联系与区别

语言特性:
1.C++允许名字空间级别的常量,变量和函数. 而所有这样的 Java 声明必须在一个类或者接口当中.
2.在 C++ 的声明中,一个类名可以用来声明一个此类对象的值,在 Java 的声明中,一个类名声明的是对此类的一个对象的引用. 而在 C++ 里与之等价的做法是用 “*” 来声明一个指针
3.在 C++ 里,声明一个指向常量的指针(只读指针)是可能的,在 Java 里这是不可能做到的.
4.C++保留了指针,而java的虚拟机为我们把这些东西都封装好了
5.c++支持多重继承,Java不支持多重继承,但允许一个类继承多个接口
6.我们在c++中可以直接手动的释放内存,但是java做不到,它的垃圾是有虚拟机去回收的
7.Java不支持操作符重载,c++支持
8.Java不支持预处理功能。c/c十十在编译过程中都有一个预编泽阶段,单java的import与预处理类似
9.Java不支持缺省函数参数,而c十十支持
10.c和c十十中有时出现数据类型的隐含转换,这就涉及了自动强制类型转换问题,Java不支持c十十中的自动强制类型转换,如果需要,必须由程序显式进行强制类型转换。
应用场景:
java:企业级应用开发,网站平台开发,移动领域等
C++:游戏领域,办公软件,搜索引擎,关系型数据库等
垃圾回收
c++的垃圾回收必须有程序员来完成,c++可以使用智能指针来管理内存(引用计数法)
java可以进行自动的垃圾回收,java的垃圾回收机制是Java虚拟机(JVM)垃圾回收器提供的一种用于在空闲时间不定时回收无任何对象引用的对象占据的内存空间的一种机制。

6.struct和class的区别

1.默认的访问权限:struct是public,class是private
2.“class”这个关键字还用于定义模板参数,就像“typename”。但关键字“struct”不用于定义模板参数

7.define和const的区别

1.编译处理的方式不同,define宏在预处理阶段展开,const在编译运行阶段使用
2.类型和安全检查不同,define宏没有类型,不做任何类型检查,仅仅是展开;const常量有具体的类型,在编译阶段会进行类型检查
3.存储方式不同,define宏仅仅展开,有多少地方使用,就展开多少次,不会分配内存。(宏定义不分配内存,变量定义分配);const常量会在内存中分配内存
4.const可以节省空间,原因在于const定义的常量只是给出了对应的内存地址,(因为是全局的只读变量,存在静态区),在整个程序运行期间只有一份拷贝,而define给出的是立即数,可能有多处替换
5.const可以调试,define不能调试

8.const和static的用法

static:
1.经过static修饰的变量会作为类的属性而不是实体属性而存在
2.static修饰的变量作为程序运行时的静态变量,存于内存的静态区,静态区数据的初始化在,操作系统加载完程序后执行main函数之前。
3.static修饰的变量通过int ClassName::value=1这种方式初始化,不再需要static关键字
4.static修饰局部变量,使得局部变量只初始化一次,且只有一份内存;static并不该变局部变量的作用域,仅仅改变局部变量的生命周期(程序结束,该变量才销毁)
5.类的static函数,只能访问该类的static变量或static函数,被static修饰的函数没有this指针,static函数不能申明为虚函数
6.static必须在类的外部定义,不能在类内定义
7.在cpp文件的全局范围内声明static变量,则该变量在该cpp内有效,其他cpp不能访问;如果在头文件中声明static变量,则包含该头文件的cpp文件各自有自己独立的同名变量。
8.函数的实现不需要static修饰,可是如果希望该函数不能被其他cpp文件访问,函数的声明需要加static,不加的话其他cpp可能可以也可能不可以访问这个函数。
const:
1.const定义的是一个常量,保护被修饰的量防止意外意外的修改
2.const可以很方便的进行参数的调整和修改,起到和宏一样的作用
3.const可以节省空间,避免不必要的内存分配
4.const可以提高效率,编译器通常不为普通const常量分配存储空间,而是将它保存在符号表中,使得它成为一个编译期间的常量(const定义时不分配空间,第一次使用时才分配且只分配一次)
用法:
1. int const i = 0; const int i = 0 本质上指const修饰类型为int的变量i的值是不可变的
2. char* const p 指针本身是常量不可变;const char* p 指针所指向的内容是常量不可变;const char* const p 俩者均为常量,均不可变。
3. void fun(const class& Var) 向函数传递const引用,传递到函数的是Var的地址,但是不能对地址的内容修改,它的效率比值传递高,同时防止修改。
4. const修饰类内的成员变量表示成员常量,不能修改,且只能在初始化列表中赋值,class A { const int i};
5. const 修饰类的成员函数,则该函数不能修改类的对象的任何数据成员,一般写在函数末尾;const成员函数能够访问对象的const成员,而其他成员函数不可以。class A { void fun() const };
6. const修饰类的对象表示该对象为常量对象,该对象的任何成员不能被修改,任何非const成员函数不能被调用。

9.static和const在类中使用的注意事项

static:
1.static定义的变量,属于某个类,而不属于类的对象;其值的修改对所有类的对象可见。
2.static数据成员必须在类的外部定义和初始化,定义时不能标示为static,除非是const static成员,它可以在类的定义体中初始化,且必须在定义时初始化。
3.static成员函数不与任意对象相关联,因此没有this指针,也无法访问类的非静态成员
4.static成员函数不能被声明为const,virtual,volatile。
const:
1.this指针是const的
2.const修饰的成员函数,this指针指向的是一个const对象,因此const成员函数不能修改调用该函数的对象
3.非const对象,可以调用const成员函数也可以调用非const成员函数;const对象只能调用const成员函数。
4.const 数据成员必须在构造函数的成员初始化列表中初始化,const static成员除外

10.c++中的const类成员函数

1.只有const类成员函数才能被const类的对象调用
2.const类成员函数不允许修改类的数据成员
3.如果数据成员是指针,const可以保证指针的指向不会改变,但不保证指针所指向对象的内容不改变
4.const成员函数可以被具有相同参数列表的非const成员函数重载,在这种情况下,类对象的常量性决定调用哪一个函数
5.好的编程风格应该将所有不能修改数据成员的函数定义为const成员函数。

猜你喜欢

转载自blog.csdn.net/wzc2608/article/details/79949877