使用智能指针实现容器

发布于 2024-10-27 19:31:41 字数 852 浏览 1 评论 0原文

好的,所以每个人都知道应该像瘟疫一样避免原始指针并更喜欢智能指针,但是这个建议在实现容器时适用吗?这就是我想要实现的目标:

template<typename T> class AVLTreeNode {
public:
    T data;
    unique_ptr<AVLTreeNode<T>> left, right;
    int height;
}

Unique_ptr 可以使容器函数编写起来更加麻烦,因为我不能让多个原始指针以优雅的方式临时指向同一个对象。例如:(

unique_ptr<AVLTreeNode<T>> rotate_right(unique_ptr<AVLTreeNode<T>> n1)
{
    unique_ptr<AVLTreeNode<T>> n2 = n1->left;

    n1->left = n2->right;
    n2->right = n1;
    // n1 must now be referenced through the longer name n2->right from now on
    n2->right->recalculate_height();
    n2->recalculate_height();

    return n2;
}

在这个例子中这不是什么大问题,但我可以想象它如何成为一个问题)。我是否应该将此类问题视为容器应该使用旧的 newdelete 和原始指针来实现的强烈暗示?仅仅为了避免编写析构函数似乎就很麻烦。

Ok, so everyone knows that raw pointers should be avoided like the plague and to prefer smart pointers, but does this advice apply when implementing a container? This is what I am trying to accomplish:

template<typename T> class AVLTreeNode {
public:
    T data;
    unique_ptr<AVLTreeNode<T>> left, right;
    int height;
}

Unique_ptr can make container functions more cumbersome to write because I can't have multiple raw pointers temporarily pointing to the same object in a way that is elegant. For example:

unique_ptr<AVLTreeNode<T>> rotate_right(unique_ptr<AVLTreeNode<T>> n1)
{
    unique_ptr<AVLTreeNode<T>> n2 = n1->left;

    n1->left = n2->right;
    n2->right = n1;
    // n1 must now be referenced through the longer name n2->right from now on
    n2->right->recalculate_height();
    n2->recalculate_height();

    return n2;
}

(It's not a big deal in this example but I can imagine how it could become a problem). Should I take problems like these as a strong hint that containers should be implemented with good old new, delete, and raw pointers? It seems like awfully a lot of trouble just to avoid writing a destructor.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

长途伴 2024-11-03 19:31:41

正如您所示,在实现容器时我通常不使用智能指针。原始指针(恕我直言)不能像瘟疫一样被避免。当您想要强制执行内存所有权时,请使用智能指针。但通常在容器中,容器拥有组成数据结构的指针所指向的内存。

如果在您的设计中,AVLTreeNode 唯一地拥有其左子节点和右子节点,并且您想使用 unique_ptr 来表达这一点,那没问题。但是,如果您希望 AVLTree 拥有所有 AVLTreeNode,并使用原始指针来实现,那也是有效的(也是我通常编码的方式)。

相信我,我并不反对智能指针。我是 unique_ptr 的发明者。但 unique_ptr 只是工具箱中的另一个工具。工具箱里有好的智能指针并不是万能的,盲目地使用它们并不能代替精心设计。

更新以回复评论(评论框太小):

我经常使用原始指针(很少拥有)。开源项目 libc++ 中存在我的编码风格的良好示例。可以通过“浏览 SVN”链接浏览源代码。

出于异常安全考虑,我更喜欢资源的每次分配都可以在析构函数中的某个地方进行释放,即使通常的释放发生在析构函数之外。当分配由单个指针拥有时,智能指针通常是工具箱中最方便的工具。当分配由比指针更大的东西(例如容器或类Employee)拥有时,原始指针通常是组成较大对象的数据结构的一个方便的部分。

最重要的是,我永远不会在不知道哪个对象拥有该资源的情况下分配任何资源,无论是智能指针、容器还是其他什么。

I do not usually use smart pointers when implementing containers as you show. Raw pointers (imho) are not to be avoided like the plague. Use a smart pointer when you want to enforce memory ownership. But typically in a container, the container owns the memory pointed to by the pointers making up the data structure.

If in your design, an AVLTreeNode uniquely owns its left and right children and you want to express that with unique_ptr, that's fine. But if you would prefer that AVLTree owns all AVLTreeNodes, and does so with raw pointers, that is just as valid (and is the way I usually code it).

Trust me, I'm not anti-smart-pointer. I am the one who invented unique_ptr. But unique_ptr is just another tool in the tool box. Having good smart pointers in the tool box is not a cure-all, and using them blindly for everything is not a substitute for careful design.

Update to respond to comment (comment box was too small):

I use raw pointers a lot (which are rarely owning). A good sampling of my coding style exists in the open source project libc++. One can browse the source under the "Browse SVN" link.

I prefer that every allocation of a resource be deallocate-able in a destructor somewhere, because of exception safety concerns, even if the usual deallocation happens outside of a destructor. When the allocation is owned by a single pointer, a smart pointer is typically the most convenient tool in the tool box. When the allocation is owned by something larger than a pointer (e.g. a container, or a class Employee), raw pointers are often a convenient part of the data structure composing the larger object.

The most important thing is that I never allocate any resource without knowing what object owns that resource, be it smart pointer, container, or whatever.

夏日落 2024-11-03 19:31:41

您提供的代码编译没有问题

#include <memory>
template<typename T> class AVLTreeNode {
public:
    T data;
    std::unique_ptr<AVLTreeNode<T>> left, right;
    int height;
};
int main()
{
    AVLTreeNode<int> node;
}

测试编译: https://ideone.com/aUAHs

就我个人而言,我已经即使我们唯一拥有的东西是 std::auto_ptr ,我们也一直在对树使用智能指针。

至于rotate_right,它可以通过对unique_ptr::swap 的几次调用来实现

The code you presented compiles with no problems

#include <memory>
template<typename T> class AVLTreeNode {
public:
    T data;
    std::unique_ptr<AVLTreeNode<T>> left, right;
    int height;
};
int main()
{
    AVLTreeNode<int> node;
}

test compilation: https://ideone.com/aUAHs

Personally, I've been using smart pointers for trees even when the only thing we had was std::auto_ptr

As for rotate_right, it could be implemented with a couple calls to unique_ptr::swap

非要怀念 2024-11-03 19:31:41

小修正:不应该像瘟疫一样避免原始指针(哎呀,不是每个人都知道这一事实),但是应该尽可能避免手动内存管理(通过使用容器而不是动态数组或智能指针),所以在你的函数中,只需这样做您的 unique_ptr 上的 get() 用于临时存储。

Small correction: raw pointers should not be avoided like the plague (oops, not everybody knew the fact), but manual memory management should be avoided when possible (by using containers instead of dynamic array or smartpointers), so in your function, just do a get() on your unique_ptr for temporary storage.

扭转时空 2024-11-03 19:31:41

std::shared_ptr 没有这些限制。特别是,多个shared_ptr实例可以引用同一个对象。

std::shared_ptr does not have these restrictions. Especially, multiple shared_ptr-instances can reference the same object.

海拔太高太耀眼 2024-11-03 19:31:41

Herb Shutter 在 他的 GoTW 系列

指南:不要将智能指针作为函数参数传递,除非
您想要使用或操作智能指针本身,例如
分享或转让所有权。

还有这个...

指南:更喜欢通过值、* 或 & 传递对象,而不是通过智能传递对象
指针。

Herb Shutter has very clear guideline about not using shared_ptr as parameters in his GoTW series:

Guideline: Don’t pass a smart pointer as a function parameter unless
you want to use or manipulate the smart pointer itself, such as to
share or transfer ownership.

and this...

Guideline: Prefer passing objects by value, *, or &, not by smart
pointer.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文