gpt4 book ai didi

c++ - 私有(private)和公共(public)职能

转载 作者:行者123 更新时间:2023-11-30 01:16:55 26 4
gpt4 key购买 nike

我正在尝试自学 C++ 中的类,但我遇到了一些绊脚石,似乎无法清除。我希望有人能为我指明正确的方向。

我决定构造一个小的 Tree 类,它构造一个新的 BST。我希望能够像这样在我的对象上调用某些方法:

int main() {
Tree<int> tree1;

tree1.insert(5);

int treeMin = tree1.minValue();
int treeMax = tree1.maxValue();

tree1.printTree();
}

现在,为了调用这些函数,我定义了 publicprivate 函数,这样您就不会以冗余方式调用函数。例如:

(我要避免的)

int main() {
Tree<int> tree1;

tree1.insert(tree1, 5);

int treeMin = tree1.minValue(tree1);
int treeMax = tree1.maxValue(tree1);

tree1.printTree(tree1);
}

为了避免这种冗余,我定义了同一函数的公共(public)和私有(private)版本。通过这种方式,公共(public)函数调用它们的私有(private)函数。

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();
};

然后,举个例子:

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); }
}

所以,我的问题是:如果我递归地编写我的函数,我知道我需要声明一个接受参数的私有(private)函数,但这是否被认为是一种糟糕的风格?这是草率的吗?

感谢您的帮助!

最佳答案

private代码中的成员函数只是不必要的复杂化。我只想将他们的代码移至公共(public)成员函数:更少的代码,更简洁的代码,更少的间接性,因此更直接的 grokable 代码,一切都很好。对于它们中的一些,您可能通过在 details 中使它们成为自由函数来支持重用。命名空间,但我认为这将是过早的概括,将精力花在可能不会发生的可能重用上。

答案末尾的示例代码。


关于另一个设计问题,声明

int minValue();
int maxValue();

排除在 const 上调用这些成员函数目的。而是做

int minValue() const;
int maxValue() const;

第三个问题,在非 i/o 类中进行 i/o 通常是一个 Really Bad Idea™。如果将树打印到标准输出,您将如何在 GUI 程序中使用该类?所以,而不是

void printTree();

做例如

ostream& operator<<( ostream& stream ) const;

或者例如

string toString() const;

第四个问题,你需要负责复制——仔细阅读“三规则”和“零规则”。

最简单的方法就是替换

treeNode<T>* root;

unique_ptr< treeNode< T > > root;

哪里unique_ptrstd::unique_ptr .

或者至少声明一个复制构造函数和一个复制赋值运算符,或者从“不可复制”类继承。要使类有效不可复制,您可以使这些运算符 privateprotected .要使其可复制,请将它们设为 public并在每个中做正确的事情(复制赋值运算符的一个很好的默认实现是通过 copy-and-swap 习惯用复制构造来表达它,这意味着引入一个不抛出的 swap 函数)。


第五个问题是实现

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 的值.您没有提供 treeNode 的声明.但这看起来像是一个设计级别的错误,其目的是针对 minValue。返回 T ,不是 intmaxValue 同上.


一个非常小的编码问题(不是设计级别):在 C++11 和更高版本中你应该优先使用 nullptr , 不是 NULL .

  • nullptr可以通过参数转发函数自由传递,而NULL然后会衰减为整数类型,因为 NULL只是整数类型的零常数。

  • nullptr不需要您包含任何 header ,而 NULL由标题定义,即 nullptr你避免了 header 依赖。


最后,关于

if (root == NULL) { return 0; }

对于minValue ,这当然可能是意图,设计。但您可能想要发出失败信号或将调用视为逻辑错误。

  • 要将调用视为错误,assert( root != nullptr );并为客户端代码提供一种检查空树的方法。

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

  • 也可以将这两种方法结合起来,同时提供这两种方法,或许可以使用类似 minValue 的名称。和 minValueOrX .

更一般地说,有时可以保留一些特殊值作为“没有这样”的指示符。例如。 std::numeric_limits<T>::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;
}

输出:

(((( 1 ) 2 ) 3 ( 4 )) 5 (( 6 ) 7 ( 8 )))min = 1, max = 8

关于c++ - 私有(private)和公共(public)职能,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25587809/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com