基类到派生类类型转换

Posted

技术标签:

【中文标题】基类到派生类类型转换【英文标题】:Base-to-derived class typecast 【发布时间】:2009-08-13 22:14:54 【问题描述】:

我有一个基类:

class RedBlackTreeNode

// Interface is the same as the implementation
public:
    RedBlackTreeNode* left;
    RedBlackTreeNode* right;
    RedBlackTreeNode* parent;
    Color color;
    TreeNodeData* data;

    RedBlackTreeNode():
        left(0),
        right(0),
        parent(0),
        color(Black),
        data(0)
    
    
    // This method is to allow dynamic_cast
    virtual void foo()
    
    
;

以及从它派生的一个:

class IndIntRBNode : public RedBlackTreeNode

public:
    IndIntRBNode* left;
    IndIntRBNode* right;
    IndIntRBNode* parent;
    IndexedInteger* data;
    IndIntRBNode():
        RedBlackTreeNode(),
        left(0),
        right(0),
        parent(0),
        color(Black),
        data(0)
    
    
;

root() 和 rootHolder 定义在 RedBlackTree 类中:

class RedBlackTree 

public:
    RedBlackTreeNode rootHolder;
    RedBlackTreeNode* root()
    
        return rootHolder.left;
    
    ...

然后我尝试进行类型转换:

IndIntRBNode *x, *y, *z;

z = dynamic_cast<IndIntRBNode*>(root());

而“z”只是变成了一个零指针,这意味着类型转换失败了。 那么它有什么问题,我该如何修复它以将“z”作为指向 IndIntRBNode 的指针进行引用?

补充:rootHolder.left 的初始化是这样的:

    int n = atoi(line + i);

    tree.clear();
    int j = 0;
    if (n > 100)
    
        n = 100;        // Maximum 100 nodes
    
    while (j < n)
    
        IndexedInteger num(j,j + 10);
        RedBlackTreeNode* node;
        int c = tree.search(&num, tree.root(), &node);
        if (c != 0)
         // if value is not in the tree
            IndexedInteger* v = new IndexedInteger(num);
            tree.insert(node, v, c);
            ++j;
        
    

换句话说,它是在“while”的第一次迭代时通过“insert”方法以这种方式初始化的:

void RedBlackTree::insert(
    RedBlackTreeNode* parentNode,
    TreeNodeData* v,
    // If it's negative, then add as the left son, else as the right
    int compare
)

    assert(parentNode != 0 && compare != 0);
    RedBlackTreeNode* x = new RedBlackTreeNode;
    x->data = v;
    x->parent = parentNode;
    // If it's root
    if (parentNode == &rootHolder)
    
        // Then it must be black
        x->color = Black;
    
    else
    
        // Leaf must be red
        x->color = Red;
    
    if (compare < 0)
    
        // Insert the node as the left son
        assert(parentNode->left == NULL);
        parentNode->left = x;
    
    else
    
        // Insert the node as the right son
        assert(parentNode != &rootHolder && parentNode->right == NULL);
        parentNode->right = x;
    

    ++numNodes;

    if (x != root())
    
        rebalanceAfterInsert(x);
    

实际上是问题所在:“插入”动态创建了 RedBlackTreeNode,因此它不可能是 IndIntRBNode。 我真的把它初始化错了,但是我怎么能派生基类而不是从头开始编写它的整个实现来改变类型呢? 我真的必须重写派生类中的所有“类型相关”方法吗?这似乎很愚蠢,我认为应该有另一种方式 - 类派生和类型转换的东西,不是吗?

【问题讨论】:

rootHolder.left 是如何初始化的? 【参考方案1】:

你确定 RedBlackTree::rootHolder.left 已经初始化了吗?

我认为您在某个地方初始化了 IndIntRBNode::left,但是当您访问 RedBlackTree::rootHolder.left 时,您访问的是 RedBlackTreeNode::left,这不是同一个字段。

【讨论】:

这可能是原因。或者可能 RedBlackTree::rootHolder.left 被初始化为错误的类型。 更新了问题:我确实做到了,但是我怎样才能派生出基类而不是为了改变类型而从头开始编写它的整个实现呢? 是否需要用新类型重新声明派生实例中的字段?实现应该仍然允许您将 IndIntRBNode 对象存储在基中声明的字段中,您只需要在想要使用它们时向下转换它们。 没错。完全摆脱 IndIntRBNode 中的成员,只使用 RedBlackTreeNode 中已经存在的成员。由于 IndIntRBNode 派生自 RedBlackTreeNode,因此您可以将 IndIntRBNode 对象分配给 RedBlackTreeNode 指针。 您可以将 RedBlackTreeNode 的任何后代存储在 RedBlackTreeNode* 字段中。如果你想限制 IndIntRBNode 只保存 IndIntRBNodes 你需要编写一个访问函数 RedBlackTreeNode::SetLeft(RedBlackTreeNode* aleft) ,它在 IndIntRBNode 中被覆盖以对它的参数进行类型检查。【参考方案2】:

等等 .. 你有一个派生类,其类型与基类同名。那是怎么编译的?

rootHolder.left 是如何初始化的?因为如果 dynamic_cast 失败,那么它不是 IndIntRBNode 类型。

基本上你没有提供足够的代码来证明你做错了什么,但你做错了什么。

【讨论】:

你说的“同名”是什么意思?检查代码两次,没有名称冲突。不过谢谢,我会检查初始化。 你有一个指向基类中名为 left 的变量和派生类中的变量的指针。你真的不应该这样做。只需使用基类中的那些。 RedBlackTree p = new IndIntRBNode;完全有效。【参考方案3】:

如果我正确阅读了您的代码,那么您根本没有将 rootHolder.left 设置为 IndIntRBNode 指针,而是一个普通的 RedBlackTreeNode

另外,你不需要为多态性编造一个无用的虚函数;只需将析构函数声明为virtual 并提供一个空实现。您对多态性的使用也不是很好,因为IndIntRBNode 成员将隐藏RedBlackTreeNode 成员,因此它们可能指向不同的事物,具体取决于您是通过IndIntRBNode 指针/引用还是@ 访问它们987654328@ 指针/引用。

【讨论】:

【参考方案4】:

无论您的示例代码可能存在或不存在任何其他缺陷,它的编写方式似乎都误解了继承和强制转换的工作原理。我建议您选择一本 C++ 书籍并阅读它。

特别是,您应该知道每个对象都是一种且只有一种类型。一旦创建了一个对象,它就永远不会改变类型。转换指针不会将对象从一种类型转换为另一种类型,也不会创建新类型的新对象。转换允许您根据指定的类类型使用现有对象,但前提是该对象已经属于该类,或者是从该类派生的类。

在你目前有这条线的地方:

z = dynamic_cast<IndIntRBNode*>(root());

试试这个:

RedBlackTreeNode* other_z = root();

如果other_z不为NULL,那么root()就不是IndIntRBNode,世界上所有的dynamic_casting都不会把它合二为一。

【讨论】:

以上是关于基类到派生类类型转换的主要内容,如果未能解决你的问题,请参考以下文章

从基类到不同派生类的多重继承转换

QObject 基类到继承对象的 C++ 类型转换

C#类型转换

Part7 继承与派生 7.3基类与派生类类型转换

C++ 类型将基础对象转换为派生对象

C++将派生类赋值给基类(向上转型)