私人和公共职能 我试图教自己C++中的课程,我遇到了一个绊脚石,我似乎无法弄清楚。我希望有人能给我指出正确的方向
我决定构造一个小的私人和公共职能 我试图教自己C++中的课程,我遇到了一个绊脚石,我似乎无法弄清楚。我希望有人能给我指出正确的方向,c++,class,oop,coding-style,C++,Class,Oop,Coding Style,我决定构造一个小的树类,它构造一个新的BST。我希望能够在我的对象上调用某些方法,如下所示: int main() { Tree<int> tree1; tree1.insert(5); int treeMin = tree1.minValue(); int treeMax = tree1.maxValue(); tree1.printTree(); } 为了避免这种冗余,我定义了同一函数的公共和私有版本。这样,公共职能部门就称之为私人职能部门 templ
树
类,它构造一个新的BST。我希望能够在我的对象上调用某些方法,如下所示:
int main() {
Tree<int> tree1;
tree1.insert(5);
int treeMin = tree1.minValue();
int treeMax = tree1.maxValue();
tree1.printTree();
}
为了避免这种冗余,我定义了同一函数的公共和私有版本。这样,公共职能部门就称之为私人职能部门
template<class T>
class Tree {
private:
treeNode<T>* root;
treeNode<T>* newNode(T data);
void insert(treeNode<T>*& root, T data);
int minValue(treeNode<T>*& root);
int maxValue(treeNode<T>*& root);
void printTree(treeNode<T>*& root);
public:
Tree();
~Tree();
void insert(T data);
int minValue();
int maxValue();
void printTree();
};
模板
类树{
私人:
树根;
树节点*新节点(T数据);
无效插入(treeNode*&根,T数据);
int最小值(treeNode*&根);
int最大值(treeNode*&根);
无效打印树(treeNode*&根);
公众:
树();
~Tree();
无效插入(T数据);
int minValue();
int maxValue();
void printree();
};
然后,举个例子:
template<class T>
int Tree<T>::minValue() { minValue(root); }
template<class T>
int Tree<T>::minValue(treeNode<T>*& root) {
if (root == NULL) { return 0; }
if (root->left == NULL) { return root->data; }
else { minValue(root->left); }
}
模板
int-Tree::minValue(){minValue(根);}
模板
int-Tree::minValue(treeNode*&根){
如果(root==NULL){返回0;}
如果(root->left==NULL){返回root->data;}
else{minValue(根->左);}
}
所以,我的问题是:
如果我以递归方式编写函数,我知道我需要声明一个接受参数的私有函数,但这被认为是一种糟糕的风格吗?这是草率的吗
谢谢你的帮助 代码中的
私有
成员函数只是一个不必要的复杂问题。我会把他们的代码移到公共成员函数中:代码更少,代码更干净,间接性更少,所以代码更直接,所有这些都很好。对于其中一些,您可能会通过在详细信息
命名空间中释放函数来支持重用,但我认为这将是过早的泛化,将精力花在可能不会发生的重用上
答案末尾的示例代码
关于另一个设计问题,声明
int minValue();
int maxValue();
排除对const
对象调用这些成员函数。取而代之的是
int minValue() const;
int maxValue() const;
第三个问题,这通常是一个非常糟糕的主意™ 在非i/o类中执行i/o。如果将树打印为标准输出,您将如何在GUI程序中使用该类?因此,与其
void printTree();
做某事
ostream& operator<<( ostream& stream ) const;
第四个问题,你需要负责复制——阅读“三法则”和“零法则” 最简单的方法就是更换
treeNode<T>* root;
强烈建议每个节点存储一个可隐式转换为int
的值。您不提供treeNode
的声明。但这看起来像是一个设计级的bug,其目的是让minValue
返回T
,而不是int
——而maxValue
也是如此
一个非常小的编码问题(不是设计级别):在C++11及更高版本中,您应该优先使用
NULL ptr
,而不是NULL
可以通过参数转发函数自由传递,而nullptr
则会衰减为整数类型,因为NULL
只是整数类型的零常量NULL
不要求包含任何标题,而nullptr
由标题定义,即使用NULL
可以避免标题依赖性nullptr
最后,关于
if (root == NULL) { return 0; }
对于minValue
,这当然可能是设计意图。但您可能希望发出失败信号,或者将调用视为逻辑错误
- 要将调用视为错误,
并为客户端代码提供检查空树的方法assert(root!=nullptr)
- 若要发出失败信号,请返回具有可选值的对象(例如,如
或Barton/Nackmann的原始boost::optional
),或抛出异常(std::runtime_error类是一个良好的常规默认异常类选择)Fallible
- 也可以将这两种方法结合起来,同时提供这两种方法,可以使用
和minValue
这样的名称minValueOrX
std::numeric\u limits::min()
。但这使得代码变得脆弱,因为这样的值很容易在数据中自然出现,而且客户机代码很容易无法检查特殊值
为C++11编写的示例:
#include <assert.h>
#include <iostream> // std::cout, std::endl
#include <string> // std::string
namespace my {
using std::string;
template<class T>
class Tree
{
private:
struct Node
{
T value;
Node* p_left;
Node* p_right;
auto to_string() const -> string
{
using std::to_string;
string const left = (p_left == nullptr? "" : p_left->to_string());
string const right = (p_right == nullptr? "" : p_right->to_string());
return "(" + left + " " + to_string( value ) + " " + right + ")";
}
~Node() { delete p_left; delete p_right; }
};
Node* root_;
Tree( Tree const& ) = delete;
Tree& operator=( Tree const& ) = delete;
public:
auto is_empty() const -> bool { return (root_ == nullptr); }
void insert( T const data )
{
Node** pp = &root_;
while( *pp != nullptr )
{
auto const p = *pp;
pp = (data < p->value? &p->p_left : &p->p_right);
}
*pp = new Node{ data, nullptr, nullptr };
}
auto minValue() const -> T
{
assert( root_ != nullptr );
Node* p = root_;
while( p->p_left != nullptr ) { p = p->p_left; }
return p->value;
}
auto maxValue() const -> T
{
assert( root_ != nullptr );
Node* p = root_;
while( p->p_right != nullptr ) { p = p->p_right; }
return p->value;
}
auto to_string() const -> string
{
return (root_ == nullptr? "" : root_->to_string());
}
~Tree() { delete root_; }
Tree(): root_( nullptr ) {}
Tree( Tree&& other ): root_( other.root_ ) { other.root_ = nullptr; }
};
} // namespace my
auto main() -> int
{
my::Tree<int> tree;
for( int const x : {5, 3, 4, 2, 7, 6, 1, 8} )
{
tree.insert( x );
}
using std::cout; using std::endl;
cout << tree.to_string() << endl;
cout << "min = " << tree.minValue() << ", max = " << tree.maxValue() << endl;
}
#包括
#包括//std::cout,std::endl
#include//std::string
名称空间我的{
使用std::string;
模板
类树
{
私人:
结构体类型
{
T值;
节点*p_左;
节点*p_右;
自动转换为字符串()常量->字符串
{
使用std::to_字符串;
字符串常量left=(p_left==nullptr?“:p_left->to_string());
字符串常量right=(p_right==nullptr?“:p_right->to_string());
返回“(“+left+”+to_字符串(值)+“+right+”)”;
}
~Node(){delete p_left;delete p_right;}
};
节点*根;
树(树常数&)=删除;
树和运算符=(树常数&)=删除;
公众:
auto is_empty()const->bool{return(root_==nullptr);}
无效插入(T常量数据)
{
节点**pp=&root;
而(*pp!=nullptr)
{
自动常数p=*pp;
pp=(数据值?&p->p_左:&p->p_右);
}
*pp=新节点{data,nullptr,nullptr};
}
自动最小值()常量->T
{
断言(root_!=nullptr);
节点*p=根;
而(p->p_left!=nullptr){p=p->p_left;}
返回p->value;
}
自动最大值()常量->T
unique_ptr< treeNode< T > > root;
template<class T>
int Tree<T>::minValue(treeNode<T>*& root) {
if (root == NULL) { return 0; }
if (root->left == NULL) { return root->data; }
else { minValue(root->left); }
}
if (root == NULL) { return 0; }
#include <assert.h>
#include <iostream> // std::cout, std::endl
#include <string> // std::string
namespace my {
using std::string;
template<class T>
class Tree
{
private:
struct Node
{
T value;
Node* p_left;
Node* p_right;
auto to_string() const -> string
{
using std::to_string;
string const left = (p_left == nullptr? "" : p_left->to_string());
string const right = (p_right == nullptr? "" : p_right->to_string());
return "(" + left + " " + to_string( value ) + " " + right + ")";
}
~Node() { delete p_left; delete p_right; }
};
Node* root_;
Tree( Tree const& ) = delete;
Tree& operator=( Tree const& ) = delete;
public:
auto is_empty() const -> bool { return (root_ == nullptr); }
void insert( T const data )
{
Node** pp = &root_;
while( *pp != nullptr )
{
auto const p = *pp;
pp = (data < p->value? &p->p_left : &p->p_right);
}
*pp = new Node{ data, nullptr, nullptr };
}
auto minValue() const -> T
{
assert( root_ != nullptr );
Node* p = root_;
while( p->p_left != nullptr ) { p = p->p_left; }
return p->value;
}
auto maxValue() const -> T
{
assert( root_ != nullptr );
Node* p = root_;
while( p->p_right != nullptr ) { p = p->p_right; }
return p->value;
}
auto to_string() const -> string
{
return (root_ == nullptr? "" : root_->to_string());
}
~Tree() { delete root_; }
Tree(): root_( nullptr ) {}
Tree( Tree&& other ): root_( other.root_ ) { other.root_ = nullptr; }
};
} // namespace my
auto main() -> int
{
my::Tree<int> tree;
for( int const x : {5, 3, 4, 2, 7, 6, 1, 8} )
{
tree.insert( x );
}
using std::cout; using std::endl;
cout << tree.to_string() << endl;
cout << "min = " << tree.minValue() << ", max = " << tree.maxValue() << endl;
}
(((( 1 ) 2 ) 3 ( 4 )) 5 (( 6 ) 7 ( 8 )))
min = 1, max = 8