结构体继承情况下,子类memset导致函数调用出现问题

2023-06-29  本文已影响0人  雯饰太一

相关代码

struct  I_Basic
{
    virtual void GetBinaryData(QByteArray& data) = 0;
    virtual uint8_t GetTaskType() = 0;
};

struct Data_S1 :public I_Basic
{
    Data_S1():uId(0), dLon(.0), dLat(.0){}
    virtual void GetBinaryData(QByteArray& data)
    {
        data.append(0x1C);
        data.append(uId);
        data.append(GetTaskType());
        data.append(dLon);
        data.append(dLat);
    }
    virtual uint8_t GetTaskType() { return 0; }
    uint16_t uId;    // 无人车ID
    double dLon;
    double dLat;
};


QbyteArray ConstructSendBuf(I_Basic& sdcRef)
{
    QByteArray resArr;
    resArr.resize(1024);
    sdcRef.GetBinaryData(resArr); // 如果有memset执行函数会崩溃,原因:虚函数表指针被清理了 
    return resArr;
}

int main()
{
    Data_S1 params;
    params.uId = 0xff;
    params.dLon = 123.01;
    params.dLat = 30.12;
    
    QByteArray arr = ConstructSendBuf(params);
}

关于虚函数表

虚函数表(Virtual Function Table,简称 vtable)是 C++ 中用于实现多态的机制之一。它是一张存储了虚函数指针的表格,用于解决基类指针指向派生类对象时的函数调用问题。

以下是虚函数表的相关知识描述:

  1. 虚函数:

    • 虚函数是在基类中声明为虚函数的成员函数。
    • 派生类可以重写(覆盖)基类的虚函数,通过在派生类中使用 override 关键字来显式指示重写基类的虚函数。
    • 虚函数通过基类指针或引用进行调用时,会根据指针或引用所指向的对象的实际类型来确定调用的是基类函数还是派生类函数。
  2. 虚函数表:

    • 虚函数表是一个存储了虚函数指针的数据结构,通常是一个由编译器自动生成的隐藏指针。
    • 对于每个包含虚函数的类,编译器会在该类的对象中插入一个指向虚函数表的指针,通常是作为对象的第一个成员。
    • 虚函数表中的每个条目对应一个虚函数,该条目存储了实际的函数地址。
    • 每个对象的虚函数表指针都指向该类的虚函数表,从而实现了动态绑定。
  3. 虚函数调用:

    • 当通过基类指针或引用调用虚函数时,会先访问对象的虚函数表指针,然后根据虚函数表指针找到对应的虚函数表。
    • 然后根据函数在虚函数表中的索引位置,找到实际的函数地址进行调用。
    • 这种动态绑定的机制使得在运行时可以根据对象的实际类型来确定调用的是哪个函数,实现多态性。

虚函数表是 C++ 实现多态的基础,它使得在通过基类指针或引用调用虚函数时能够正确地调用派生类的函数。这种机制允许在基类中定义通用的接口,而由派生类提供不同的实现。

结构体存在继承情况时,使用memset会对子类的虚函数表有何影响?

使用 memset 函数对结构体进行内存清零操作时,会对结构体中的所有成员进行赋值,包括虚函数表指针。这可能会导致子类的虚函数表指针被覆盖,进而影响虚函数的调用。

在 C++ 中,使用 memset 对含有虚函数的结构体进行内存清零是不安全的,因为它会直接对内存进行字节级别的赋值,不考虑类型和对象的内部结构。这会导致虚函数表指针被设置为 0,破坏了虚函数的调用机制。

正确地对含有虚函数的结构体进行初始化或赋值操作应使用适当的构造函数、拷贝构造函数或赋值运算符重载等方法,以保证虚函数表的正确设置和维护。

总之,不建议使用 memset 对含有虚函数的结构体进行内存清零操作,因为它可能破坏虚函数的调用机制。最好使用适当的方式进行结构体的初始化和赋值。

上一篇下一篇

猜你喜欢

热点阅读