指向类层次结构的派生部分的指针

Pointer to derived portion of a class hierarchy

本文关键字:派生部 指针 层次结构      更新时间:2023-10-16

这个问题不是关于以标准方式引用派生类的基本指针。 相反,我需要在我的代码中支持两种不同的 IOCTL 结构(SCSI_PASS_THROUGH_DIRECT_EX 和 SCSI_PASS_THROUGH_DIRECT 以供参考)。 这些结构是封装结构的一部分(每个结构都有单独的结构)。 我已经将这些封装结构子类化为一个共同的基础。

对于未使用 IOCTL 的任何人,IOCTL 的处理需要一个指向结构的指针。 在这种情况下,预期的是封装结构。

我的问题不是关于基指针是否可以指向派生类(我知道它们可以)。 我想知道的是,我如何可靠地指向内存区域中派生类的开头,此类层次结构? 下面的代码说明了我的策略,并在VS 2010和Cygwin(GCC 4.5.3)中编译。 GCC 对 offsetof() 宏使用不当提出了一些抱怨(从我正在阅读的所有内容来看,这似乎是真的),但代码似乎反映了我在运行时所追求的内容。

有没有更好的方法

#include <iostream>
#include <stdint.h>
#include <cstddef>
enum IoctlType {
    T1,
    T2
};
struct ptd1 {
    uint32_t Version;
    uint32_t Size;
    uint32_t OffsetOfStuff;
    uint8_t ary[1];
};
struct ptd2 {
    uint32_t Version;
    uint32_t Size;
    uint32_t OffsetOfStuff;
    uint8_t ary[16];
};
struct base {
    IoctlType it;
    virtual void* GetPtr()=0;
};
struct derv1 : base {
    ptd1 passThrough;
    uint8_t ary[15]; // with ptd.ary[1], array is 16 elements
    uint32_t fluff; // realign to DWORD boundary
    uint32_t stuff;
    derv1() {
        it = T1;
        passThrough.Version = 33;
        passThrough.Size = sizeof(ptd1);
        passThrough.OffsetOfStuff = offsetof(derv1, stuff);
    }
    // as the first member of the derv1 struct, this pointer should be the "start"
    virtual void* GetPtr() { return &passThrough; }
};
struct derv2 : base {
    ptd2 passThrough;
    uint32_t stuff;
    derv2() {
        it = T2;
        passThrough.Version = 22;
        passThrough.Size = sizeof(ptd2);
        passThrough.OffsetOfStuff = offsetof(derv2, stuff);
    }
    // as the first member of the derv2 struct, this pointer should be the "start"
    virtual void* GetPtr() { return &passThrough; }
};
int main() {
    base* pBase = new derv1();
    std::cout << "Now, check the results" << std::endl;
    std::cout << "size of base " << sizeof(base) << std::endl;
    std::cout << "size of derv " << sizeof(derv1) << std::endl;
    std::cout << "addr of struct " << pBase << std::endl;
    std::cout << "addr of passThrough " << pBase->GetPtr() << std::endl;
    std::cout << "offset of passThrough " << offsetof(derv1, passThrough)
        << std::endl;
    base* pBase2 = new derv2();
    std::cout << "Now, check the results" << std::endl;
    std::cout << "size of base " << sizeof(base) << std::endl;
    std::cout << "size of derv " << sizeof(derv2) << std::endl;
    std::cout << "addr of struct " << pBase2 << std::endl;
    std::cout << "addr of passThrough " << pBase2->GetPtr() << std::endl;
    std::cout << "offset of passThrough " << offsetof(derv2, passThrough)
        << std::endl;
    return 0;
}

经过多次内部辩论,我决定斯拉瓦的评论是最有帮助的; 不要这样做。 为此,我基本上制作了一个包装器结构,它知道要使用哪种结构。 标准是:

  • 这是 Windows 7 及更早版本,还是 Windows 8 及更高版本
  • 如果是 Windows 8 及更高版本,这是 32 位操作系统上的 64 位进程吗

然后,有适当的函数用于获取/设置所需的值。 最终产品如下所示:

struct ScsiPassThruExWBuffers {
    SCSI_PASS_THROUGH_DIRECT_EX  passThru;
    unsigned char cdbExt[259]; // 260 by SCSI Standard
    int fluff;  // realign
    unsigned char senseInfo[256];
    STOR_ADDR_BTL8 addrInfo;
};
struct ScsiPassThru32ExWBuffers {
    SCSI_PASS_THROUGH_DIRECT32_EX  passThru;
    unsigned char cdbExt[259]; // 260 by SCSI Standard
    int fluff;  // realign
    unsigned char senseInfo[256];
    STOR_ADDR_BTL8 addrInfo;
};
struct ScsiPassThruWBuffers {
    SCSI_PASS_THROUGH_DIRECT  passThru;
    unsigned char senseInfo[256];
};
enum PassThruType {
    PassThru,
    PassThruEx,
    PassThru32Ex
};
struct Wrapper {
    void * pStruct;
    PassThruType ptt;
    unsigned int ioctlCode;
    unsigned int aggregateStructSize;
    // ctors/dtors
    Wrapper();
    Wrapper(Wrapper const& source); // copy c-tor
    ~Wrapper(); // delete memory allocated for pStruct;
    // several setters/getters for the properties in the structures
    private:
    Wrapper& operator=(Wrapper const& rhs); // no implementation
};

这使得直通结构的所有聚合或封装结构都保留为 POD,并且可靠地确定偏移量的能力是合理的。