我们有一个函数返回一个新的已分配对象作为输出参数(引用指针)。
MyFunc(MyObject*& obj)
{
obj = new MyObject();
}
这样称呼:
Object* obj;
MyFunc(obj);
在内部,该函数执行了很多操作,并使用shared_ptr
进行内存管理。完成后,我们想要返回的对象由shared_ptr
引用。我正在努力研究如何将新分配的对象作为常规指针返回。
我们希望继续在内部使用shared_ptr
来降低风险,但是返回shared_ptr
似乎没有意义,因为调用者对返回的值(被调用的函数)拥有完全的所有权或者对象不再需要或保留对返回数据的引用),我们希望它们具有灵活性。
有没有人建议允许我们在内部使用shared_ptr
但是有一个常规的指针界面?感谢
答案 0 :(得分:3)
一旦它被暴露在“外部世界”,它取决于谁“拥有”指针。所有权基本上归结为:“谁负责释放这种记忆,以后呢?”
可以通过一个简单的问题来回答:当调用MyFunc
时,调用者是否负责在完成时删除指针?
MyFunc
需要“释放”所有权,否则shared_ptr
会在超出范围时自动删除指针。使用shared_ptr
实际上无法完成。您需要使用unique_ptr
代替,并致电unique_ptr::release()。 MyFunc
只是使用生成的指针而忘记了它而没有delete
- 那么你可以简单地使用shared_ptr::get()返回'原始'指针。您必须小心,因为这意味着shared_ptr
仍然存在于代码的其他位置。答案 1 :(得分:3)
如果出于某种原因,您不能/不想使用std::unique_ptr
或std::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)
我可以看到四种选择,如下所示。它们都非常糟糕,并且没有将您的所有权转换为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
}
答案 3 :(得分: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认为他们拥有对数据的所有权但却没有。
答案 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之类的工具来生成其他语言的包装器,则返回智能指针可能会出现问题。