在这种多态方案中使用static_cast是否安全?

时间:2013-11-15 13:53:21

标签: c++ design-patterns polymorphism hierarchy static-cast

我有一个类型heirarchy:

class Object {
...
};

class Node : public Object {
...
};

class Leaf : public Object {
...
};

class Something : public Node {
...
};

class SomethingElse : public Leaf {
...
};

换句话说,绝对每个类都直接或间接地继承Object

每个对象的构造函数采用以下格式:

ClassType(Object * parent)

但是,按设计,只有Node可以是父级,而Leaf是终止节点;

目前,在每个构造函数中,我正在执行以下操作:

        Node * p = dynamic_cast<Node *>(parent);
        if (p) p->adopt(this);
        else qDebug() << "for some reason cast failed";

所以,我的主要问题是 - 考虑到每个父对象只能是Node或从Node派生,使用static_cast是否安全?当然,主要关注的是性能,现在我每秒创建大约400万个节点,这似乎不是瓶颈,但是,这仍然是准系统层次结构,因为它填充了更多的逻辑,它会变得更慢,所以如果我可以摆脱dynamic_cast这将是一件好事。

我已经确认Something s &s == static_cast<Node *>(&s) == dynamic_cast<Node *>(&s)这是真的,但我不确定我是否可以“将所有鸡蛋放入那个篮子里”可以这么说 - 我保证能够“盲目地”将每个直接或间接从Node类派生的地址重用为Node *,无论它是否在堆栈上创建,Object * s = new SomethingNode * s = new SomethingSomething * s = new Something或许多可能的变化中的任何一种?

我的第二个问题是这个设计是不是一个好主意。我确实认为,为父母传递Object *有点没有意义,因为只有Node可以是父母,但Node本身将其子女存储为Object * 因为并非所有儿童都来自Node 。当然,欢迎任何使这更优雅的建议。

7 个答案:

答案 0 :(得分:1)

如果你担心这个演员,也许你应该将parent成员定义为Node?那你永远不需要施展。此外,您将确保始终使用Node作为父级。

答案 1 :(得分:1)

我在大学的语言教授告诉我们尽可能避免动态演员,每次我必须开发一个复杂的层次结构时,我都会尝试这样做。 话虽这么说,如果您知道完全 指向节点,那么静态投射是安全的。< / p>

答案 2 :(得分:1)

  

指向某个类D的成员的指针可以向上转换为指针   其基类B的成员。此static_cast不进行检查以确保   该成员实际存在于指向的运行时类型中   对象

简而言之,如果您知道D始终来自B,则使用static_cast是安全的。

如果性能是您关注的问题之一,我建议您简化(展平)您的层次结构。

答案 3 :(得分:1)

现在,由于某些逻辑原因,您知道父母将永远是Node,因此您应该使用static_cast

问题是,如果您的假设失败,则很难调试此代码。您可能想要做的是这样的事情:

 #ifdef NDEBUG
    Node * p = static_cast<Node*>(parent);
 #else
    Node * p = dynamic_cast<Node *>(parent);
    assert(p);
 #endif
    p->adopt(this);

使用boost::polymorphic_downcast可以轻松实现这一目标。

 Node * p = boost::polymorphic_downcast<Node*>(parent);

答案 4 :(得分:0)

如果您知道 parentNode,那么static_cast会做正确的事。

但如果您知道 parentNode,那么为什么不更改构造函数(可能是您在Object内保留的任何父指针)反映这一现实?

答案 5 :(得分:0)

如果您创建了一个虚拟函数,如果它是一个节点,则可以将object转换为node,您可以为演员表创建一个快速(O(1),一个虚拟调用)版本。如果成功,您将获得node*,如果失败,您将获得nullptr。此转换隐藏在虚拟调用的机制内,因此类型安全。

class node;
class object {
  // other things
public:
  node const* as_node() const { return 0; }
  node* as_node() { return 0; }
};

class node {
  // other things
public:
  node const* as_node() const { return this; }
  node* as_node() { return this; }
};

答案 6 :(得分:-1)

当您进行向下转换时(从对象到节点),您必须使用dynamic_cast。换句话说(节点到对象),static_cast没问题。

您必须使用dynamic_cast进行向下转换表明设计存在问题。

我不知道细节,但你可以改变构造函数。您可以选择Object* parentNode* parent

,而不是Leaf* parent

所以,改变:

struct A
{
  A( Object* parent );
};

到:

struct A
{
  A( Node* parent );
  A( Leaf* parent );
};

另外,我会使用引用而不是指针。


另一种选择是在所有类中实现adopt方法。在不需要它的类中,将使用默认的无操作操作,并在实际执行某些操作的类中覆盖它。那你就不需要施放了。

class Object {
  virtual void foo(){ /*no-op*/ }
};

class Node : public Object {
  virtual void foo(){ /* hi-ho */ }
};

class Leaf : public Object {
 // uses default foo
};

void callFoo( Object & obj )
{
  // no need to cast
  obj.foo();
}