C/C++ 面试知识点2
7.浅拷贝和深拷贝的原理
浅拷贝(位拷贝)就是两/多个对象共享一块内存,其缺点就是当析构一个对象的时候,另一个对象也不存在了,如果再使用它就会发生错误。
同一块资源释放多次,崩溃或者内存泄漏
深拷贝就是完完全全的复制出一个对象,两者在内存上无任何关系。
必须显式提供拷贝构造函数和赋值运算符
缺省拷贝构造函数在拷贝过程中是按字节复制的,对于指针型成员变量只复制指针本身,而不复制指针所指向的目标--浅拷贝。
8.常见的不能声明为虚函数的有哪些?
普通函数(非成员函数)
静态成员函数
内联成员函数
构造函数
友元函数
9.C++的静态多态和动态多态
静态多态:运行前确定类型,采用的是函数重载和泛型
动态多态:运行后确定类型或者函数调用,采用虚函数实现
10.C++虚函数的原理
11.C++虚函数占用类的大小
因为只需要维护一个指向虚表的指针,所以大小为4或8个字节
静态成员和函数不计入sizeof中。
12.new与malloc的区别
- 1. 申请的内存所在位置 :new操作符从自由存储区(free store)上为对象动态分配内存空间,而malloc函数从堆上动态分配内存。
- 自由存储区是C++基于new操作符的一个抽象概念,凡是通过new操作符进行内存申请,该内存即为自由存储区
- 自由存储区是否能够是堆(问题等价于new是否能在堆上动态分配内存),这取决于operator new 的实现细节。
- 自由存储区不仅可以是堆,还可以是静态存储区,这都看operator new在哪里为对象分配内存
- malloc函数从堆上动态分配内存, 堆是操作系统中的术语,是操作系统所维护的一块特殊内存,
- 用于程序的内存动态分配,C语言使用malloc从堆上分配内存,使用free释放已分配的对应内存。
- 2.返回类型安全性
- new操作符内存分配成功时,返回的是对象类型的指针,类型严格与对象匹配,无须进行类型转换
- malloc内存分配成功则是返回void * ,需要通过强制类型转换将void*指针转换成我们需要的类型。
- 3.内存分配失败时的返回值
- new内存分配失败时,会抛出bac_alloc异常,它不会返回NULL;malloc分配内存失败时返回NULL。
- 4.是否需要指定内存大小
- 使用new操作符申请内存分配时无须指定内存块的大小,编译器会根据类型信息自行计算,而malloc则需要显式地指出所需内存的尺寸。
- 5.是否调用构造函数/析构函数
- 使用new操作符来分配对象内存时会经历三个步骤:
- 调用operator new 函数,分配一块足够大的,原始的,未命名的内存空间以便存储特定类型的对象。
- 编译器运行相应的构造函数以构造对象,并为其传入初值
- 对象构造完成后,返回一个指向该对象的指针。
-
使用delete操作符来释放对象内存时会经历两个步骤:
- 第一步:调用对象的析构函数。
- 第二步:编译器调用operator delete(或operator delete[])函数释放内存空间。
总之来说,new/delete会调用对象的构造函数/析构函数以完成对象的构造/析构。而malloc则不会
- 6.对数组的处理
- 使用new[]分配的内存必须使用delete[]进行释放:释放对象时为每个对象调用析构函数。注意delete[]要与new[]配套使用,不然会找出数组对象部分释放的现象,造成内存泄漏。
- 7.new与malloc是否可以相互调用
- 不可以
- 8.是否可以被重载
- operator new /delete 可以
- 9. 能够直观地重新分配内存
- 10. 客户处理内存分配不足
13.C++中有哪几种数据存储区?
栈、堆、自由存储区、全局/静态存储区、常量存储区
14.什么是栈溢出?哪些情况下比较容易出现栈溢出?
栈溢出泛指系统维护的栈溢出,因数据压不下去了,导致溢出,此时程序会崩溃。
一般递归深度过大、创建普通数组过大(就是局部变量占用的空间大于栈了就会溢出,new的是堆,不算)。
15.“#include”后面跟引号与尖括号的区别?
引号编译器会搜索当前工作目录下的头文件,尖括号会搜索安装目录下的头文件
16.gcc和g++的区别
gcc和g++都是GNU(组织)的一个编译器
编译阶段,g++会调用gcc, 但是gcc命令不能自动和C++程序使用的库链接,所以为了统一,干脆编译/链接统统用g++了
17.类成员函数的重载、覆盖和重写区别
重载和普通的函数重载一样。
覆盖则基类的函数要加virtual (这就是多态的实现)
如果派生类的函数与基类的函数同名,但是参数不同。此时,不论有无virtual关键字,基类的函数将被隐藏,子类就重写了这个函数
如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有virtual关键字。此时,基类的函数被隐藏,子类就重写了这个函数
18.构造函数为什么不能是虚函数
虚函数存在于虚表中,而虚表要靠虚表指针维护,而只有实例化后的对象才有虚表指针,
而实例化对象就必须调用构造函数初始化对象,所以冲突了,结果就是构造函数不能是虚函数。
19. printf(“%d,%d\n”,i++,i++),若i=0,则结果输出什么。
这里有一个点就是,printf和cout输出的时候都是从右至左读取值,所以结果应该是1,0。