在不调用未定义行为的情况下在成员函数中检查此== NULL

时间:2013-05-04 11:24:10

标签: c++ recursion static-methods

假设我有一个C ++类,我希望有一个递归的成员函数,该函数使用该类的实例项调用,例如

// the eplicit "this" is just for clarity in the following code:
void recursivePrintTree(){
    (if this == NULL){ // We are "out" of the tree
        return; 
    }
    cout << this->val;
    (this->leftSon)->printBinaryTree();
    (this->rightSon)->printBinaryTree();
}

问题当然是首先通过调用printBinary来调用未定义的行为!所以我想避免这种情况,据我所知,我至少有三种方法可以避免这种情况:

1)使用静态成员函数,它获得可以安全检查的显式this-type参数。这实际上是我到目前为止所做的,但因为它是一个非常递归的实现,几乎所有的成员函数都被编码为静态。那不是很好,对吗?

2)在使用NULL指针进行另一次递归调用之前,检查 next 节点的停止条件,可能为“this”。这是一种不太自然的写作形式,实际上还会检查其他项目。我想避免它。

3)使用默认的虚拟值。尝试过,感觉它并没有真正拯救我任何特殊情况的治疗,但这可能只是因为我的树的通用性。

我一直在为这件事烦恼一段时间,所以非常感谢任何好的建议。

3 个答案:

答案 0 :(得分:2)

你的代码错了。

您可以在this中检查NULL,而不是在this->next中检查NULL,这样就可以避免在第一时间调用NULL指针的方法。

即代替:

void printBinaryTree() {
    if(this == NULL){
       return;
    }
    cout << this->val;
    this->next->printBinaryTree();
}

这样做:

void printBinaryTree() {
    cout << this->val;
    if(this->next)
        this->next->printBinaryTree();
}

顺便说一句。这是一个链表。

答案 1 :(得分:1)

第二个解决方案是唯一的 if 解决方案 从节点结构中导航。通常的解决方案, 然而,是区分节点和树,以及 导航代码是树对象的成员,而不是节点。 该节点最多具有返回下一个指针的功能。 这意味着naviagtion函数会指向 节点;您的printBinaryTree可能类似于:

void
BinaryTree::print( Node const* node )
{
    if ( node != NULL ) {
        node->print();
        print( node->next() );
    }
}

或者您可以使用分隔树的访客模式 从每个节点的动作中走代码。

答案 2 :(得分:0)

让我们试试你的实现:

#include <iostream>

class BinaryTree {
    public:
        BinaryTree(int value, BinaryTree * left, BinaryTree * right) : value_(value), left_(left), right_(right) {}

        void printBinaryTree(int depth = 0) {

            for ( int i = 0; i < depth; i++ ) std::cout << "  ";

            if ( this == NULL ) {
                std::cout << "Null node, returning..." << std::endl;
                return;
            }
            else {
                std::cout << value_ << std::endl;
            }

            left_->printBinaryTree(depth+1);
            right_->printBinaryTree(depth+1);
        }

    private:
        int value_;
        BinaryTree * left_;
        BinaryTree * right_;
};

int main() {
    BinaryTree leaf(0,NULL,NULL);
    BinaryTree top(1,&leaf, &leaf);

    top.printBinaryTree();

    return 0;
}

如果我们进行此操作,我们会得到一个如下所示的输出:

1
  0
    Null node, returning...
    Null node, returning...
  0
    Null node, returning...
    Null node, returning...

此处解释了其工作原因:Accessing class members on a NULL pointer

但是,根据C ++标准,这样做是未定义的行为。同样,这只是因为你的,或者在本例中我的编译器的实现能够使这项工作。它不是任何形式的保证,这会降低您的可移植性,如果您需要更新编译器,甚至可能会停止工作!

有很多替代方案。你已经列出了一些,但我必须说我不喜欢静态实现,因为从设计的角度来看它并没有真正意义,并且使你的所有代码都变得混乱。另一种解决方案可以是使printBinaryTree函数为虚拟,并将叶节点定义为树的子类。这是一个例子:

#include <iostream>

class BinaryTree {
    public:
        BinaryTree(int value, BinaryTree * left, BinaryTree * right) : value_(value), left_(left), right_(right) {}

        virtual void printBinaryTree(int depth = 0) {

            for ( int i = 0; i < depth; i++ ) std::cout << "  ";

            std::cout << value_ << std::endl;

            left_->printBinaryTree(depth+1);
            right_->printBinaryTree(depth+1);
        }

        int getValue() { return value_; }

    private:
        int value_;
        BinaryTree * left_;
        BinaryTree * right_;
};

class BinaryTreeLeaf : public BinaryTree {
    public:
        BinaryTreeLeaf(int value) : BinaryTree(value, NULL, NULL) {}

        virtual void printBinaryTree(int depth=0) {
            for ( int i = 0; i < depth; i++ ) std::cout << "  ";

            std::cout << getValue() << std::endl;
        }

};

int main() {
    BinaryTreeLeaf leaf(0);
    BinaryTree top(1,&leaf, &leaf);

    top.printBinaryTree();

    return 0;
}

根据需要,此处的输出为:

1
  0
  0