在 C++ 中管理内存所有权的最佳方法?共享指针或其他机制?
在一段新代码中,我有几个相互引用的不同类。是这样的(这不是我的实际情况,而是类似的例子):
class BookManager
{
...
};
class Book
{
public:
void setBookManager(BookManager *bookManager) {m_bookManager = bookManager;}
private:
BookManager *m_bookManager;
};
每本书都引用一个图书管理器,但问题是许多图书都会有自己特定的 BookManager,但有些图书可能共享一个公共 BookManager。
调用者并没有真正指定 Book 应该用它的 BookManager 做什么,但是在大约 90% 的情况下,BookManager 可以和 Book 一起销毁。大约10%的情况下,同一个BookManager会被多本书重复使用,并且BookManager一定不能和Book一起删除。
在 90% 的情况下,将 BookManager 与 Book 一起删除是很方便的,因为 Book::setBookManager 的调用者不再需要记住 BookManager。它只是随着这本书本身而消亡。
我看到有两种替代解决方案可以解决这个问题。
首先是广泛使用共享指针。如果调用者之后不再对 BookManager 感兴趣,则它不会保留指向它的共享指针。如果它仍然对此感兴趣,或者如果它希望 BookManager 在多本书上共享,它会保留共享指针并将其传递给这些多本书。
第二种选择是明确告诉 Book 如何处理这本书的所有权,如下所示:
class Book
{
public:
void setBookManager(BookManager *bookManager, book takeOwnership=true)
{
m_bookManager = bookManager;
m_hasOwnership = takeOwnership;
}
~Book()
{
if (m_hasOwnership && m_bookManager) delete m_bookManager;
}
private:
BookManager *m_bookManager;
bool m_hasOwnership;
};
第二种解决方案似乎更容易,并允许我们使用普通的指针语法(BookManager *
而不是 < code>std::shared_ptr
另一种选择可能是在 BookManager 中有一个 typedef,如下所示:
class BookManager
{
public:
typedef std::shared_ptr<BookManager> Ptr;
...
};
这允许我们这样写:
BookManager::Ptr bookManager;
它看起来更像普通的指针语法,而不是原始的共享指针语法。
有人有这两种方法的经验吗? 还有其他建议吗?
In a new piece of code I have several different classes that refer to each other. Something like this (this is not my actual situation but an example of something similar):
class BookManager
{
...
};
class Book
{
public:
void setBookManager(BookManager *bookManager) {m_bookManager = bookManager;}
private:
BookManager *m_bookManager;
};
Every book refers to a book manager, but the problem is that many books will have its own specific BookManager, but some books may share a common BookManager.
The caller doesn't really specify what the Book should do with its BookManager, but in about 90% of the cases, the BookManager can be destroyed together with the Book. In about 10% of the cases, the same BookManager is reused for multiple books, and the BookManager must not be deleted with the Book.
Deleting the BookManager together with the Book is handy in those 90% of the cases, as the caller of Book::setBookManager doesn't need to remember the BookManager anymore. It just dies with the Book itself.
I see two alternative solutions for solving this.
First is to make extensive use of shared pointers. If the caller is not interested anymore in the BookManager afterwards, it doesn't keep a shared pointer to it. If it is still interested in it, or if it wants the BookManager to be shared over multiple books, it keeps the shared pointer and passes it to those multiple books.
A second alternative is to tell the Book explicitly what to do with the ownership of the book, like this:
class Book
{
public:
void setBookManager(BookManager *bookManager, book takeOwnership=true)
{
m_bookManager = bookManager;
m_hasOwnership = takeOwnership;
}
~Book()
{
if (m_hasOwnership && m_bookManager) delete m_bookManager;
}
private:
BookManager *m_bookManager;
bool m_hasOwnership;
};
The second solution seems much easier and allows us to use normal pointer syntax (BookManager *
as opposed to std::shared_ptr<BookManager>
), but it seems less 'clean' than the shared pointer approach.
Another alternative might be to have a typedef in BookManager like this:
class BookManager
{
public:
typedef std::shared_ptr<BookManager> Ptr;
...
};
Which allows us to write this:
BookManager::Ptr bookManager;
Which looks more like the the normal pointer-syntax than the original shared pointer syntax.
Does anyone have experience with either approach?
Any other suggestions?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
在 C++ 中,如果您对公共对象进行共享、非协调访问,那么最常见的方法是某种引用计数,您可以从
shared_ptr
获得这种引用计数。唯一的缺点是它在 C++(尤其是库)中并不普遍,因此有时需要访问原始指针。在这些情况下,您需要小心保持对象处于活动状态。
我建议如果您使用shared_ptr——尝试在任何地方使用它,除非不可能。是的,如果需要,可以使用 typedef。
In C++ if you have shared, un-coordinated access to common objects then the most common approach is some kind of reference counting, which you get from
shared_ptr
.The only downside is that it isn't pervasive in C++ (especially libraries), so you sometimes need access to the raw pointer. In those cases, you need to be careful to keep the object alive.
I suggest that if you used shared_ptr -- try to use it everywhere, unless it's impossible. And yes, use the typedef if you want.
看来你对这个问题考虑得很清楚。对我来说,这似乎是
shared_ptr
的理想用途。为了给您增加一些价值,请务必查看用于高效创建
shared_ptr
的模板 此处。You seem to have this pretty well thought through. Looks like an ideal use for
shared_ptr
to me.In the interests of adding SOME value for you, make sure you look at the templates for efficient
shared_ptr
creation here.C++ 中有两种“所有权”:
shared_ptr
shared_ptr 很难确定他们的归属。 code>,顾名思义,是针对第二种情况的。它很少被需要,特别是在引入移动语义并因此引入unique_ptr
后,但在实际需要它的情况下,它是无价的。看看你的案例,我的问题是:你真的需要共享所有权吗?
避免共享所有权的一种典型解决方案是使用
Factory
,它将成为其创建的所有对象的唯一所有者,并保证只要Factory
本身它们就处于活动状态还活着。它可能比使用
shared_ptr
更“安全”,但有一些非常有趣的论点:除非您内存有限(在这种情况下,对象删除得越早越好),您可能希望在此处采用
BookManagerFactory
方法。There are 2 kinds of "ownership" in C++:
shared_ptr
, as the name implies, is for the second case. It is rarely required, especially with the introduction of move semantics and thusunique_ptr
, but in those cases where it is actually required, it's invaluable.Looking at your case, the question I have is: do you actually need shared ownership ?
One typical solution to avoid shared ownership is to use a
Factory
, which will be the sole owner of all the objects it creates, and guarantees they are alive as long theFactory
itself is alive.It might be "less safe" than using a
shared_ptr
, but there are very interesting arguments:Unless you are memory constrained (in which case the sooner the objects get deleted the better it is), you might wish to take a
BookManagerFactory
approach here.只有当您确切地知道该对象的所有者在哪里并小心地将其删除时(!),您才应该使用裸指针。我有一个类的“堆栈”,每个类都保存唯一的父指针,在这种情况下,引用计数始终为 1,并且您可以通过最后一个子元素访问最深的元素。不过,这里的设置似乎要复杂得多,请使用智能指针。请记住,即使裸指针看起来更干净或更简单,也应该推荐
unique_ptr
,但您可能必须在预转换代码中与移动和复制分配以及切换产生的神秘错误消息进行斗争。Only when you know exactly where the object is owned and take care to delete it there (!), should you use bare pointers. I have a "stack" of classes that each hold unique parent pointer, in which case the reference count would always be 1 and you can access the deepest element through the last child. It seems like you have a much more complicated setup here, though, go with smart pointers. Remember that even if a bare pointer might seem cleaner or easier,
unique_ptr
should be recommended, but you might have to battle move vs copy assignment in pre-conversion code and cryptic error messages resulting from the switch.Lou Franco 已经给了您答案,但作为旁注:您的第二个实现想法本质上是 auto_ptr 所做的事情(当取得所有权为真时)。
更好的解决方案可能是让 Book 类保存驻留在某个 BookManagerCache 类中的 BookManager 的句柄(例如数组索引或散列)。 Cache 类单独负责管理 BookManager 的生命周期。
Lou Franco already gave you the answer, but as a side note: Your second implementation idea is essentially what an auto_ptr does (when take ownership is true).
A better solution might be to have the Book class hold a handle (e.g. array index or hash) to a BookManager that residese in some BookManagerCache class. The Cache class is solely responsible for managing the lifetimes of BookManagers.