leftSon)->printbarytree(); (this->rightSon)->printbarytree(); },c++,recursion,static-methods,C++,Recursion,Static Methods" /> leftSon)->printbarytree(); (this->rightSon)->printbarytree(); },c++,recursion,static-methods,C++,Recursion,Static Methods" />

在成员函数中检查此==NULL而不调用未定义的行为 假设我有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(); } //为了清楚起见,以下代码中使用了eplicit“this”: void recursivePrintTree(){ (如果this==NULL){//我们“出”树了 返回; } 库特瓦尔; (this->leftSon)->printbarytree(); (this->rightSon)->printbarytree(); }

在成员函数中检查此==NULL而不调用未定义的行为 假设我有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(); } //为了清楚起见,以下代码中使用了eplicit“this”: void recursivePrintTree(){ (如果this==NULL){//我们“出”树了 返回; } 库特瓦尔; (this->leftSon)->printbarytree(); (this->rightSon)->printbarytree(); },c++,recursion,static-methods,C++,Recursion,Static Methods,问题当然是首先通过调用带有NULL的printBinary来调用未定义的行为!所以我想避免这种情况,据我所知,我至少有三种方法: 1) 使用静态成员函数,该函数获取可安全检查的显式this类型参数。这实际上是我到目前为止所做的,但是因为它是一个非常递归的实现,几乎所有的成员函数都被编码为静态的。那不太好,对吧 2) 检查下一个节点的停止条件,然后再执行另一个可能为“this”的空指针递归调用。这是一种不太自然的书写形式,实际上会检查除此之外的其他项目。我想避免它 3) 使用默认的虚拟值。我试过了

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

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

2) 检查下一个节点的停止条件,然后再执行另一个可能为“this”的空指针递归调用。这是一种不太自然的书写形式,实际上会检查除此之外的其他项目。我想避免它

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

我已经为这件事操心了一段时间了,所以如果有什么好的建议,我将不胜感激。

您的代码是错误的

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

也就是说,而不是:

void printBinaryTree() {
    if(this == NULL){
       return;
    }
    cout << this->val;
    this->next->printBinaryTree();
}
void printBinaryTree(){
if(this==NULL){
返回;
}
库特瓦尔;
此->下一步->打印二进制树();
}
这样做:

void printBinaryTree() {
    cout << this->val;
    if(this->next)
        this->next->printBinaryTree();
}
void printBinaryTree(){
库特瓦尔;
如果(本->下一步)
此->下一步->打印二进制树();
}

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

如果您愿意,第二个解决方案是唯一的解决方案 从节点结构中导航。通常的解决方案, 但是,是为了区分节点和树,以及 导航代码是树对象的成员,而不是节点。 节点最多有一个返回下一个指针的函数。 这意味着导航函数将使用指向 节点;您的
printBinaryTree
可能类似于:

void
BinaryTree::print( Node const* node )
{
    if ( node != NULL ) {
        node->print();
        print( node->next() );
    }
}
或者您可以使用访客模式,它将树分隔开
从每个节点的操作中提取步行代码。

让我们试试您的实现:

#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;
}
这里解释了这项工作的原因:

但是,按照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;
}

不能为
NULL
,因为至少您有一个调用该方法的对象。。。。这是什么语法?如果(!this)则编写
。它更为惯用。@jrok首先,
这个
永远不能为空,这是他的问题的关键。其次,检查空指针的唯一可接受的方法是将其与
null
0
(或
nullptr
,在C++11中)进行比较。隐式转换令人困惑,通常应该避免。@Jiwan:退后一步。根本问题是他在树上行走,而不是在节点上行走。树行走代码不属于
节点
,而是属于
(当然,它知道
节点
,因为它有
节点),我想他知道这一点。这就是他问这个问题的原因:他想知道避免空
这个
指针的最佳方法。谢谢。您善意地建议的解决方案是我在选项2中提到的,但是在第一次调用它之前,我必须在函数外部检查NULL。想象一下,有一个指向树的指针,它很可能是空的。然后,指针不是一个节点而是一个null,我需要在每次调用printTree()时检查它。我想避免这种情况。我同意访客模式对这种情况是合法的。
#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