我可以将基础 class 提升为 child class 吗?

Can I promote base class to child class?

我有一个classA,如下:

struct MY_DATA
{
    int m_nType;

    union
    {
        DWORD m_dwBData;
        double m_dCData;
    } u;
};

class CClassA
{
public:
    CClassA(BYTE* lpData, UINT uSize);
    virtual ~CClassA(void);

    MY_DATA m_Data;
};

CClassA::CClassA(BYTE* lpData, UINT uSize)
{
    ::memcpy(&m_Data, lpData, uSize);
}

CClassA::~CClassA(void)
{
}

和一个class B派生自class A,如下:

class CClassB : public CClassA
{
public:
    CClassB(BYTE* lpData, UINT uSize);
    virtual ~CClassB(void);

    void ProcessBData()
    {
        m_Data.u.m_dwBData ++;
    }
};

CClassB::CClassB(BYTE* lpData, UINT uSize)
: CClassA(lpData, uSize)
{
}

CClassB::~CClassB(void)
{
}

然后我收到一个大小为uSize的内存块lpData。 uSize 与 MY_DATA 的大小相同。但是事先不知道m_nType

我的目的是判断内存块的类型,按照类型调用相应的处理函数处理数据

所以我在下面的代码片段中这样做:

    CClassA a(lpData, uSize);

    if (a.m_Data.m_nType == 0)
    {
        CClassB& b = (CClassB&)a;
        b.ProcessBData();
    }

我会用一个内存块创建一个ClassA的实例,那么如果数据的类型是0,那么我们就知道数据是classB的,那么我会将原始实例提升到其 child class ClassB 以便我们可以调用 ProcessBData 来处理特定于 class B.

的数据

但是,我怀疑这是否可行,因为 class B 是 A 的 child class,因此它将包含比 class A 更多的条目,通过将基数 class 提升到它的 child class,这会导致问题吗?

另一种解决方案是在知道类型为 0 后,使用相同的内存块创建一个新的 Class B 实例。但是,这会降低性能,因为我们复制内存块两次,一次到 class A,一次到 class B.

那么如何解决这样的困境呢?

谢谢

如果没有 adding/getting 有关数据内容的任何类型信息,您将无法在运行时获取类型信息。如果你能以某种方式获取类型信息,有一些解决方案

  1. 要么你在处理它之前设置 struct MY_DATA::m_nType 通过查看数据来获取它的类型(也许你的数据中有某种标识符,特定类型的特定值范围,不同数据的不同大小类型)。
  2. 文档为您提供了一些关于您将收到何种信息的信息
  3. 您可以更改 MY_DATA 的声明并使用 Runtime Type information

解决方案一: 在 DWORD 和 double 的情况下,您可以使用数据的大小

std::cout << sizeof(unsigned int) << std::endl;
>> 4
std::cout << sizeof(bouble) << std::endl;
>> 8

如果您有权访问数据的原始创建。

方案二: 好吧,显然是看你的文档或者问问提供数据的开发者。

方案三: 使用 Runtime Type information 作为您的 MY_DATA 结构。这可能如下所示:

myclass.h

#pragma once

#include <cstdint>

struct MY_DATA{
  virtual ~MY_DATA() {};
};

struct MY_DATA_B : public MY_DATA{
    unsigned int data;
};

struct MY_DATA_C : public MY_DATA{
    double data;
};

class CClassB {
public:
    CClassB(MY_DATA* lpData);
    virtual ~CClassB(void);

    void myMethodB();

private:
  MY_DATA_B* m_Data;
};

class CClassC {
public:
    CClassC(MY_DATA* lpData);
    virtual ~CClassC(void);

    void myMethodC();

private:
  MY_DATA_C* m_Data;
};

myclass.cpp

#include "myclass.h"

#include <cstring>
#include <iostream>

CClassB::CClassB(MY_DATA* lpData){
  m_Data = new MY_DATA_B;
  memcpy(m_Data, lpData, sizeof(MY_DATA_B));
}

CClassB::~CClassB(){
  delete m_Data;
}

void CClassB::myMethodB(){
  std::cout << "MY_DATA_B data: " <<  m_Data->data << std::endl;
}

CClassC::CClassC(MY_DATA* lpData){
  m_Data = new MY_DATA_C;
  memcpy(m_Data, lpData, sizeof(MY_DATA_C));
}

CClassC::~CClassC(){
  delete m_Data;
}

void CClassC::myMethodC(){
  std::cout << "MY_DATA_C data: " << m_Data->data << std::endl;
}

main.cpp

#include "myclass.h"

#include <iostream>
#include <typeinfo>


void execute(MY_DATA * ptr){
  if(typeid(*ptr) == typeid(MY_DATA_B)){
    CClassB a(ptr);
    a.myMethodB();
  }else if(typeid(*ptr) == typeid(MY_DATA_C)){
    CClassC a(ptr);
    a.myMethodC();
  }
}

int main(){
  MY_DATA_B b;
  b.data = 5;
  MY_DATA_C c;
  c.data = 3.1;

  MY_DATA * ptr = &b;
  MY_DATA * ptr2 = &c;

  execute(ptr);
  execute(ptr2);
  
  return 0;
}

这将导致以下输出:

MY_DATA_B data: 5
MY_DATA_C data: 3.1

如果你真的只是得到一堆内存而没有任何关于它的内容的进一步信息并且想要得到信息的类型=>那么你的设计已经无法修复。

要准确地弄清楚你想做什么是有挑战性的……据我所知,如果你试图从基础 class 转换为派生的 class,这可能是错误的,并可能导致 U.B。或不受欢迎的定义行为......这真的很难说,因为它取决于它将被使用的上下文,极端情况等......

这里有一个可能的建议结构,可以帮助您...它通过模板专业化和 CRTP 结构的组合使用多态继承。它可能看起来像这样:

template<class T, int type>
class Data {
protected:
    Data() {};
public:
    virtual ~Data() {}
};

template<class T>
class Data<T, 0> {
protected:
    DWORD data;
};

template<class T>
class Data<T, 1> {
protected:
    double data;
};

template<typename T, int type>
class Base : public Data<T, type> {
protected:
    Base() {}
public:
    virtual ~Base() {}
    virtual void processData() {}
};

class ClassA : public Base<ClassA, 0> {
public:
    ClassA(const BYTE* lpData, UINT size) {
        ::memcpy( &(this->data), lpData, size);
    }

    void processData() override {
        return;
    }
};

class ClassB : public Base<ClassB, 1> {
public:
    ClassB(const BYTE* lpData, UINT size) {
        ::memcpy( &(this->data), lpData, size);
    }

    void processData() override {
        data++;
    }
};