假设我有一个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)使用默认的虚拟值。尝试过,感觉它并没有真正拯救我任何特殊情况的治疗,但这可能只是因为我的树的通用性。
我一直在为这件事烦恼一段时间,所以非常感谢任何好的建议。
答案 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