今天我整合了2021年100道大厂高频C++基础面试题,里面包含了C++很多基础知识点,干货满满。因内容较多,篇幅较长,所以会分成上下两篇讲解,强烈建议小伙伴们收藏!
下面我们一起来测验下,大家一起看看能闯多少关?
main函数执行之后:
voidtest(int*p){inta=1;p=&a;cout<
可以实现用父类型别的指针指向其子类的实例,然后通过父类的指针调用实际子类的成员函数。
有了虚函数,基类指针指向基类对象时就使用基类的成员(包括成员函数和成员变量),指向派生类对象时就使用派生类的成员,从而实现多态。
注意:构造函数不能为虚函数,但是析构函数可以为虚函数,并且虚析构函数可以防止父类指针销毁子类对象时不正常导致的内存泄漏。
相同点:
不同点:
const:
//i可以是int型或者constint型voidfun(constint&i){//...}static:
顶层const:指的是const修饰的变量本身是一个常量,无法修改,指的是指针,就是*号的右边。
底层const:指的是const修饰的变量所指向的对象是一个常量,指的是所指变量,就是*号的左边。
使用命名的强制类型转换函数const_cast时,只能改变运算对象的底层const。
constinta;intconsta;constint*a;int*consta;15、简单介绍内存池?内存池是一种内存分配方式。通常我们习惯直接使用new、malloc申请内存。
这样做的缺点在于所申请内存块的大小不定,当频繁使用时会造成大量的内存碎片并进而降低性能。
内存池则是在真正使用内存之前,预先申请分配一定数量、大小相等(一般情况下)的内存块留作备用。
当有新的内存需求时,就从内存池中分出一部分内存块,若内存块不够再继续申请新的内存。这样做的一个显著优点是,使得内存分配效率得到提升。
intmain(intargc,charconst*argv[]){constchar*str="name";sizeof(str);//取的是指针str的长度,是8strlen(str);//取的是这个字符串的长度,不包含结尾的\0。大小是4return0;}19、简单描述内存泄漏?内存泄漏一般是指堆内存的泄漏,也就是程序在运行过程中动态申请的内存空间不再使用后没有及时释放,导致那块内存不能被再次使用。
假设数组inta[10];int(*p)[10]=&a;23、虚函数与纯虚函数的区别?24、数组名和指针(这里为指向数组首元素的指针)区别?数组名不是真正意义上的指针,可以理解为常指针,所以数组名没有自增、自减等操作。
当数组名当做形参传递给调用函数后,就失去了原有特性,退化成一般指针,多了自增、自减操作,但sizeof运算符不能再得到原数组的大小了。
宏定义在预处理的时候进行简单的字符串替换,而内联函数在编译时在每个调用内联函数的地方将函数展开,这样不用使内联函数占用栈空间,提高效率。
宏定义没有类型检查,但是内联函数还是具有函数的性质,有参数以及返回值。
都是是指向无效内存区域(这里的无效指的是"不安全不可控")的指针,访问行为将会导致未定义行为。
野指针,指的是没有被初始化过的指针。
intmain(void){int*p;std::cout<<*p< 悬空指针,指针最初指向的内存已经被释放了的一种指针。 intmain(void){int*p=nullptr;int*p2=newint;p=p2;deletep2;}此时p和p2就是悬空指针,指向的内存已经被释放。继续使用这两个指针,行为不可预料。需要设置为p=p2=nullptr。此时再使用,编译器会直接保错。 避免野指针比较简单,但悬空指针比较麻烦。c++引入了智能指针,C++智能指针的本质就是避免悬空指针的产生。 产生原因及解决办法: 野指针:指针变量未及时初始化=>定义指针变量及时初始化,要么置空。 悬空指针:指针free或delete之后没有及时置空=>释放操作后立即置空。 final: 当不希望某个类被继承,或不希望某个虚函数被重写,可以在类名和虚函数后添加final关键字,添加final关键字后被继承或重写,编译器会报错。例子如下: classBase{virtualvoidfoo();};classA:publicBase{voidfoo()final;//foo被override并且是最后一个override,在其子类中不可以重写};classBfinal:A//指明B是不可以被继承的{voidfoo()override;//Error:在A中已经被final了};classC:B//Error:Bisfinal{};override: 当在父类中使用了虚函数时候,你可能需要在某个子类中对这个虚函数进行重写,以下方法都可以: classA{virtualvoidfoo();}classB:publicA{voidfoo();//OKvirtualvoidfoo();//OKvoidfoo()override;//OK}如果不使用override,当你手一抖,将foo()写成了foo()会怎么样呢? 结果是编译器并不会报错,因为它并不知道你的目的是重写虚函数,而是把它当成了新的函数。 如果这个虚函数很重要的话,那就会对整个程序不利。 所以,override的作用就出来了,它指定了子类的这个虚函数是重写的父类的,如果你名字不小心打错了的话,编译器是不会编译通过的: 再free一次时,由于堆中的内容已经是无效的东西,所以就会出错。 不过,有的编译器在free时并没有清理堆中的内存,有时你对它free两次也不一定出错。不过这是一个很大的隐患,在实际写代码中千万要注意避开这点。 浅拷贝: 深拷贝: 无参构造函数,析构函数,拷贝构造函数,重载赋值运算符函数。 volatile关键字告诉编译器该关键字修饰的变量是随时可能发生变化的。 每次使用它的时候必须从内存中取出它的值,因而编译器生成的汇编代码会重新从它的地址处读取数据放在左值中。 如果该变量是一个寄存器变量或者表示一个端口数据或者是多个线程的共享数据,就容易出错,所以说volatile可以保证对特殊地址的稳定访问。 由于类的多态性,基类指针可以指向派生类的对象,如果删除该基类的指针,就会调用该指针指向的派生类析构函数,而派生类的析构函数又自动调用基类的析构函数,这样整个派生类的对象完全被释放。 #include 重载是指在同一范围定义中的同名成员函数才存在重载关系。 主要特点是函数名相同,参数类型和数目有所不同,不能出现参数个数和类型均相同,仅仅依靠返回值不同来区分的函数,重载和函数成员是否是虚函数无关。 classA{...virtualintfun();voidfun(int);voidfun(double,double);staticintfun(char);...}重写(覆盖)(override):重写指的是在派生类中覆盖基类中的同名函数,重写就是重写函数体,要求基类函数必须是虚函数且: //父类classA{public:virtualintfun(inta){}}//子类classB:publicA{public://重写,一般加override可以确保是重写父类的函数virtualintfun(inta)override{}}重载与重写的区别: 隐藏(hide):隐藏指的是某些情况下,派生类中的函数屏蔽了基类中的同名函数,包括以下情况: //父类classA{public:voidfun(inta){cout<<"A中的fun函数"< #include