我有一个类型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 Something
,Node * s = new Something
,Something * s = new Something
或许多可能的变化中的任何一种?
我的第二个问题是这个设计是不是一个好主意。我确实认为,为父母传递Object *
有点没有意义,因为只有Node
可以是父母,但Node
本身将其子女存储为Object *
因为并非所有儿童都来自Node
。当然,欢迎任何使这更优雅的建议。
答案 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)
如果您知道 parent
是Node
,那么static_cast
会做正确的事。
但如果您知道 parent
是Node
,那么为什么不更改构造函数(可能是您在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* parent
和Node* 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();
}