C++ 继承 class 并将修改后的参数发送给父级的构造函数

C++ Inherit class with sending modified parameters to parent's constructor

我想创建 class 继承,我的新 class 创建父对象 class 的对象,设置一些默认参数(指向其他对象的指针)。

一开始我有:

btDefaultCollisionConfiguration* collisionConfiguration =
        new btDefaultCollisionConfiguration();
btCollisionDispatcher* dispatcher = new btCollisionDispatcher(collisionConfiguration);
btVector3 worldAabbMin(-1000,-1000,-1000);
btVector3 worldAabbMax(1000,1000,1000);

btAxisSweep3* broadphase = new btAxisSweep3(worldAabbMin,worldAabbMax);

colWorld = new btCollisionWorld(dispatcher,broadphase,collisionConfiguration);
colWorld->setDebugDrawer(dbgdraw);

我想要这样的东西:

colWorld = new myBtCollisionWorld(dbgdraw);

我尝试了很多东西并发明了一个技巧

btVector3 worldAabbMin;
btVector3 worldAabbMax;

class BtOgreCollisionWorld_initializer { //A trick: we need one more constructor to
    // initialize default variables; Then we will pass them to btCollisionWorld
    // as it stands next in inheritance list
public:
    BtOgreCollisionWorld_initializer(btCollisionDispatcher *&dispatcher,
        btAxisSweep3 *&broadphase,
        btDefaultCollisionConfiguration *&collisionConfiguration)
    {
        if (!worldAabbMin) worldAabbMin = btVector3(-1000,-1000,-1000);
        if (!worldAabbMax) worldAabbMax = btVector3(1000,1000,1000);
        if (!collisionConfiguration)
            collisionConfiguration = new btDefaultCollisionConfiguration();
        if (!dispatcher)
            dispatcher = new btCollisionDispatcher(collisionConfiguration);
        if (!broadphase) broadphase = new btAxisSweep3(worldAabbMin,worldAabbMax);
    };
};

class BtOgreCollisionWorld: public BtOgreCollisionWorld_initializer,
        public btCollisionWorld {
public:
    //using btCollisionWorld::btCollisionWorld;
    BtOgreCollisionWorld(BtOgre::DebugDrawer *dbgdraw,
        btCollisionDispatcher* dispatcher = 0, btAxisSweep3* broadphase = 0,
        btDefaultCollisionConfiguration* collisionConfiguration = 0)
        : BtOgreCollisionWorld_initializer(dispatcher, broadphase,
            collisionConfiguration),
        btCollisionWorld(dispatcher, broadphase, collisionConfiguration)
    {
        /*copying variables above*/
        this->setDebugDrawer(dbgdraw);
    };
protected:
    btDefaultCollisionConfiguration* collisionConfiguration;
    btCollisionDispatcher* dispatcher;
    btVector3 worldAabbMin;
    btVector3 worldAabbMax;
    btAxisSweep3* broadphase;
};

这背后的想法是首先调用 "initializer" class 构造函数并覆盖 btCollisionWorld class.

的变量

突然间它起作用了。

我知道这似乎是试图开始毫无意义的讨论,但我不擅长 C++,而且在谷歌搜索时确实没有找到类似的东西;所以我很好奇是否存在任何可能的陷阱或其他实现方法。

好像太复杂了。从设置一些硬编码值的 class 继承看起来不太好。如果您真的希望派生的 class 在逻辑上是分开的(基于 init 参数),最好将其模板化。它将允许您根据不同的初始化参数创建更多这样的 classes。

这是我的理解(如果我错了告诉我,我会删除答案)

你的设计好像很复杂。起初我以为你没有利用继承权。但后来我意识到问题应该更复杂。这是我的理解:

你有一个基础 class,它的构造函数依赖于它的环境(例如一些外部变量)。我尝试一个更简单的例子:

Dispatcher* dispatcher = new Dispatcher(xyz);  // global parameter
class World {
private: 
    Dispatcher *my_dispatcher;     // some private that you can't change later on
public: 
    World() { 
        my_dispatcher = dispatcher;  // take over the global.  
        ...
    }
    ...
};

然后您想要派生一个 class,其中的构造函数参数应该影响基础 class 的环境。

class DWorld : public World {
public: 
    DWorld(Dispatcher* d) : World() { 
        dispatcher = d;  // too late:  the base is already created !!!    
        ...
    }
    ...
};

此逻辑在标准中定义:

  • 基础 class 部分总是在派生 class
  • 之前构造
  • 首先执行基础初始化器(可能初始化基础,如果不是默认基础构造函数)然后是其他成员,然后才执行构造函数的主体指令。

简而言之:在初始化基础之前,DWorld 无法做任何事情!

你的技巧是这样的:

所以你的技巧是使用多重继承。很聪明!

  class Helper {
  public: 
      Helper (Dispatcher* d) {
           dispatcher = d; 
           // do whatever you want to do before World base is intialized    
           }
  };
  class DWorld : public Helper, public World {
public: 
    DWorld(Dispatcher* d) : Helper(d), World() { 
        ...  // everything is fine
    }
    ...
};

这不是巧合:这在 C++ 标准中也有非常精确的定义:在多重继承的情况下,基初始化器按照您在继承语句中定义它们的顺序运行。所以在这里,首先 Helper 然后 World

然而,您随后必须处理多重继承,这可能很困难,尤其是当继承树的多个不同层需要同一个助手时。

再来一招:

不幸的是,它只能在一种情况下工作:您的基本构造函数必须至少采用一个参数。

假设我有一个构造函数 World(int a)。在这种情况下,我可以使用辅助函数来避免多重继承:

int helper(Dispatcher*d, int a) {
    dispatcher = d;  
    return a; 
}   
class DWorld : public World {   // single inheritance 
public:
DWorld(Dispatcher* d) : World(helper(0)) { }   // but function pipeline !
};

这是因为必须首先评估函数才能调用 World 构造函数这一事实。

带有 lambda 函数的变体,虽然可读性较差,但允许您捕获作用域中的任何变量并以最合适的方式使用它们:

DWorld(Dispatcher* d) : World([&]()->int {dispatcher = d; return 0;  }()) {...}

结论

这里有一些工具可以解决您的紧迫问题。然而诡计终归是诡计。这是一种解决方法。最好的解决方案是重新设计基础 class。