返回智能指针

时间:2014-04-15 15:26:15

标签: c++ shared-ptr smart-pointers unique-ptr resource-management

假设我有一个包含某个对象向量的Manager类:

class SomeObjectManager
{
private:
    std::vector<SomeObject> _heldObjects;
};

在那个类中,我有一些函数迭代通过所述向量来返回请求的对象。

SomeObject getSomeObjectByName(std::string nameToFind);

我需要知道的是什么时候使用智能指针是正确的。我真的应该返回下面的东西吗?

std::shared_ptr<SomeObject> getSomeObjectByName(std::string nameToFind);

或者我应该使用像unique_ptr或weak_ptr这样的东西吗?我希望SomeObjectManager类拥有返回的实际对象,并且永远不会删除SomeObject,除非管理器这样做。

我最近才在C#模式下回到C ++世界已有一段时间了;感谢您的帮助并解决了我的困惑。

我已经阅读了很多关于此事的内容,但从未真正找到对我的具体情况的直接答案。


编辑#1

我想用这句话改写我的最后几句:

我希望SomeObjectManager类拥有正在返回的实际对象,并且从未说过SomeObject从向量中删除并且经常删除,超出范围,直到Manager强制它这样做。例如:

void SomeObjectManager::removeSomeObjectByName(const std::string& objectToRemove);

这将迭代向量,找到所述SomeObject,然后将其从Vector中删除。

6 个答案:

答案 0 :(得分:4)

由于SomeObjectManagerSomeObject个实例的所有者(存储在其std::vector数据成员中),我只是返回原始的指针,因为它们实际上是观察指针。

std::vector<SomeObject> _heldObjects;

SomeObject* getSomeObjectByName(const std::string& nameToFind) {
    ... find index of object corresponding to 'nameToFind'
    return &_heldObjects[foundIndex];
}

(请注意,我使用引用nameToFind 传递了const,因为我认为nameToFind是输入字符串,所以如果你在方法内部,那么只需观察该字符串,就可以避免使用const &进行深层复制。

当你拥有拥有原始指针时,你必须注意(它们应该包含在安全的RAII边界内),但观察原始指针是正常的。

确保SomeObjectManager的生命周期超过其客户端的生命周期,以确保客户端引用有效对象。

另请注意,如果向向量数据成员添加新项(例如,使用std::vector::push_back()),则向量中存储的先前SomeObject个实例的地址可能会发生变化。所以,如果你指出那些外面的东西,它们就会失效。

因此,在将指向其元素的指针指向外部的客户端代码之前,请确保不更改向量大小和向量内容。

另一种方法是让std::vector<std::unique_ptr<SomeObject>>作为数据成员。在这种情况下,即使向量调整了大小,使用智能指针返回的地址(特别是使用std::unique_ptr::get())仍然有效:

std::vector<std::unique_ptr<SomeObject>> _heldObjects;

SomeObject* getSomeObjectByName(const std::string& nameToFind) {
    ... find index of object corresponding to 'nameToFind'
    return _heldObjects[foundIndex].get();
}

<强> PS
另一个选项可能是将引用返回到const SomeObject(假设const的这种用法在您的设计中有意义):

std::vector<SomeObject> _heldObjects;

const SomeObject& getSomeObjectByName(const std::string& nameToFind) const {
    ... find index of object corresponding to 'nameToFind'
    return _heldObjects[foundIndex];
}

答案 1 :(得分:3)

如果您的程序在单线程中运行,那么如果您有足够的纪律,那么您最好能够返回原始指针或对vector中存储的对象的引用。< / p>

由于管理器私有地拥有vector及其中的对象,因此控制何时删除对象,您可以确保没有任何无效指针指向已删除的对象(这不是't&t; t自动保证!)。
基本上,管理员必须只有在知道没有人持有对该对象的引用时才删除对象,例如只在不同的,明确定义的时间(例如在程序结束时,或者当它知道没有消费者留下时)或者这样) 引用计数是这样做的一种方式,它也是内部shared_ptr所做的事情(嗯,没有...... 严格遵守规范的字母,引用计数不是&#39; t required,只定义了可见行为,但是 practially 所有实现都是这样做的。

&#34;删除&#34;因此,对象将仅仅递减引用计数器(很像在托管语言中),并且当引用计数器达到零时,对象将确实不再存在。哪个可能是,但不一定是你&#34;删除&#34;它。在实际销毁物体之前,可能需要一些时间 这种方法可以自动运行&#34;没有大量的勤奋和严格的纪律,只需将对象的shared_ptr存储在向量中并返回shared_ptrweak_ptr s即可实现。

等一下!那么,如果你还可以返回一个weak_ptr,为什么还有shared_ptr?他们在逻辑上和实际上都做了不同的事情。 shared_ptr拥有(至少部分),而weak_ptr则不拥有。此外,weak_ptr的复制成本更低。

多线程程序中,存储shared_ptr并返回weak_ptr是唯一安全的方法。 weak_ptr不拥有资源,因此无法阻止管理器删除对象,但它为持有者提供了一种可靠且明确的方式来了解资源是否有效并且资源将保持有效< / em>当你使用它时。

您返回weak_ptr,当消费者真正想要使用该对象时,它会将weak_ptr转换为临时shared_ptr。这将失败(给出一个空指针),以便消费者知道该对象已被删除,并且它可能不会使用它。或者,它会成功,现在消费者有一个有效的指针,该对象具有对象的共享所有权,现在可以保证在使用时保持有效

中间没有任何内容&#34;有效&#34;并且&#34;无效&#34;,没有猜测,也没有什么可以失败。如果您成功转换为有效的临时shared_ptr,那么您很高兴。否则,对象消失,但你知道 这在安全性方面是一个很大的优势。即使经理&#34;删除&#34;当你使用它时,你的程序不会崩溃或产生垃圾,对象在你停止使用之前一直有效!

可以说,这有点模糊&#34;经理在选择时删除对象&#34;范式,但它确实是安全地做到这一点的唯一方式。管理员仍然是控制要删除的对象的人,它只能在使用时立即删除 对象(这可能会导致可怕的灾难)。但是,它可以随时通过删除shared_ptr来减少下一个可能的时间,从而减少引用计数。

唯一明显的showstopper是一个对象必须立即被销毁的情况(因为析构函数具有必须立即发生的副作用而没有延迟)。但在这种情况下,获得并发访问权限非常困难(一场噩梦!)。幸运的是,这也是一种非常罕见的情况。

答案 2 :(得分:2)

从函数返回SomeObject的引用(或常规指针)。只要它保留在向量中,引用就是有效的,并且不会重新分配向量(小心使用,可能使用list而不是unique_ptr的向量)。从向量中删除时,对象已经死亡,并且对它的所有引用都不再有效。 (再次小心地删除中间的元素)

答案 3 :(得分:2)

如果您没有将对象存储为std :: shared_ptrs,那么返回std :: shared_ptr是没有意义的。甚至不确定你将如何做到这一点。我认为没有办法将现有指针包装在智能指针中。如果你已经有数据,你可以返回一个常规的const指针。这样,您将避免复制对象内容所需的开销。

答案 4 :(得分:0)

在这种情况下,您可以选择使用shared_ptrweak_ptr。你使用的将取决于你想要的对象的生命周期。

如果您只希望对象有效,而SomeObjectManager有一个对它的引用,并且客户端当时正在使用它,那么请使用weak_ptr。如果您希望引用在SomeObjectManager具有引用且客户端存储对它的引用时保持有效。

以下是weak_ptr的示例。

std::weak_ptr<SomeObject> weakref = getSomeObject();   
// weakref will not keep the object alive if it is removed from the object manager.

auto strongref = weakref.lock();
if ( strongref ) {
     // strongref is a shared_ptr and will keep the object alive until it 
     // goes out of scope.
}

这在多线程环境中非常有用,因为shared_ptr引用计数访问是线程安全的。但是,它确实意味着客户端可以延长对象的生命周期,而不是您想要的时间。

答案 5 :(得分:-1)

如果要使用智能共享指针,向量本身应使用智能指针。

class SomeObjectManager
{
private:
    std::vector<std::shared_ptr<SomeObject> > _heldObjects;
};

但是那时你很安全。