C++ 八股文(简单面试题)
1.左值
可寻址变量,持久性;
2.右值
没有变量名,不可寻址,短暂性;
3.指针
指向的内存地址,指针变量存储的就是指向的对象的首地址
4.引用
为一个变量起别名,定义引用的时候一定要初始化,初始化后不能改变;
5.引用 vs 指针
①别名的地址等于原名的地址;指针变量的地址不等于原名的地址;
(39条消息) 指针的指针的地址,指针自身的地址,指针指向的地址理解_良风抚旧的博客-CSDN博客
②sizeof引用---->引用所指变量大小;sizeof指针------>本指针的大小;
③指针可多级,引用只能一级;
④指针初始化后可以改变,引用不可以改变;
int a = 996;
int *p = &a; // 初始化, p 是 a 的地址
int &r = a; // 初始化, r 是 a 的引用
int b = 885;
p = &b; // 合法, p 更改为 b 的地址
r = b; // 不合法, r 不可以再变
6.memcpy 和 strcpy 的区别
①复制的内容不同。strcpy只能复制字符串,而memcpy可以复制任意内容,例如字符数组、整型、结构体、类等。
②复制的方法不同。strcpy不需要指定长度,它遇到被复制字符的串结束符"\0"才结束,所以容易溢出。memcpy则是根据其第3个参数决定复制的长度。
③用途不同。通常在复制字符串时用strcpy,而需要复制其他类型数据时则一般用memcpy。
(30条消息) strcpy和memcpy的区别_memcpy函数和strcpy函数的区别_hustanding的博客-CSDN博客
7.内存分布情况
内存分区的意义是赋予不同的生命周期;
①全局区:包含常量区,存放全局变量、静态变量以及常量,由操作系统释放;
②代码区:存放程序的二进制代码;
③栈区:存放局部变量等,由编译器自动分配内存;
④堆区:程序员手动分配释放;
8. 堆 vs 栈
①申请方式不同:栈由系统自动分配,堆需要手动申请释放
②申请大小限制不同,栈顶和栈底是预设好的,栈是向栈底扩展,大小固定;堆是向高地址扩展,不连续的内存区域,大小灵活可调整;
③申请效率不同,堆操作由函数库提供,分配内存需要算法寻找合适大小的内存,比较慢;操作系统再底层有对栈的支持,所以栈的效率较高较快;
9.重载 vs 重写 vs 重定义
①重载是再同一个作用域下,函数名称相同,参数类型/个数/顺序有所不同(返回值类型不同不属于),属于静态多态;
②重写是发生再子类中覆盖父类中的同名函数,函数特征相同,具体实现不同;被重写的函数必须是virtual虚函数,属于动态多态;
③重定义是子类对父类的有相同名称的非虚成员函数重新定义,函数名必须相同,对函数的返回值、形参列表没有要求;
10.#include<> vs include “ ”
主要区别就是编译器预处理阶段查找头文件的路径不同;
include<iostream>:一般用于包含标准库头文件,编译器回去系统配置的库环境变量去搜索,即编译器设置的头文件路径-->系统变量;
include"math.h":一般用于包含用户自己编写的同文件,编译器会先在项目的当前目录查找,即头文件目录--->编译器设置的头文件路径--->系统变量;
11.new/delete vs malloc/free
①new/delete是运算符,malloc/free是标准库函数;
②malloc仅仅分配内存空间,free仅仅回收空间,不具备调用构造和析构函数的功能;
③malloc需要手动计算;new自动计算分配的空间大小;
④new申请内存并初始化,malloc申请内存但不初始化;
⑤new申请内存失败,返回NULL;malloc申请失败返回异常
12.静态成员(static)
①静态全局变量:限制作用域,只在定义该变量的源文件内有效;
②静态局部变量:改变局部变量的生存周期,使得该变量存在于定义直到程序运行结束;类内声明,类外初始化;所有对象共享同一份数据
class Data{
public:
static int count ;//静态数据成员
};
int Data::count=0;//静态数据成员定义并初始化
③静态成员函数:所有对象共享同一个函数;静态成员函数不能声明为虚函数、const函数、volatile函数;静态成员函数只能访问静态成员变量;
13.const关键字
①修饰变量--->变成常量,不可改变;
②修饰指针
- const int*p; 常量指针,指向可改,指向的值不可改;
- int* const p; 指针常量,指向的值不可修改,指向可改;
- const int* const p;常量指针常量,指针指向的是一个常来给你,指向也不可以改;
③修饰类对象:对象中的任何成员都不能被修改,并且只有const修饰的成员函数可以调用该对象;
④修饰类中的成员变量:成员变量不可修改,并且只能在构造函数的初始化列表中进行初始化;
⑤修饰类的成员函数:该成员函数不能修改类对象中的成员变量,如果一定要修改,可以使用mutable关键字强制修改;const位置在函数后面
void func() const{
cout<<"114514"<<endl;
}
⑥cosnt修饰函数的形参:意味该变量在函数内部不能被修改;如果函数运行时候被传入的实参是const常量,则形参必须也是const修饰的;
14.函数传递参数的几种方式
①值传递:形参是实参的拷贝,形参的改变并不会影响实参;
②指针传递:也是值传递的一种,不过形参接收的是实参的地址,被调函数的形参被作为被调函数的局部变量处理,会在栈中开辟空间存放主调函数传入的实参值拷贝值及实参的一个副本,故当形参的指向没有变化时,对形参所指对象进行操作,等价于对实参进行操作;
③引用传递:被调函数的形参也是作为被调函数的局部变量,但对于任何引用参数的处理都会通过间接寻址的方式操作到实参,实际上就是把引用对象的地址放在了所开辟的栈空间中,函数对其形参的操作直接映射到实参上;
指针传递 vs 引用传递:引用的指向不可更改,指针的指向可以更改;
15.野指针和悬挂指针,如何避免
①野指针:没有经过初始化的指针;
②悬挂指针:最初指向的内存被释放后,未被置空的指针;
③两个所指向的都是一块无效内存的指针,访问无效内存都会导致编译出错;
④避免上述情况的发生:定义指针后且在使用之前对指针进行初始化,在内存释放后把指针置空或调用智能指针;
16.struct 和 class的区别
主要区别在访问权限和默认继承权限的不同;struct的默认访问权限和继承权限都是public;而class是private;
17.构造函数
作用:类的对象在被创建的时候,编译系统对该对象分配内存空间,并且自动调用构造函数,完成类成员的初始化;
常用写法:
①无参构造:创建一个类没有写任何的构造函数,系统会自动生成默认的无参构造函数,此函数为空;
class Person()
{
public:
Person()
{ cout<<"无参构造函数"<<endl; }
}
②有参构造函数:
class Person()
{
public:
Person(int a)
{
age=a;
cout<<"有参构造函数"<<endl; }
}
③拷贝构造函数:函数参数为对象本身的一个引用,用于根据已存在的对象复制出一个新对象;
class Person()
{
public:
Person(cosnt Person& p)
{
age=p.age;
cout<<"无参构造函数"<<endl; }
}
18.拷贝构造函数:如果用户定义有参构造函数,C++不再提供无参构造,但会提供默认拷贝构造(浅拷贝);
调用时机:
①使用一个已经创建完毕的对象来初始化一个新对象;
②值传递的方式个i函数参数传值;
③以值的方式返回局部对象;
19.深拷贝 vs 浅拷贝
①浅拷贝:简单的复制拷贝操作;
②深拷贝:在堆区重新申请空间,进行拷贝操作;
拷贝构造函数如果不利用深拷贝在堆区创建新内存,会导致浅拷贝带来的重复释放堆区的问题;
如果属性有在堆区开辟的,一定要自己提供拷贝构造函数,防止浅拷贝带来的问题;
20.析构函数
作用:用于对类做收尾工作,在对象销毁时,自动调用;只能由一个析构函数,无参;
当程序员不自定义析构函数时,其编译器会自动生成一个析构函数,没有任何操作;
析构函数常被用来释放成员变量中指针变量所指向的内存;
21.类中构造函数、析构函数的调用顺序
构造函数:虚基类-->基类--->类成员函数--->本类;
析构函数:相反;
22.面向对象的三大特性
封装、继承、多态
①封装:将属性和行为作为一个整体进行封装,将属性和行为加以权限的控制;
②继承:指某个对象可以获取另一个对象的属性和方法,可以无需重新编译源对象的属性和方法;
③多态:多态分为静态多态和动态多态;静态多态包括函数重载和运算符重载;动态多态主要指子类重写父类中的函数;作用是提高程序的复用性,同时提高代码的扩充性和可维护性;
23.类的继承权限问题
当子类继承父类时,父类中的私有权限也会被继承,但子类访问不到父类私有权限的属性和方法,所有父类被子类继承的私有权限的属性和方法不属于子类的任何一个权限;
①public继承:父类中的访问权限全都不变继承到子类;
②pritected继承:父类中的public会被继承为protected;
③private继承:父类中的public和private都会被即成为private;
24.菱形继承
两个子类击沉了同一个父类,又有某个类同时继承两个子类;
带来的问题主要是子类继承两份相同的数据,导致资源浪费与无意义;
可以采用虚继承解决问题;
25.虚函数作用(virtual)
实现动态多态;
子类重写父类的虚函数;本质是覆盖,重写:函数返回值类型 函数名 参数列表完全一致;
本质是非静态成员函数,所以不可和static一起修饰;
26.纯虚函数和抽象类
再多态中,通常父类中的虚函数的实现毫无意义,主要调用子类重写的内容,因此可以将虚函数改为纯虚函数,这个类也称为抽象类;
声明纯虚函数的目的只是为了让子类只继承父类的函数接口;
抽象类无法实例化对象;
27.虚析构和纯虚析构
多态使用时,如果子类中有属性开辟到堆区,那么父类指针在释放时无法调用到子类的析构代码,则需要将父类中的析构函数改为虚析构或纯虚析构;
如果是纯虚析构,这个类就是抽象类,无法实例化对象;
28.this指针
没有个非静态成员函数只会诞生一份函数实例,也就是说多个同类型的对象会共用一块代码,用this指针指向被调用的成员函数所属对象;
用途:
①当形参和成员变量同名时,可用this指针区分;
②在类的非静态成员中返回对象本身,可使用return *this;
29.STL常用容器
①string:是C++风格的一个字符串,本质是一个类,内部封装了很多成员方法:查找find、拷贝copy、删除delete、替换replace、插入insert;
②vector:数据结构和数组非常相似,也称为单端数组;区别在于vector可以动态扩展;并不是在原空间之后继续接新空间,二十重新找更大的内存空间,将原数据拷贝到新空间,释放原空间;
③deque:双端数组,可以对头端进行插入删除操作;
④stack:先进后出数据结构,只有一个出口;栈中只有顶端的元素才可以被外界使用,因此栈不允许有遍历行为;
⑤list: 将数据结构进行链式存储;
⑥set:所有元素都会在插入时自动被排序,底层结构使用二叉树实现;