C++面试基础知识汇总
本篇文章用于C++面试基础知识复习,不断更新。自用。
文章目录
- C++面试基础知识汇总
-
- 1.new/delete/malloc/free关系
- 2.delete与delete[]的区别
- 3.extern关键字作用
- 4.static关键字作用
- 5.volatile
- 6.const
- *7.C++多态性与虚函数表*
- 8.指针和引用的区别
- 9.指针与数组
- 10.智能指针
- 11.C++四种类型转换
- 12.结构体内存对齐
- 13.右值引用
- 14.类管理内存的方式
- 15.可重入函数与不可重入函数
- 16.内联函数、宏定义
- 17.C++程序五大内存分区:
- 18.C++11线程的几种锁
- 19.乐观锁、悲观锁
- 20.堆与栈
- 21.C++内存管理
- 22.struct和class的区别
- 23.成员初始化列表
- 24.C++的调用惯例(函数调用)
- 25.可执行文件的编译过程
- 26.set,map和vector的插入复杂度
- 27.free和delete释放后的内存系统会马上回收吗?
- 28.C++11新特性
- 数据结构部分
- 操作系统部分
1.new/delete/malloc/free关系
new/delete属于C++语言中的运算符,malloc/free属于C语言中的标准库函数;
它们都可用于申请动态内存和释放内存;
对于非内部数据类型的对象而言,malloc/free无法满足动态对象的要求,对象消亡时,delete可以执行对象的析构函数,malloc只会释放内存。new会调用构造函数;
new按照数据类型进行内存分配,malloc按照大小分配内存;
new返回的是指定对象的指针,而malloc返回的是void*,因此malloc的返回值一般都需要类型转化;
new是一个操作符可以重载,malloc是一个库函数;
new失败了会跑出bad_malloc异常,malloc返回NULL;
new[]一次分配所有内存,多次调用构造函数。
2.delete与delete[]的区别
delete只会调用一次析构函数,而delete[]会调用每一个成员的析构函数。不能互用,new[]就要对应delete[]。
3.extern关键字作用
基本解释:extern可以置于变量或者函数前,用以标示变量或函数定义在别的文件中,提示编译器在遇到此变量或函数时在其他模块中寻找其定义。此外extern也可以用来进行链接指定。
作用一:当与“C"一起用时,标示函数应该用C的规则去编译;
作用二:声明函数或全局变量的作用范围的关键字,其声明的函数或全局变量可以在本模块或其他模块中使用,是声明而非定义,需要注意一致性。
4.static关键字作用
1 static修饰局部变量
修饰局部变量时,使得被修饰的变量成为静态变量,存储在静态区、存储在静态区的数据生命周期与程序相同,在main函数之前初始化,在程序退出时销毁。生命周期不代表离开函数之后还可以访问。
2 static修饰全局变量
全局变量本来就存储在静态区,因此static并不能改变其存储位置,但是,static限制了其链接属性,被static修饰的全局变量只能被包含该定义的文件访问。非静态全局变量作用域为整个源程序,静态全局变量作用域为源文件。
3 static修饰函数
static修饰函数使得函数只能在包含该函数定义的文件中被调用。
对于静态函数,声明和定义需要放在同一个文件夹中。
如果将static函数定义在头文件中,则每一个包含该头文件的文件都实现了一个func函数,因此static实现了不同文件中定义同名的函数而不发生冲突。
4 static修饰成员变量和成员函数
在C++中修饰类对象成员和成员函数时,所有的对象都只维持同一个实例,实现不同对象之间的数据共享。即该变量或函数属于一个类而不是某一个特定对象。
static数据成员的初始化在类外进行,而且不加static,以免与一般静态变量或对象混淆;初始化时不加成员访问控制权限符;静态存储必须初始化。
static成员函数仅能访问静态数据成员,不能访问非静态数据成员,也不能访问非静态成员函数,这是由于静态成员函数没有this指针。
5.volatile
1.访问寄存器要比访问内存快,因此CPU会优先访问该数据在寄存器中的存储结果,但是内存中的数据可能已经发生了改变,而寄存器中还保留着原来的结果,为了避免这种情况的发生,将该变量声明为volatile,告诉CPU每次都从内存中读取数据。
2 与const关键字类似,volatile也可以修饰指针和变量。
attention:可以把一个非volatile int赋给volatile int,但是不能把非volatile对象赋给一个volatile对象;
出了基本类型外,对用户定义类型也可以用volatile类型进行修饰;
3 多线程下的volatile
当两个线程都要用到某一个变量且该变量的值会被改变时,应该用volatile声明,防止优化编译器把变量从内存装入寄存器中。
6.const
作用:
1)防止一个变量被改变,定义时需要初始化。
2)对指针而言,可以指定指针本身为const,也可以指定指针所指的数据为const。
3)在一个函数声明中,const可以修饰形参表明他是一个输入参数,在函数内部不可以改变其值。
4)对于类的成员函数,表明其是一个常函数,不能修改类的成员变量。
·const修饰指针:
·如果const位于*左侧,则用来修饰指针指向的变量,即指向常量。
·如果const位于/*右侧,则用来修饰指针本身,即指针本身不可变。
指针常量:不能通过指针来修改变量的值;
常量指针:一直指向该变量,不能给该指针赋予其他地址。
·函数中使用const
- const修饰函数参数
表示参数不可变
若参数为引用,可以增加效率:
const引用传递和函数按值传递的效果一样,但是传地址省却copy过程
·const
类中使用const
修饰成员变量:表示成员变量不能被修改,同时只能在初始化列表中赋值。
修饰成员函数:该函数不能改变对象的成员变量;不能调用非const成员函数,const类对象只能调用const成员函数;不能与static关键字同时使用,因为static关键字修饰静态成员函数时,静态成员函数不含有this指针,即不能实例化,但是const成员函数要求必须实例化。
7.C++多态性与虚函数表
1.C++多态的实现
概念:在面向对象语言中,接口的多种不同的实现方式–允许将父对象设置成为一个或更多的他的子对象的相等的技术。
多态分为静态多态和动态多态,静态多态是通过重载和模板技术实现,在编译的时候确定。动态多态是通过虚函数和继承实现,执行动态绑定,在运行的时候确定。
动态多态实现条件:
1)虚函数;
2)一个基类的指针或引用指向派生类的对象;
基类指针在调用成员函数(虚函数)时,就会去查找该对象的虚函数表,虚函数表的地址在每个对象的首地址,C++内部为每一个类维持一个虚函数表,该类的对象都指向这一个虚函数表。
虚函数的作用:
主要是为了能够实现多态而设计的,让成员函数操作一般化,用基类的指针指向不同的派生类的对象时,基类指针可以调用其各自的虚成员函数。
什么是动态多态:
对于相关的对象类型,确定它们之间的一个共同功能集,然后在基类中把这些共同的功能声明为多个公共的虚函数接口,各个子类重写这些虚函数,客户端的代码通过指向基类的引用或指针来操作这些对象,对虚函数的调用会自动绑定到实际提供的子类对象上去。
静态多态:
对于相关的对象类型,直接实现它们各自的定义,不需要有共有基类,只需要各个具体类的实现中要求相同的接口声明,这里的接口称之为隐性接口,客户端把操作这些对象的函数定义为模板,当需要操作什么类型的对象时,直接对模板指定该类型实参即可。
什么是纯虚函数?
在基类中不能对虚函数给出有意义的实现,它的实现留给该基类的派生类去做,纯虚函数也可以叫抽象函数,一般来说没有函数体,用=0作为此函数的标志。
纯虚函数如何定义,为什么对于存在虚函数的勒种析构函数要定义成虚函数?
为了实现多态进行动态绑定,将派生类对象绑定到基类指针上,对象销毁时,如果析构函数没有定义为虚函数,则调用时会调用基类析构函数,只能销毁部分数据。
虚继承
如果b,c是a的子类,d继承b,c,那么d中a会被构造2次,造成数据冗余,使用虚就成a只会被构造一次。
//纯虚函数定义
virtual ~A() = 0;
析构函数能抛出异常吗?
不能,析构函数已经变成了异常处理的一部分。
不要在构造函数和析构函数中调用虚函数
- 构造函数完成之前,被构造的独享被认为“未完全生成”,当创建某个派生类的对象时,如果在它的基类的构造函数中调用虚函数,那么此时派生类的构造函数并未执行,所调用的函数可能还有没被初始化的成员,将引发问题。
- 一个类的构造函数在执行时,并不能保证该函数所能访问到的虚指针就是当前被构造对象最后所拥有的虚指针,因为后面派生类的构造函数会对当前被构造对象的虚指针进行重写,因此无法完成动态联编。
8.指针和引用的区别
1.指针保存的是所指对象