我应该避免在现代 C++ 中引用指针还是在这种特殊情况下可以
Posted
技术标签:
【中文标题】我应该避免在现代 C++ 中引用指针还是在这种特殊情况下可以【英文标题】:Should I avoid reference to pointer in modern C++ or it is okay in this special case 【发布时间】:2019-02-03 19:16:50 【问题描述】:我知道,除非真的需要,否则必须避免使用现代 C++ 中的原始指针(或更准确地说是 new
和 delete
)。我相信在图形实现方面,指针非常有用。不过,我很高兴听到 cmets 对此的看法。
我在玩二叉搜索树并编写insert
函数。我使用两种不同但相似的方法来实现,一种使用指针,另一种使用指针引用。显然我想更改指针本身(参考 ptr)。想知道它们中的任何一个是否有优点或者不应该使用并且必须使用 const 引用或智能指针(现代方式)编写。
Node *insert_ptr(Node *root, int data)
if (root == NULL)
return create(data);
else if (data < root->data)
root->left = insert(root->left, data);
else
root->right = insert(root->right, data);
return root;
void insert_ref_to_ptr(Node *&root, int data)
if (root == NULL)
root = create(data);
else if (data < root->data)
insert(root->left, data);
else
insert(root->right, data);
如果您想试一试,我将在下面提供其余代码。您可能想使用1 3 6 8 10 14 13 4 7
作为输入
struct Node
int data;
Node *left;
Node *right;
;
void display(Node *root)
if (root != NULL)
display(root->left);
cout << root->data << " ";
display(root->right);
Node *create(int data)
Node *node = new Node();
node->data = data;
node->left = node->right = NULL;
return node;
int main()
Node *root = NULL;
vector<int> nodes;
string line;
int value;
getline(cin, line);
stringstream split(line);
while (split >> value)
nodes.push_back(value);
/*
root = insert_ptr(root, nodes[0]);
for (int i = 1; i < nodes.size(); i++)
insert_ptr(root, nodes[i]);
*/
for (int i = 0; i < nodes.size(); i++)
insert_ref_to_ptr(root, nodes[i]);
display(root);
return 0;
【问题讨论】:
绝对不是一个好的设计。所有这些函数都应该在类中,左右应该是唯一的指针。 你绝对应该避免使用原始指针,除非你 100% 确定你会需要它们。 @MatthieuBrucher 绝对正确。但我不关心这里的设计。 那么这个问题有什么意义呢? 您问的是二叉树、封装还是良好的 C++ 实践?如果你想new/delete,你必须完全封装它。不过,您没有理由在这里进行新建/删除。 【参考方案1】:that pointers in modern C++ must be avoided unless really needed
错了。
应避免使用new
和delete
,而应使用唯一和共享指针。
但如果您不转移所有权,您仍将通过引用或原始指针传递对象。
您的Node
具有所有权关系,因此应该是:
struct Node
int data;
std::unique_ptr<Node> left;
std::unique_ptr<Node> right;
;
或
struct Node
int data;
std::shared_ptr<Node> left;
std::shared_ptr<Node> right;
;
您的display
函数不需要所有权,因此您将节点作为指针(或引用)传递
void display(Node *root)
if (root != nullptr)
display(root->left);
cout << root->data << " ";
display(root->right);
由于您不打算更改它,所以我会使用 const ref:
void display(const Node &root)
if(root.left != nullptr)
display(*root.left);
cout << root.data << " ";
if(root.right != nullptr)
display(*root.right);
你insert_ref_to_ptr
是一个非常糟糕的构造,因为不清楚它是否转移了任何所有权,也不清楚它调用create(data)
在其中使用new
创建一个节点。
创建看起来像这样:
std::unique_ptr<Node> create(int data)
auto node = std::make_unique<Node>();
node->data = data;
return std::move(node);
insert_ref_to_ptr
函数类似:
void insert_ref_to_ptr(std::unique_ptr<Node> &root, int data)
if (root == nullptr)
root = std::move(create(data));
else if (data < root->data)
insert(root->left, data);
else
insert(root->right, data);
【讨论】:
你自己的insert
函数会是什么样子?
非常有兴趣了解您将如何实现整个事情。如果可以的话,这将是非常有教育意义和帮助的。
@KarimSole 我将如何实现该树,取决于用例。但是您的问题更多的是关于是否仍应使用指针,以及对指针的引用和引用。是的,应该,但你用它做什么总是很重要的。我建议你看一下 “Leak-Freedom in C++... By Default.” of Herb Sutter 的谈话(或他或 Titus Winter 的任何其他谈话)
好的,先生。对于我的具体案例,我很想看看有经验的开发人员如何适合这个简单的用例。请您在最后一部分努力。高度赞赏。
是我理解有误,还是display
在这里引用有误?树结构的通常模式是叶节点将left
和right
作为nullptr
,在这种情况下,display
中必要的取消引用将调用 UB。 (root != NULL
检查也没有多大意义。)【参考方案2】:
我知道在现代 C++ 中必须避免使用指针,除非真的需要。
那不是真的。您可能听说过,如果您的要求符合标准化模式/解决方案,例如unique_ptr
,您应该避免使用原始(裸)指针。
显然我想更改指针本身(参考 ptr)。想知道它们中的任何一个是否有优点或者不应该使用并且必须使用 const 引用(现代方式)编写。
如果你想有输出参数,你有很多方法可以做到:
返回值。 对值的引用(作为参数)。 指向值的指针(作为参数)。struct
、std::tuple
中的多个返回值或使用结构化绑定。
成员变量(仅适用于类中的成员函数)。
全局变量。
没有一种适用于所有情况的最佳方法。您使用哪一个取决于您以及您将如何使用该功能。在某些情况下,它还取决于项目的编码风格指南(例如,在引用与指针的情况下)。例如:
如果只有一个输出参数,有些人更喜欢使用返回值。 一些库更喜欢将返回值专门用于错误代码。 一些编码风格指南避免引用,因为他们希望在调用者中明确看到您正在传递地址(例如f(a)
与 f(&a)
)。
在成员函数中,您通常使用成员变量。
在几乎所有情况下,您都应该避免使用全局变量。
必须使用 const 引用(现代方式)编写。
顺便提一下,你写的不是const
引用,只是Note *
的引用。而且它不是“现代 C++”:引用是第一个 C++ 标准的一部分。
【讨论】:
基本上没有偏好或最佳实践?! 对于一般情况,不,不是。 确实很有帮助。如果您有机会使用自己的风格编写insert
函数,我会非常激动。很好奇专家会怎么写这样的案例。
@KarimSole 在 C 中,您的第一个界面可能是最流行的解决方案(返回指针)。在 C++ 中,几乎每个人都会设计一个类来封装数据结构(参见标准 C++ 库中的示例)——因此,insert
将是一个成员函数。以上是关于我应该避免在现代 C++ 中引用指针还是在这种特殊情况下可以的主要内容,如果未能解决你的问题,请参考以下文章