C++高频面试
1.sizeof是什么?sizeof一个class大小怎么确定?是在编译期还是在运行期确定?
1.1 sizeof
是一个在C和C++等编程语言中使用的运算符,它用于获取存储在内存中某个类型或对象所占用的字节大小。当你使用sizeof运算符时,它会返回一个size_t类型的值,这个值表示的是字节数。
1.2 对于一个类来说,其大小是在编译期间确定的,因为编译器需要知道每个类型的确切大小以便于分配内存空间。
1.3 一个类的sizeof是由多个因素决定的,主要包括:
类中所有非静态成员变量
的大小:每个成员变量都有自己的数据类型,sizeof运算符可以用来计算这些类型的字节大小,类的大小至少是这些成员变量大小的总和。
内存对齐
:为了提高访问效率,许多系统会对数据进行对齐处理。例如,某些系统要求所有类型的数据按照其大小的倍数进行对齐。这意味着如果一个成员变量是一个char类型(通常是1字节),而下一个成员变量是一个double类型(通常是8字节),那么char变量后面可能会有额外的填充字节,以确保double变量按照其自身大小的倍数进行对齐。
虚函数
:如果类包含虚函数,编译器会在这个类的对象中插入一个虚函数表指针(vptr)。这个指针指向一个虚函数表,该表包含了类中所有虚函数的地址。这个额外的指针会增加类的大小。
继承
:如果一个类是从其他类派生出来的,派生类的大小至少是其直接基类和间接基类的大小加上派生类自己添加的成员变量大小。需要注意的是,虽然派生类包含了基类的所有成员,但基类的成员不会重复计算,也就是说,派生类的大小不会是所有基类大小的简单累加。
在编译期间,编译器会根据上述因素计算出一个类的总大小。这个大小是固定的,与运行时无关。因此,当你在程序中创建类的实例或将类的指针或引用传递给函数时,sizeof运算符会返回这个在编译时确定的大小。
需要注意的是,sizeof并不考虑运行时可能发生的变化,例如动态分配的内存或者对象的动态大小。它只是简单地返回在编译时确定的、存储该类型或对象所需的字节大小。
举个例子,如果你有一个简单的类定义如下:
class MyClass {
public:
int a;
double b;
char c;
};
2.函数重载的机制,重载是在编译期还是在运行期确定,重载有额外开销吗
函数重载是C++等编程语言中的一种特性,它允许在同一个作用域中定义多个具有相同名称但参数列表不同的函数。
函数重载的确定是在编译期间进行的。编译器在处理函数调用时,会根据函数名和提供的参数类型及数量来匹配对应的函数声明或定义。也就是C++编译时多态的体现。
在函数重载的过程中,可能会涉及到以下几个方面:
参数类型匹配
:编译器会检查提供的参数类型是否与函数声明中的参数类型兼容。这可能涉及到类型转换,如隐式类型转换或显式类型转换(使用static_cast等)。
参数数量匹配
:函数调用必须提供正确数量的参数,与函数声明或定义中列出的参数数量相匹配。
函数参数顺序
:虽然参数顺序对于函数重载的解析不是决定性因素,但在某些情况下,参数顺序可能会影响最佳匹配的判断,尤其是在存在可变形参数(如C++中的std::initializer_list)的情况下。
至于开销,函数重载本身在运行时并没有额外的执行开销。一旦编译器在编译期间确定了调用哪个重载版本,运行时的函数调用就像调用任何其他函数一样。然而,函数重载可能会增加编译器的工作负担,因为它需要在多个候选函数中进行匹配,这可能会使得编译时间略有增加。
此外,如果重载的函数数量过多,可能会导致编译器报错,如模板实例化过多导致编译错误,或者在某些情况下,重载函数的选择可能会变得复杂,增加程序员的理解和调试难度。
3.函数重写在编译还是运行时确定?
重写(OverWrite):重写是子类对父类的允许访问的方法的实现过程进行重新编写, 返回值和形参都不能改变。即外壳不变,核心重写!
重写的好处在于子类可以根据需要,定义特定于自己的行为。 也就是说子类能够根据需要实现父类的方法。
方法的重写发生在运行时。因为在编译时,编译器是无法知道我们到底是调用父类的方法还是子类的方法,相反的,只有在实际运行的时候,我们才知道应该调用哪个方法。这个也是C++运行时多态的体现。
4.如何找到虚函数表,如何找到虚函数指针
虚函数表
(vtable)是C++中实现动态多态性的关键组件之一。当你在类中声明至少一个虚函数时,编译器会为该类创建一个虚函数表,这个表是一个存储了指向类中所有虚函数的指针的数组。每个包含虚函数的类实例都包含一个指向其虚函数表的指针,称为虚函数指针(vptr)
。虚函数表在编译的时候产生 ,否则这个类的结构信息中也不会插入虚指针的地址信息。
虚函数表指针
是虚函数表所在位置的地址。虚函数表指针属于对象实例。因而通过new出来的对象的虚函数表指针位于堆,声明对象的虚函数表指针位于栈虚函数表
位于只读数据段(.rodata),即:C++内存模型中的常量区虚函数
代码则位于代码段(.text),也就是C++内存模型中的代码区
C++类的虚函数表和虚函数在内存中的位置
对于这种简单的类,其对象内存布局的最开始四个字节就是一个虚函数表指针(32位编