【问题标题】:Can I promote base class to child class?我可以将基类提升为子类吗?
【发布时间】:2020-10-28 09:59:08
【问题描述】:

我有一个A类,如下:

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)
{
}

还有一个从A类派生的B类,如下:

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。

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

所以我在下面的代码 sn-p 中这样做:

    CClassA a(lpData, uSize);

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

我会使用一个内存块来创建一个Class A的实例,那么如果数据的类型是0,那么我们就知道数据是B类的,那么我将把原始实例提升到它的子类ClassB这样我们就可以调用 ProcessBData 来处理特定于 B 类的数据。

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

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

那么如何解决这样的delimma呢?

谢谢

【问题讨论】:

  • 如果基类实际上不是子类,则绝对不要将基类转换为子类。在我看来,实例化哪个类的决定应该由工厂函数根据手头的数据来处理。
  • 为什么不简单地实现一些移动语义呢?或者,如果旧对象必须保持有序,则使另一个类不是子类,而是存储指向第一个类的指针。
  • 我也确实认为指针是正确的方法。工厂如何创建一个包含指针的对象。
  • 您的代码 sn-p 包含未定义的行为。在某些情况下,它可能似乎适用于某些编译器,但在其他情况下会失败。如果ClassB 有额外的数据成员,那肯定是非常危险的。
  • 来自“写出完美的问题”codeblog.jonskeet.uk/2010/08/29/writing-the-perfect-question > One trap that many posters fall into is to ask how to achieve some “small” aim, but never say what the larger aim is. 那么你的目标是什么?什么是lpDatauSize,您如何使用它们?你如何初始化m_Data?为什么在这种情况下需要继承?没有这些信息就没有困境,因为不清楚选项是什么。

标签: c++ superclass base-class


【解决方案1】:

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

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

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

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

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

解决方案 2: 好吧,显然请查看您的文档或询问提供数据的开发人员。

解决方案 3: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

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

【讨论】:

  • 实际上我在评论中使用了指针建议。还是谢谢你。
【解决方案2】:

确切地弄清楚你想要做什么是一种挑战......据我所知,如果你尝试从基类转换为派生类,这可能是不正确的,并可能导致UB或不受欢迎的定义行为...这真的很难说,因为它取决于将要使用的上下文、极端情况等...

这是一个可能对您有所帮助的建议结构...它通过模板专业化和 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++;
    }
};

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-04-29
    • 1970-01-01
    • 1970-01-01
    • 2020-10-27
    相关资源
    最近更新 更多