通过不同的父类来自同一基类的多重继承真的是一个问题吗?

Pol*_*hic 20 c++ inheritance

我想在我的C++游戏引擎中实现一个继承层次结构,它与Java有一些类比:

  • 所有对象都继承自Object类,
  • 某些功能由接口提供.

只是一个类比,给了我一些好处(不是严格的1:1 Java:C++映射,这是不可能的).

通过"接口",我的意思是这里只有纯虚拟公共方法的类.我知道这不是一个严格而准确的定义.

所以我做了:

class Object{...};

/* interfaces */
class Nameable : public Object{...};
class Moveable : public Object{...};
class Moveable3D : public Moveable{...};
class Moveable2D : public Moveable{...};

class Model3D : public Moveable3D, public Nameable{...}
class Line3D : public Moveable3D{...}
class Level: public Nameable{...}
class PathSolver : public Object{...}
Run Code Online (Sandbox Code Playgroud)

如您所见,Model3D现在Object通过多种方式继承:

  • Moveable3D -> Moveable -> Object,
  • Nameable -> Object.

我在VS2013(VC++)中的编译器警告我.

这是一个问题吗?

如果是这样,如何解决它以获得更好的继承结构?

我正在考虑两种方式,但两者都有更多的缺点,然后是优点:

  1. 删除eg NameableObject.之间的继承.但是那种方式Level也会失去它的继承Object(我的第一个目标是:所有对象都从Object类继承).
  2. 我也可以: public Object 从每个界面中删除.但这种方式,我将力手动输入:public Object每个许多发动机final类的(Model3D,Line3D,Level,PathSolver).这样重构和创建新的最终类型都会更难.此外,每个接口将保证继承的信息Object都是这样丢失的.

如果没有必要,我想避免虚拟继承(进一步的抽象级别).但也许它(至少是: public Object)?

Ser*_*sta 21

正如Juraj Blaho所说,解决这个问题的规范方法是虚拟继承.虚拟继承的一种实现模型是使用指向基类中的真实唯一对象的虚拟继承在类的vtable中添加条目.这样,您将获得以下继承图:

             Model3D
            /       \
     Moveable3D   Nameable
           |         |
      Moveable       |
            \       /
             Object
Run Code Online (Sandbox Code Playgroud)

那是你必须拥有的:

class Nameable : public virtual Object{...};
class Moveable : public virtual Object{...};
Run Code Online (Sandbox Code Playgroud)

其他类不需要虚拟继承


如果没有虚拟继承,图表就会存在

             Model3D
            /       \
     Moveable3D   Nameable
           |         |
      Moveable       |
           |         |
        Object    Object
Run Code Online (Sandbox Code Playgroud)

有2个不同的Object实例

虚拟继承中最棘手的部分是构造函数的调用(在C++中引用虚拟继承,并解决钻石问题)因为只有一个虚拟基类的实例由从它继承的多个类共享,所以构造函数对于虚基类不会被从它继承的类调用(这是调用构造函数的方式,当每个类都有自己的父类副本时),因为这意味着构造函数会多次运行.相反,构造函数由具体类的构造函数调用.... 顺便说一下,虚拟基类的构造函数总是在非虚拟基类的构造函数之前调用.这确保了从虚基类继承的类可以确保在继承类的构造函数中使用虚拟基类是安全的.具有虚基类的类层次结构中的析构函数顺序遵循与C++其余部分相同的规则:析构函数以与构造函数相反的顺序运行.换句话说,虚拟基类将是最后一个被销毁的对象,因为它是第一个完全构造的对象.

但真正的问题是,这种衍生的可怕钻石通常被认为是一种糟糕的层次结构设计,并且在Java中明显被禁止.

但是恕我直言,如果所有的接口类都是纯粹的抽象类(只有纯虚拟公共方法)而不是从Object继承而且所有的实现类都明确地继承了C++虚拟继承,那么C++虚拟继承所做的事情并没有那么远.宾语.使用与否取决于你:毕竟它是语言的一部分......


Jur*_*aho 5

使用接口的虚拟继承。这将确保派生类中只有一个基类实例:

class Object{...};

/* interfaces */
class Nameable : public virtual Object{...};
class Moveable : public virtual Object{...};
class Moveable3D : public virtual Moveable{...};
class Moveable2D : public virtual Moveable{...};

class Model3D : public virtual Moveable3D, public virtual Nameable{...}
class Line3D : public virtual Moveable3D{...}
class Level: public virtual Nameable{...}
class PathSolver : public virtual Object{...}
Run Code Online (Sandbox Code Playgroud)

如果没有虚拟继承,对象中会有相同基类的多个实例,这将导致您无法替换需要接口的具体类型:

void workWithObject(Object &o);

Model3D model;
workWithObject(model);
Run Code Online (Sandbox Code Playgroud)