使用智能指针实现容器
好的,所以每个人都知道应该像瘟疫一样避免原始指针并更喜欢智能指针,但是这个建议在实现容器时适用吗?这就是我想要实现的目标:
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;
}
在这个例子中这不是什么大问题,但我可以想象它如何成为一个问题)。我是否应该将此类问题视为容器应该使用旧的 new
、delete
和原始指针来实现的强烈暗示?仅仅为了避免编写析构函数似乎就很麻烦。
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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
正如您所示,在实现容器时我通常不使用智能指针。原始指针(恕我直言)不能像瘟疫一样被避免。当您想要强制执行内存所有权时,请使用智能指针。但通常在容器中,容器拥有组成数据结构的指针所指向的内存。
如果在您的设计中,
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 withunique_ptr
, that's fine. But if you would prefer thatAVLTree
owns allAVLTreeNode
s, 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
. Butunique_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.
您提供的代码编译没有问题
测试编译: https://ideone.com/aUAHs
就我个人而言,我已经即使我们唯一拥有的东西是 std::auto_ptr ,我们也一直在对树使用智能指针。
至于rotate_right,它可以通过对
unique_ptr::swap 的几次调用来实现
The code you presented compiles with no problems
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
小修正:不应该像瘟疫一样避免原始指针(哎呀,不是每个人都知道这一事实),但是应该尽可能避免手动内存管理(通过使用容器而不是动态数组或智能指针),所以在你的函数中,只需这样做您的 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.
std::shared_ptr
没有这些限制。特别是,多个shared_ptr实例可以引用同一个对象。std::shared_ptr
does not have these restrictions. Especially, multipleshared_ptr
-instances can reference the same object.Herb Shutter 在 他的 GoTW 系列:
还有这个...
Herb Shutter has very clear guideline about not using shared_ptr as parameters in his GoTW series:
and this...