C ++继承类,将修改后的参数发送到父级的构造函数

时间:2015-02-19 18:35:18

标签: c++ class pointers inheritance constructor

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

一开始我有:

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;
};

这背后的想法是"初始化器"首先调用类构造函数并覆盖btCollisionWorld类的变量。

突然间它奏效了。

我理解这似乎是尝试开始一个毫无意义的讨论,但我对c ++并不擅长,并且在搜索它时真的没有找到类似的东西;所以我只是好奇是否有任何可能的陷阱或其他方法来实现这一点。

2 个答案:

答案 0 :(得分:2)

看起来太复杂了。从设置一些硬编码值的类继承并不是很好。如果你真的希望派生类在逻辑上是分开的(基于init参数),那么最好对它进行模板化。它将允许您基于不同的初始化参数创建更多此类。

答案 1 :(得分:1)

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

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

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

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 DWorld : public World {
public: 
    DWorld(Dispatcher* d) : World() { 
        dispatcher = d;  // too late:  the base is already created !!!    
        ...
    }
    ...
};

该逻辑在标准中定义:

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

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

以下是您的诀窍如何运作:

所以你的诀窍就是使用多重继承。它很聪明!

  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;  }()) {...}

结论

你有一些工具可以解决你的即时问题。然而,技巧仍然是一个技巧。这是一种解决方法。他最好的解决方案是重新设计基类。