广告
返回顶部
首页 > 资讯 > 后端开发 > 其他教程 >C++的多态和虚函数你真的了解吗
  • 579
分享到

C++的多态和虚函数你真的了解吗

2024-04-02 19:04:59 579人浏览 薄情痞子
摘要

目录一、c++的面试常考点二、阿里真题2.1 真题一(1)虚函数表vtbl(2)构造一个派生类对象的过程(3)析构一个派生类对象的过程2.2 真题二2.3 真题三2.4 真题四2.5

一、C++的面试常考点

阿里虽然是国内Java的第一大厂但是并非所有的业务都是由Java支撑,很多服务和中下层的存储,计算,网络服务,大规模的分布式任务都是由C++编写。在阿里所有部门当中对C++考察最深的可能就是阿里云

阿里对C++的常考点:

1.STL 容器相关实现

2.C++新特性的了解

3.多态和虚函数的实现

4.指针的使用

二、阿里真题

2.1 真题一

现在假设有一个编译好的C++程序,编译没有错误,但是运行时报错,报错如下:你正在调用一个纯虚函数(Pure virtual function call error),请问导致这个错误的原因可能是什么?

纯虚函数调用错误一般由以下几种原因导致:

  • 从基类构造函数直接调用虚函数。(直接调用是指函数内部直接调用虚函数)
  • 从基类析构函数直接调用虚函数。
  • 从基类构造函数间接调用虚函数。(间接调用是指函数内部调用其他的非虚函数,其内部直接或间接地调用了虚函数)
  • 从基类析构函数间接调用虚函数。
  • 通过悬空指针调用虚函数。

注意:其中1,2编译器会检测到此类错误。3,4,5编译器无法检测出此类情况,会在运行时报错。

(1)虚函数表vtbl

编译器在编译时期为每个带虚函数的类创建一份虚函数表

实例化对象时, 编译器自动将类对象的虚表指针指向这个虚函数表

(2)构造一个派生类对象的过程

1.构造基类部分:

  • 构造虚表指针,将实例的虚表指针指向基类的vtbl
  • 构造基类的成员变量
  • 执行基类的构造函数函数体

2.递归构造派生类部分:

  • 将实例的虚表指针指向派生类vtbl
  • 构造派生类的成员变量
  • 执行派生类的构造函数体

(3)析构一个派生类对象的过程

1.递归析构派生类部分:

  • 将实例的虚表指针指向派生类vtbl
  • 执行派生类的析构函数体
  • 析构派生类的成员变量(这里的执行函数体,析构派生类成员变量,两者的顺序和构造的步骤是相反的)

2.析构基类部分:

  • 将实例的虚表指针指向基类的vtbl
  • 执行基类的析构函数函数体
  • 析构基类的成员变量

构造函数和析构函数执行函数体时,实例的虚函数表指针,指向构造函数和析构函数本身所属的类的虚函数表,此时执行的虚函数,即调用的本身的该类本身的虚函数,下面是一个【间接调用】的栗子:基类中的析构函数中,调用纯虚函数(该虚函数就在基类中定义)。

#include <iOStream>
using namespace std;

class Parent {
public:
	//纯虚函数
    virtual void virtualFunc() = 0;
    void helper() {
        virtualFunc();
    }
    virtual ~Parent(){
        helper();
    }
};

class Child : public Parent{
    public:
    void virtualFunc() {
        cout << "Child" << endl;
    }
    virtual ~Child(){}
};


int main() {
    Child child;
	//system("pause");
    return 0;
}

运行时报错libc++abi.dylib: Pure virtual function called

在这里插入图片描述

2.2 真题二

在构造实例过程当中一部分是初始化列表一部分是在函数体内,你能说一下这些的顺序是什么?差别是什么和this指针构造的顺序

顺序:

(1)初始化列表中的先初始化。

(2)执行函数体代码。

  • 执行类中函数体,如执行构造函数时,所有成员已经初始化完毕了;
  • this指针属于对象,而对象还没构造完成前,若使用this指针,编译器会无法识别。在初始化列表中显然不能使用this指针,注意:在构造函数体内部可以使用this指针。

构造函数的执行可以分成两个阶段:

  • 初始化阶段:所有类类型的成员都会在初始化阶段初始化,即使该成员没有出现在构造函数的初始化列表中。
  • 计算赋值阶段:一般用于执行构造函数体内的赋值操作。
#include <iostream>
using namespace std;

class Test1 {
public:
    Test1(){
    	cout << "Construct Test1" << endl;
    }
	//拷贝构造函数
    Test1& operator = (const Test1& t1) {
    	cout << "Assignment for Test1" << endl;
        this->a = t1.a;
        return *this;
    }
    int a ;
};

class Test2 {
public:
    Test1 test1;
	//Test2的构造函数
    Test2(Test1 &t1) {
    	cout << "构造函数体开始" << endl;
        test1 = t1 ;
        cout << "构造函数体结束" << endl;
    }
};

int main() {
    Test1 t1;
    Test2 test(t1);
	system("pause");
    return 0;
}

在这里插入图片描述

分析上面的结果:

(1)第一行结果即Test t1实例化对象时,执行Test1的构造函数;

(2)第二行代码,实例化Test2对象时,在执行Test2构造函数时,正如上面所说的,构造函数的第一步是初始化阶段:所有类类型的成员都会在初始化阶段初始化,即使该成员没有出现在构造函数的初始化列表中。所以Test2在构造函数体执行之前已经使用了Test1的默认构造函数初始化好了t1。打印出Construct Test1

这里的拷贝构造函数中可以使用this指针,指向当前对象。

(3)第三四五行结果:执行Test2的构造函数。

2.3 真题三

初始化列表的写法和顺序有没有什么关系?

构造函数的初始化列表中的前后位置,不影响实际标量的初始化顺序。成员初始化的顺序和它们在类中的定义顺序一致。

必须使用初始化列表的情况:数据成员是const、引用,或者属于某种未提供默认构造函数的类类型。

2.4 真题四

在普通的函数当中调用虚函数和在构造函数当中调用虚函数有什么区别?

普调函数当中调用虚函数是希望运行时多态。而在构造函数当中不应该去调用虚函数因为构造函数当中调用的就是本类型当中的虚函数,无法达到运行时多态的作用。

2.5 真题五

成员变量,虚函数表指针的位置是怎么排布?

如果一个类带有虚函数,那么该类实例对象的内存布局如下:

  • 首先是一个虚函数指针,
  • 接下来是该类的成员变量,按照成员在类当中声明的顺序排布,整体对象的大小由于内存对齐会有空白补齐。
  • 其次如果基类没有虚函数但是子类含有虚函数:
    • 此时内存子类对象的内存排布也是先虚函数表指针再各个成员。

如果将子类指针转换成基类指针此时编译器会根据偏移做转换。在visual studio,x64环境下测试,下面的Parent p = Child();是父类对象,由子类来实例化对象。

#include <iostream>
using namespace std;

class Parent{
public:
    int a;
    int b;
};

class Child:public Parent{
public:
    virtual void test(){}
    int c;
};

int main() {
    Child c = Child();
    Parent p = Child();
    cout << sizeof(c) << endl;//24
    cout << sizeof(p) << endl;//8

    Child* cc = new Child();
    Parent* pp = cc;
    cout << cc << endl;//0x7fbe98402a50
    cout << pp << endl;//0x7fbe98402a58
	cout << endl << "子类对象abc成员地址:" << endl;
    cout << &(cc->a) << endl;//0x7fbe98402a58
    cout << &(cc->b) << endl;//0x7fbe98402a5c
    cout << &(cc->c) << endl;//0x7fbe98402a60
	system("pause");
    return 0;
}

结果如下:

24
8
0000013AC9BA4A40
0000013AC9BA4A48

子类对象abc成员地址:
0000013AC9BA4A48
0000013AC9BA4A4C
0000013AC9BA4A50
请按任意键继续. . .

分析上面的结果:

(1)第一行24为子类对象的大小,首先是虚函数表指针8B,然后是2个继承父类的int型数值,还有1个是该子类本身的int型数值,最后的4是填充的。

(2)第二行的8为父类对象的大小,该父类对象由子类初始化,含有2个int型成员变量。

(3)子类指针cc指向又new出来的子类对象(第三个),然后父类指针pp指向这个子类对象,这两个指针的值:

  • 父类指针pp值:0000013AC9BA4A48
  • 子类指针cc值:0000013AC9BA4A40

即发现如之前所说的:如果将子类指针转换成基类指针此时编译器会根据偏移做转换。我测试环境是64位,所以指针为8个字节。转换之后pp和cc相差一个虚表指针的偏移。

(4)&(cc->a)的值即 0000013AC9BA4A48,和pp值是一样的,注意前面的 0000013AC9BA4A40到0000013AC9BA4A47其实就是子类对象的虚函数表指针了。

三、小结

阿里常考的C++的问题集中在以下几点:

  • 虚函数的实现
  • 虚函数使用出现的问题原因
  • 带有虚函数的类对象的构造和析构过程
  • 对象的内存布局
  • 虚函数的缺点:相比普通函数,虚函数调用需要2次跳转(即需要先找到对象的虚函数表,再查找该表项,即虚函数指针,即真正的虚函数地址),会降低CPU缓存的命中率。运行时绑定,编译器不好优化

总结

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注编程网的更多内容!    

--结束END--

本文标题: C++的多态和虚函数你真的了解吗

本文链接: https://www.lsjlt.com/news/139141.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • c语言怎么计算字符串长度
    在C语言中,可以使用内置的strlen()函数来计算字符串的长度。该函数位于string.h头文件中。以下是一个示例代码: #inc...
    99+
    2024-04-29
    c语言
  • c语言怎么判断三个数的大小
    可以通过比较三个数之间的大小关系来判断它们的大小。以下是一个示例代码: #include <stdio.h> int ...
    99+
    2024-04-29
    c语言
  • c++排序函数sort怎么使用
    在C++中,使用sort函数对容器中的元素进行排序,可以按照默认的升序排列,也可以自定义排序规则。 下面是一个简单的例子,对一个ve...
    99+
    2024-04-29
    c++
  • c++中string的compare函数怎么使用
    在C++中,可以使用string的compare函数来比较两个string对象的大小。该函数的原型如下: int compare(c...
    99+
    2024-04-29
    c++
  • c++中string的compare函数有什么用
    c++中string的compare函数用于比较两个字符串的大小关系。该函数返回一个整数值,表示两个字符串的比较结果。 具体来说,如...
    99+
    2024-04-29
    c++
  • c++中print函数的功能有哪些
    在C++中,通常使用std::cout来进行输出操作,而不是使用print函数。std::cout是C++标准库中的一个输出流对象,...
    99+
    2024-04-29
    c++
  • c语言数据结构怎么使用
    在C语言中,数据结构可以通过定义结构体来实现。以下是一些常见的数据结构的使用方法示例: 链表: #include <std...
    99+
    2024-04-29
    c语言
  • c++中print函数的用法是什么
    在C++中没有内置的print函数,但可以使用cout来输出内容到控制台。cout是C++标准库中的一个输出流对象,可以使用<...
    99+
    2024-04-29
    c++
  • c++中concept的用法是什么
    在C++20中,Concept是一种新的语言特性,用于定义类型要求和约束。Concept可以被用来约束函数模板、类模板和普通函数的参...
    99+
    2024-04-29
    c++
  • c++中concept的作用是什么
    在C++中,concept的作用是定义一种通用的约束,用于限制模板参数的类型范围。通过使用concept,可以在编译时对模板参数进行...
    99+
    2024-04-29
    c++
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作