【问题标题】:Solution to diamond inheritance, but is it appropriate?钻石继承的解决方案,但合适吗?
【发布时间】:2015-07-02 19:43:28
【问题描述】:

除了我一直在做的其他各种事情之外,我还一直在制作图像 api。我遇到的一个问题是需要一个包含图像尺寸的图像基类。 ImageIn 和 ImageOut 会从它分支出来,而 ImageIO 会从这些分支出来。这是我原来的解决方案;

class ImageBase{
public:
  point<int> Dim() const=0;
};

class ImageI : virtual public ImageBase{
public:
  virtual unsigned int get(int x, int y) const=0;
};

class ImageO : virtual public ImageBase{
public:
  virtual void set(int x, int y, unsigned int col)=0;
};

class ImageIO : public ImageI, public ImageO{
};

class Bitmap : public ImageIO{
  ...
};

当然,现在它们是每个图像实例的两个 vtable 指针和暗淡,并且我想考虑到像素数组的大小,可能不值得担心,但是对此有些苦行不愉快,所以我使用此解决方案;

class ImageIO{ // could also be called ImageBase
public:
  point<int> Dim() const=0;
  virtual unsigned int get(int x, int y) const=0;
  virtual void set(int x, int y, unsigned int col)=0;
};

class ImageI : public ImageIO{
public:
  point<int> Dim() const=0;
  virtual unsigned int get(int x, int y) const=0;
private:
  virtual void set(int x, int y, unsigned int col)=0;
};

class ImageO : public ImageIO{
public:
  point<int> Dim() const=0;
  virtual void set(int x, int y, unsigned int col)=0;
private:
  virtual unsigned int get(int x, int y) const=0;
};

class Bitmap : public ImageIO{
  ...
};

现在,由于 ImageO 和 ImageI 中的某些运算符是私有的,因此无法从该类型的指针访问它们,从而完成了类的输入和输出版本所完成的工作。不过有一个小问题,转换语法现在有点复杂。

ImageI* myImgInput = static_cast<ImageI*>((ImageIO*)(&myReal));

我在想一些巧妙地使用转换函数可以使过程自动化,所以最后一个问题是,这有什么内在缺陷吗?我已经用一个小实现对其进行了测试,它可以完全运行,但我不想完成 api,然后必须重新开始。

编辑

请注意,为了清楚起见,第二个示例中的 ImageBase 现在是 ImageIO,并且第二个示例中的 ImageI 和 ImageO不应虚拟继承自 ImageIO

【问题讨论】:

  • 编辑是第二个问题,应该被删除并作为一个新问题提出
  • 请注意,第二个示例中的类不应该使用虚拟继承。
  • 看起来原来的解决方案更好,更正确。
  • 怎么样?第二个解决方案的大小只有一半,并且可以包含第一个解决方案的所有功能。有什么问题?

标签: c++ virtual-inheritance diamond-problem


【解决方案1】:

如果基础有数据成员,这将不起作用

使用以下尺寸

Imagaebase=4
ImageI=12 + 4 from Imagebase
ImageO=24 + 4 from Imagebase

在第一个示例中,Bitmap 的大小为 4 + 12 + 24(注意 Imagebase 只计算一次)= 40

在第二种情况下,Bitmap 的大小为 4

如果尺寸如此歪斜,铸造将无法正常工作

如果任何类中都没有数据,那么:

Base 应该是ImageIO 并包含所有功能

然后让ImageI 和ImageO 继承它以使不允许的函数成为私有函数。那么就不需要强制转换了。

如下所示:

class ImageIO{
public:
  point<int> Dim() const=0;
  virtual unsigned int get(int x, int y) const=0;
  virtual void set(int x, int y, unsigned int col)=0;
};

class ImageI : virtual public ImageIO{
private:
  virtual void set(int x, int y, unsigned int col)
  {
        // Can't be called
  }
};

class ImageO : virtual public ImageIO{
private:
  virtual unsigned int get(int x, int y) const
  {
        // Can't be called
  }
};

class Bitmap : public ImageIO{
  ...
};

【讨论】:

  • 我不理解,为什么 ImageO 会比 ImageI 大?它们都不包含数据,它们只声明函数,并且由于它们的继承相似,我应该认为它们都具有相同的宽度?
  • 我刚刚用 -fdump-class-hierarchy 编译了这里的代码,.class 文件将第一个示例的类 ImageIO 声明为只有 8 个字节宽,第二个示例的版本为只有四个字节宽。 40 字节宽是什么意思?
  • @user4578093 您没有明确说明基类有没有数据,如果没有数据,请参阅编辑以获取解决方案
  • 这正是第二个例子中的状态。 ImageO -> ImageBase, ImageI -> ImageBase, Bitmap -> ImageBase
  • 那么,你同意这个结构吗?
猜你喜欢
  • 1970-01-01
  • 2017-12-09
  • 2018-11-12
  • 2011-02-09
  • 2012-08-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多