🍋04-C++面试之析构函数和构造函数
0 前述
针对于自己在秋招的面试中,对于Cpp
部分遇到的问题,其中大部分是以此为起点,尼克以基于这些点,将自己对于Cpp
的学习,串联起来,无论面试官,问这一类问题中的那个点,你都应该可以将这一个珠子串联到自己一大串知识点上来讲。这是一种拓展知识的能力。
在此专栏下面个人校招记录:回馈牛客,对CPP
做一个小小的总结。
本部分关于析构函数,构造函数的内容,以及两者能否声明为虚函数或者纯虚函数问题的总结。
- 下面对应的是之前发布的个人校招其他公司面试总结,希望可以更好的帮到你
-
这里是
Cpp
一些面试问题整理
1 析构函数
-
从使用角度:
1)析构函数可以为虚函数,并且一般情况下基类的析构函数都定义为虚函数(🤔原因:我们通过在基类中将析构函数定义成虚函数以确保执行正确的析构函数版本)。
2)只有在把基类的析构函数定义为虚函数,调用操作符 delete 销毁指向对象的基类指针时,才能准确调用派生类的析构函数,来析构派生对象,准确析构回收。
-
构建和析构顺序上:
1)另外就是对象析构的顺序和其创建的顺序正好相反:派生类的析构函数首先执行,然后是基类的析构函数,是沿着继承体系的反方向直到最后。从内到外构建,从外到内析构,在默认情况下,基类默认构造函数初始化派生类的基类部分。
2)析构函数可以是纯虚函数,含有纯虚函数的类是抽象类,你不能直接创建一个抽象基类的对象,此时便不能被实例化。抽象类的派生类可以根据自身需求重新改写基类中的纯虚函数,之后,就可以创建该类的对象,进行实例化。
2 构造函数
-
从使用角度上:
构造函数不能声明为虚函数,由于在构造一个对象时,必须知道该对象的实际类型,而虚函数是在运行期间确定实际类型的。问题来了,当在构建一个对象的时候,这个对象目前还未构造成功,编译器就无法知道对象的实际类型,因此,就不能是虚函数。
-
从内存初始化角度上:
虚函数的执行依赖于虚函数表,而虚函数表是在构造函数中进行初始化的,即初始化虚表指针(
vptr
),使其指向正确虚函数表。当在构造期间,虚表(vtable
)还没有被初始化,将无法进行。(💁♂️由此,可以引出,虚函数的底层原理)
3 为什么基类析构函数一搬写成虚函数virtual
防止内存泄漏:
由于类的多态性,基类指针可以指向派生类的对象,如果删除该(这样的一个)基类的指针,就会调用该指针指向的派生类析构函数,而派生类的析构函数又自动调用基类的析构函数,这样,整个派生类的对象就可以完全被释放。
反过来想:
如果析构函数不被声明成虚函数,则编译器实施静态绑定,在删除基类指针时,只会调用基类的析构函数而不调用派生类的析构函数,这样就会造成派生类对象析构不完全,造成内存泄漏。
举例运行:
基类析构函数不是虚函数
#include <iostream>
using namespace std;
class Base {
public:
Base() {
cout << "Base construct function" << endl;
};
~Base() {
cout << "Base destruct function" << endl;
}
};
class Derive : public Base {
public:
Derive() {
cout << "Derive construct function" << endl;
};
~Derive() {
cout << "Derive destruct function" << endl;
}
};
int main() {
Base* p =new Derive();
delete p;
p =NULL;
return 0;
}
// 运行结果
Base construct function
Derive construct function
Base destruct function // 只析构基类,并不析构派生类
基类析构函数是虚函数
#include <iostream>
using namespace std;
class Base {
public:
Base() {
cout << "Base construct function" << endl;
};
virtual ~Base() {
cout << "Base destruct function" << endl;
}
};
class Derive : public Base {
public:
Derive() {
cout << "Derive construct function" << endl;
};
~Derive() {
cout << "Derive destruct function" << endl;
}
};
int main() {
Base* p =new Derive(); // 基类的指针(引用)指向派生类是,
delete p; // 删除基类指针时候的调用情况
p =NULL;
return 0;
}
// 运行结果
Base construct function
Derive construct function
Derive destruct function // 先调用派生类析构
Base destruct function // 再调用基类析构
4 总结
- 类中使用虚函数,系统会有一定的空间开销。当一个类带有虚函数时,编译系统会为该类构造一个虚函数表,是一个指针数组,来存放每个虚函数的入口地址。系统在进行动态关联的时间开销会很少,提高多态性的效率。
- 虚函数的调用关系:
this->vptr->vtable->virtual function
。
这是一个求职总结专栏,求职过程中,牛客里面各位同志,提供了很多面试的信息,对我个人有很大的帮助。这里简单将自己面试记录总结于此。 本人23届校招生,双非硕士,投递岗位嵌入式,控制算法,后台开发均有涉猎,优先级递减。简历累计投递数量:提前批(34)+正式批(128),累计Offer(5+)。