将shared_ptr转换为常规指针

时间:2017-02-14 16:13:29

标签: c++ c++11 pointers shared-ptr

我们有一个函数返回一个新的已分配对象作为输出参数(引用指针)。

MyFunc(MyObject*& obj)
{
    obj = new MyObject();
}

这样称呼:

Object* obj;
MyFunc(obj);

在内部,该函数执行了很多操作,并使用shared_ptr进行内存管理。完成后,我们想要返回的对象由shared_ptr引用。我正在努力研究如何将新分配的对象作为常规指针返回。

我们希望继续在内部使用shared_ptr来降低风险,但是返回shared_ptr似乎没有意义,因为调用者对返回的值(被调用的函数)拥有完全的所有权或者对象不再需要或保留对返回数据的引用),我们希望它们具有灵活性。

有没有人建议允许我们在内部使用shared_ptr但是有一个常规的指针界面?感谢

6 个答案:

答案 0 :(得分:3)

一旦它被暴露在“外部世界”,它取决于谁“拥有”指针。所有权基本上归结为:“谁负责释放这种记忆,以后呢?”

可以通过一个简单的问题来回答:当调用MyFunc时,调用者是否负责在完成时删除指针?

  1. 如果,则MyFunc需要“释放”所有权,否则shared_ptr会在超出范围时自动删除指针。使用shared_ptr实际上无法完成。您需要使用unique_ptr代替,并致电unique_ptr::release()
  2. 如果不是 - 如果MyFunc只是使用生成的指针而忘记了它而没有delete - 那么你可以简单地使用shared_ptr::get()返回'原始'指针。您必须小心,因为这意味着shared_ptr仍然存在于代码的其他位置。

答案 1 :(得分:3)

如果出于某种原因,您不能/不想使用std::unique_ptrstd::auto_ptr(例如,如果您因创造某些原因需要在内部拥有多个所有者,或者您的基础方法需要std::shared_ptr要传递,您仍然可以使用自定义删除工具使其与std::shared_ptr一起使用,如下所述:https://stackoverflow.com/a/5995770/1274747

原则上,在返回之前完成后,将切换器切换为不实际删除实例(使删除器为“null”),然后通过shared_ptr get()返回。即使在所有shared_ptr对象被销毁之后,内存也不会被删除(因为删除的删除器将跳过删除)。

评论中还有一个链接不太明显,这可能是您感兴趣的: http://paste.ubuntu.com/23866812/ (不确定,如果在没有所有情况下交换机的共享所有权的情况下它真的可行,则需要进行测试)

修改

正如所料,使用来自pastebin的链接简单可变删除器时,您需要小心,因为删除器实际上已被复制以存储在std::shared_ptr中。

但您仍然可以使用std::ref

来使其发挥作用
MyFunc(MyObject*& obj)
{
    DisarmableDelete<MyObject> deleter;
    std::shared_ptr<MyObject> ptr(new MyObject(), std::ref(deleter));
    // do what is necessary to setup the object - protected by deleter
    // ...
    // disarm before return
    deleter._armed = false;
    obj = ptr.get();
    // deleter disarmed - object not freed
}

为了完整性(并避免将来可能出现断开链接),以下是来自http://paste.ubuntu.com/23866812/DisarmableDelete的实现。

template <typename T, typename Deleter = typename std::default_delete<T> >                                                                                                                              
    struct DisarmableDelete : private Deleter {                                                                                                                                                         
        void operator()(T* ptr) { if(_armed) Deleter::operator()(ptr); }                                                                                                                                
        bool _armed = true;                                                                                                                                                                             
    };        

答案 2 :(得分:2)

shared_ptr的目的是表达共享所有权。

任何不共享对象所有权的东西 - 没有权利使对象的生命周期持续更长时间,对象生命周期是对象生命周期的共享所有者请求的并集 - 不应该使用shared_ptr

在这里,您有shared_ptr并且您将返回它。那时,你违反了shared_ptr的假设;任何保留shared_ptr副本的人都希望其内容可以持续到请求的时间。

同时,调用代码认为它拥有你传递它的MyObject*原始指针。

这是滥用shared_ptr的一个例子。

说&#34;我们有内存管理问题,请使用shared_ptr&#34;没有解决内存管理问题。正确使用shared_ptr需要小心和设计,并且设计必须是在有问题的对象的生命周期结束时由2个或更多个数据和/或代码共享。

内部代码,如果它不拥有指针,则应使用类似observer_ptr<T>或原始T*的内容(第一个明确说明它不拥有该对象) )。

所有权应该是明确的,并且在unique_ptr中。然后,如果需要,它可以调用.release()将所有权传递给原始指针;在实践中,我会更改您的签名以获取unique_ptr&,或让其返回unique_ptr

然后调用者在想要使用其他对象生存期管理系统时调用.release(),或者对象生命周期管理系统应该使用unique_ptr s(因此非常明确地获取事物的所有权)

使用非黑客解决方案。

例如std::shared_ptr<std::unique_ptr<T>>。在这种情况下,您拥有独特所有权的共享所有权。

unique_ptr可以从中获取所有权(通过.release())。执行此操作后,仍然存在的所有shared_ptr也会清除其unique_ptr

这会将共享的唯一所有权放在首位和中心位置,而不是将非删除者黑客攻击到shared_ptr,而是让悬挂的shared_ptrs认为他们拥有对数据的所有权但却没有。

答案 3 :(得分:2)

我可以看到四种选择,如下所示。它们都非常糟糕,并且没有将您的所有权转换为std::unique_ptr<T>并通过obj = ptr.release();返回我只能提供一个hack,其中参数在销毁时分配给指针,但您仍然需要捕获异常并测试指针是否已分配。

#include <iostream>
#include <memory>
#include <exception>

struct foo {
  void bar() const { std::cout << this << " foo::bar()\n"; }
  ~foo() { std::cout << this << " deleted\n"; }
};

void f1(foo*& obj) {
  obj = new foo;
  // do stuff... if an exception is thrown before we return we are
  // left with a memory leak
}

void f2(foo*& obj) {
  auto holder = std::make_shared<foo>();
  // do stuff.. if an exception is thrown the pointer will be
  // correclty deleted.

  obj = holder.get(); // awesome, I have a raw pointer!
} // oops, the destructor gets called because holder went out of
  // scope... my pointer points to a deleted object.

void f3(foo*& obj) {
  auto holder = std::make_unique<foo>();
  // do stuff.. if an exception is thrown the pointer will be
  // correclty deleted.

  obj = holder.release(); // awesome, I have a raw pointer!
} // no problem whem holder goes out of scope because it does not own the pointer

void f4(foo*& obj) {
  // a super-weird hack that assigns obj upon deletion
  std::shared_ptr<foo> holder(new foo, [&obj](foo*& p){  obj = p; });
  throw std::exception();
} // no problem whem holder goes out of scope because it does not own
  // the pointer... but if an execption is throw we need to delete obj

int main() {

  foo* p1;
  f1(p1);
  p1->bar();

  foo* p2;
  f2(p2);
  // p2->bar(); // error

  foo* p3;
  f3(p3);
  p3->bar();

  foo* p4;
  try {
    f4(p4);
  } catch(...) {
    std::cout << "caught an exception... test whether p4 was assigned  it\n";
  }
  p4->bar(); // I still need to delete this thing
}

答案 4 :(得分:1)

最好的方法是在返回原始指针之前使用内部unique_ptr并调用其.release()方法。

如果您在内部停留使用shared_ptr,则可以选择创建指定自定义的&#34; noop&#34; deleter,在shared_ptr被销毁时不执行任何操作(而不是在拥有的指针上调用delete)。像往常一样从shared_ptr获取原始指针(.get()方法)。 可以在Boost库(null_deleter)中找到此类删除器的示例 但请注意,有效地执行此操作&#34;禁用&#34;完全拥有shared_ptr的有用性......

答案 5 :(得分:0)

如果您使用std :: shared_ptr在内部管理分配对象的寿命,并且正在返回用于访问的原始指针,并且不希望该指针影响引用计数,则可以通过调用shared_ptr返回原始指针。 get()。

如果您使用Swig之类的工具来生成其他语言的包装器,则返回智能指针可能会出现问题。