私人和公共职能 我试图教自己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)
    并为客户端代码提供检查空树的方法

  • 若要发出失败信号,请返回具有可选值的对象(例如,如
    boost::optional
    或Barton/Nackmann的原始
    Fallible
    ),或抛出异常(std::runtime_error类是一个良好的常规默认异常类选择)

  • 也可以将这两种方法结合起来,同时提供这两种方法,可以使用
    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