我一直在评估各种智能指针实现(哇,那里有很多),在我看来,大多数可以归类为两大类:
1)此类别对引用的对象使用继承,以便它们具有引用计数,并且通常实现up()和down()(或它们的等价物)。 IE,要使用智能指针,您指向的对象必须从ref实现提供的某些类继承。
2)此类别使用辅助对象来保存引用计数。例如,不是将智能指针指向对象,而是实际指向此元数据对象...谁具有引用计数以及up()和down()实现(并且通常为指针提供一种机制)获取指向的实际对象,以便智能指针可以正确实现operator - >())。
现在,1有一个缺点,它强制你想引用的所有对象都从一个共同的祖先继承,这意味着你不能用它来引用你无法控制的计数对象源代码为。
2有一个问题,因为计数存储在另一个对象中,如果你有一个指向现有引用计数对象的指针被转换为引用的情况,你可能有一个错误(IE,因为计数不在实际的对象中,新的引用没有办法得到计数...参考ref复制构造或赋值是好的,因为它们可以共享count对象,但是如果你必须从指针转换,你完全被冲了过来...... ...
现在,正如我所理解的那样,boost :: shared_pointer使用机制2,或类似的东西......那就是说,我无法下定决心哪个更糟糕!我只使用机制1,在生产代码中......有没有人有这两种风格的经验?或许还有另一种方式比这两种方式更好?
答案 0 :(得分:26)
“在C ++中实现智能指针的最佳方法是什么”
不要忘记我在上述不完整列表中遗忘的任何内容。
答案 1 :(得分:9)
只是为无处不在的Boost答案提供不同的视图(即使它是许多使用的正确答案),请查看Loki的智能指针实现。对于关于设计哲学的讨论,Loki的原始创作者写了这本书Modern C++ Design。
答案 2 :(得分:7)
我已经使用boost :: shared_ptr已经有好几年了,虽然你对下行是正确的(没有可能通过指针进行赋值),但我认为它绝对值得,因为它有大量与指针相关的错误救了我。
在我的自制游戏引擎中,我尽可能地用shared_ptr替换了普通指针。如果你通过引用调用大多数函数,那么性能上升的原因实际上并不是那么糟糕,因此编译器不必创建太多临时的shared_ptr实例。
答案 3 :(得分:3)
Boost也有一个侵入式指针(如解决方案1),不需要继承任何东西。它确实需要将指针更改为类来存储引用计数并提供适当的成员函数。我已经在内存效率很重要的情况下使用了这个,并且不希望使用每个共享指针的另一个对象的开销。
示例:
class Event {
public:
typedef boost::intrusive_ptr<Event> Ptr;
void addRef();
unsigned release();
\\ ...
private:
unsigned fRefCount;
};
inline void Event::addRef()
{
fRefCount++;
}
inline unsigned Event::release(){
fRefCount--;
return fRefCount;
}
inline void intrusive_ptr_add_ref(Event* e)
{
e->addRef();
}
inline void intrusive_ptr_release(Event* e)
{
if (e->release() == 0)
delete e;
}
使用Ptr typedef以便我可以轻松地在boost :: shared_ptr&lt;&gt;之间切换。和boost :: intrusive_ptr&lt;&gt;无需更改任何客户端代码
答案 4 :(得分:3)
如果你坚持使用标准库中的那些,你会没事的 虽然除了你指定的类型之外还有其他一些类型。
标准库有:
Boost还有一些比tr1(标准的下一个版本)更新了
那些仍然处于提升状态的人(无论如何都是必须拥有的),希望能够成为tr2。
答案 5 :(得分:2)
在我看来,这个问题有点像问“哪种是最好的排序算法?”没有一个答案,这取决于你的情况。
出于我自己的目的,我正在使用你的类型1.我无法访问TR1库。我确实可以完全控制所有需要共享指针的类。类型1的额外内存和时间效率可能相当轻微,但内存使用和速度对我的代码来说是个大问题,所以类型1是一个扣篮。
另一方面,对于任何可以使用TR1的人,我认为类型2 std :: tr1 :: shared_ptr类是一个合理的默认选择,只要没有一些紧迫的理由就不会使用用它。
答案 6 :(得分:1)
2的问题可以解决。由于同样的原因,Boost提供了boost :: shared_from_this。在实践中,这不是一个大问题。
但他们选择#2的原因是它可以在所有情况下使用。依赖继承并不总是一个选项,然后你留下一个智能指针,你不能使用一半的代码。
我不得不说#2是最好的,因为它可以在任何情况下使用。
答案 7 :(得分:1)
我们的项目广泛使用智能指针。在开始时,使用哪个指针存在不确定性,因此其中一位主要作者在他的模块中选择了一个侵入式指针,另一个是非侵入式版本。
通常,两种指针类型之间的差异并不显着。唯一的例外是我们的非侵入式指针的早期版本从原始指针隐式转换,如果指针使用不正确,这很容易导致内存问题:
void doSomething (NIPtr<int> const &);
void foo () {
NIPtr<int> i = new int;
int & j = *i;
doSomething (&j); // Ooops - owned by two pointers! :(
}
前一段时间,一些重构导致代码的某些部分被合并,因此必须选择使用哪种指针类型。非侵入式指针现在将转换构造函数声明为 explicit ,因此决定使用侵入式指针来节省所需的代码更改量。
令我们惊讶的是,我们注意到的一件事是我们通过使用侵入式指针立即改善了性能。我们没有对此进行太多研究,只是假设差异是维护计数对象的成本。到目前为止,非侵入式共享指针的其他实现可能已经解决了这个问题。
答案 8 :(得分:1)
您所谈论的是侵入性和非侵入式智能指针。 Boost有两个。每次需要更改引用计数时,boost::intrusive_ptr
都会调用一个函数来减少和增加对象的引用计数。它不是调用成员函数,而是调用自由函数。因此,它允许管理对象,而无需更改其类型的定义。正如你所说,boost::shared_ptr
是非侵入性的,你的类别2.
我的答案解释了intrusive_ptr:Making shared_ptr not use delete。简而言之,如果您有一个已经引用计数的对象,或者需要(当您解释)已被引用为intrusive_ptr所拥有的对象时,则使用它。