将 shared_ptr 转换为常规指针
Convert a shared_ptr to regular a pointer
我们有一个函数,return将一个新分配的对象作为输出参数(指向指针)。
MyFunc(MyObject*& obj)
{
obj = new MyObject();
}
这样称呼:
Object* obj;
MyFunc(obj);
该函数在内部做了很多工作,并使用 shared_ptr
进行内存管理。完成后,我们想要 return 的对象被 shared_ptr
引用。我正在努力研究如何 return 我们新分配的对象作为常规指针。
我们想继续在内部使用 shared_ptr
来降低风险,但是 return 和 shared_ptr
似乎没有意义,因为调用者完全拥有returned 值(被调用的函数或对象不再需要或保留对 returned 数据的引用)并且我们希望它们具有灵活性。
有没有人有任何建议允许我们在内部使用 shared_ptr
但有一个常规的指针界面?谢谢
最好的方法是在内部使用 unique_ptr
并在返回原始指针之前调用其 .release() 方法。
如果你坚持在内部使用 shared_ptr,一个选项是创建它指定一个自定义的 "noop" 删除器,当 shared_ptr 被销毁时它什么都不做(而不是在拥有的指针上调用 delete)。像往常一样从 shared_ptr
获取原始指针(.get() 方法)。
可以在 Boost 库 (null_deleter) 中找到此类删除器的示例。
但请注意,这样做有效 "disable" 拥有 shared_ptr 的用处...
这取决于谁 "owns" 指针,一旦它暴露给 'outside world.' 所有权基本上归结为:"who is responsible for freeing this memory, later?"
可以用一个简单的问题来回答:当MyFunc
被调用时,调用者是否负责在完成时删除指针?
- 如果是,那么
MyFunc
需要'release'所有权,否则shared_ptr
会自动删除指针,当它去超出范围。这实际上无法完成,使用shared_ptr
。您需要改用 unique_ptr
,并调用 unique_ptr::release()。
- If not - if
MyFunc
将简单地 使用 结果指针并在没有 delete
的情况下忘记它-ing 它 - 然后你可以简单地 return 使用 shared_ptr::get() 的 'raw' 指针。您必须小心,因为这意味着 shared_ptr
仍然存在于您的代码中的其他地方。
如果出于某种原因您 cannot/want 未使用 std::unique_ptr
或 std::auto_ptr
(例如,如果您出于某种原因在创建过程中需要在内部拥有多个所有者,或者您的基础方法需要 std::shared_ptr
传递),您仍然可以使用自定义删除器使其与 std::shared_ptr
一起使用,如下所述:
原则上,在 return 之前完成后,将删除器切换为不实际删除实例(使删除器 "null"),然后 return 通过shared_ptr get()
。即使在所有 shared_ptr 个对象被销毁后,内存也不会被删除(因为 nulled 删除器将跳过删除)。
评论里还有一个link不太明显,你可能会感兴趣:
http://paste.ubuntu.com/23866812/
(不确定在所有情况下如果没有开关的共享所有权它是否真的有效,需要测试)
编辑
正如预期的那样,使用来自 pastebin 的 linked 简单可解除删除器你需要小心,因为删除器实际上被复制用于存储在 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
}
并且为了完整起见(并避免潜在的未来中断 link),这里是 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;
};
我可以看到四个备选方案,如下所示。它们都很糟糕,并且没有将您的所有权切换到 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
}
shared_ptr
的重点是表达共享所有权。
任何不共享对象所有权的东西——它们无权延长对象的生命周期,而对象生命周期是共享所有者请求对象生命周期的联合——不应使用 shared_ptr
.
在这里,你有一个 shared_ptr
,你将要 return 它。那时,您违反了 shared_ptr
的假设;任何保留了 shared_ptr
副本的人都希望它的内容可以在它请求的时候持续下去。
与此同时,调用代码认为它拥有您传递给它的 MyObject*
原始指针。
这是一个误用 shared_ptr
的例子。
说 "we have memory management issues, use shared_ptr
" 并不能解决内存管理问题。正确使用shared_ptr
需要细心和设计,设计必须是当问题对象的生命周期结束时由2个或多个数据and/or代码共享。
内部代码,如果它不拥有指针,应该使用类似 observer_ptr<T>
或原始 T*
的东西(首先要明确它不拥有该对象).
所有权应该是明确的,并且在 unique_ptr
中。如果需要,它然后可以调用 .release()
将所有权传递给原始指针;实际上,我会将您的签名更改为 unique_ptr&
,或者 return unique_ptr
.
当调用者想要使用其他对象生命周期管理系统时,调用者将调用 .release()
,或者该对象生命周期管理系统应该消耗 unique_ptr
s(因此非常清楚获得所有权东西)。
使用 non-hack 解决方案。
如std::shared_ptr<std::unique_ptr<T>>
。在这种情况下,您拥有一个独特所有权的共享所有权。
unique_ptr
的所有权可以被剥夺(通过 .release()
)。当它这样做时,所有仍然存在的 shared_ptr
的 unique_ptr
也将被清除。
这将共享的唯一所有权放在首位和中心位置,而不是将 non-deleter 黑客攻击成 shared_ptr 并悬而未决的 shared_ptr 认为他们对数据拥有所有权但实际上并不.
如果您使用 std::shared_ptr 在内部管理已分配对象的生命周期,并且 return 使用原始指针进行访问并且不希望该指针影响引用计数,您可以return 通过调用 shared_ptr.get() 的原始指针。
如果您使用 Swig 等工具为其他语言生成包装器,return 智能指针可能会出现问题。
我们有一个函数,return将一个新分配的对象作为输出参数(指向指针)。
MyFunc(MyObject*& obj)
{
obj = new MyObject();
}
这样称呼:
Object* obj;
MyFunc(obj);
该函数在内部做了很多工作,并使用 shared_ptr
进行内存管理。完成后,我们想要 return 的对象被 shared_ptr
引用。我正在努力研究如何 return 我们新分配的对象作为常规指针。
我们想继续在内部使用 shared_ptr
来降低风险,但是 return 和 shared_ptr
似乎没有意义,因为调用者完全拥有returned 值(被调用的函数或对象不再需要或保留对 returned 数据的引用)并且我们希望它们具有灵活性。
有没有人有任何建议允许我们在内部使用 shared_ptr
但有一个常规的指针界面?谢谢
最好的方法是在内部使用 unique_ptr
并在返回原始指针之前调用其 .release() 方法。
如果你坚持在内部使用 shared_ptr,一个选项是创建它指定一个自定义的 "noop" 删除器,当 shared_ptr 被销毁时它什么都不做(而不是在拥有的指针上调用 delete)。像往常一样从 shared_ptr
获取原始指针(.get() 方法)。
可以在 Boost 库 (null_deleter) 中找到此类删除器的示例。
但请注意,这样做有效 "disable" 拥有 shared_ptr 的用处...
这取决于谁 "owns" 指针,一旦它暴露给 'outside world.' 所有权基本上归结为:"who is responsible for freeing this memory, later?"
可以用一个简单的问题来回答:当MyFunc
被调用时,调用者是否负责在完成时删除指针?
- 如果是,那么
MyFunc
需要'release'所有权,否则shared_ptr
会自动删除指针,当它去超出范围。这实际上无法完成,使用shared_ptr
。您需要改用unique_ptr
,并调用 unique_ptr::release()。 - If not - if
MyFunc
将简单地 使用 结果指针并在没有delete
的情况下忘记它-ing 它 - 然后你可以简单地 return 使用 shared_ptr::get() 的 'raw' 指针。您必须小心,因为这意味着shared_ptr
仍然存在于您的代码中的其他地方。
如果出于某种原因您 cannot/want 未使用 std::unique_ptr
或 std::auto_ptr
(例如,如果您出于某种原因在创建过程中需要在内部拥有多个所有者,或者您的基础方法需要 std::shared_ptr
传递),您仍然可以使用自定义删除器使其与 std::shared_ptr
一起使用,如下所述:
原则上,在 return 之前完成后,将删除器切换为不实际删除实例(使删除器 "null"),然后 return 通过shared_ptr get()
。即使在所有 shared_ptr 个对象被销毁后,内存也不会被删除(因为 nulled 删除器将跳过删除)。
评论里还有一个link不太明显,你可能会感兴趣: http://paste.ubuntu.com/23866812/ (不确定在所有情况下如果没有开关的共享所有权它是否真的有效,需要测试)
编辑
正如预期的那样,使用来自 pastebin 的 linked 简单可解除删除器你需要小心,因为删除器实际上被复制用于存储在 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
}
并且为了完整起见(并避免潜在的未来中断 link),这里是 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;
};
我可以看到四个备选方案,如下所示。它们都很糟糕,并且没有将您的所有权切换到 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
}
shared_ptr
的重点是表达共享所有权。
任何不共享对象所有权的东西——它们无权延长对象的生命周期,而对象生命周期是共享所有者请求对象生命周期的联合——不应使用 shared_ptr
.
在这里,你有一个 shared_ptr
,你将要 return 它。那时,您违反了 shared_ptr
的假设;任何保留了 shared_ptr
副本的人都希望它的内容可以在它请求的时候持续下去。
与此同时,调用代码认为它拥有您传递给它的 MyObject*
原始指针。
这是一个误用 shared_ptr
的例子。
说 "we have memory management issues, use shared_ptr
" 并不能解决内存管理问题。正确使用shared_ptr
需要细心和设计,设计必须是当问题对象的生命周期结束时由2个或多个数据and/or代码共享。
内部代码,如果它不拥有指针,应该使用类似 observer_ptr<T>
或原始 T*
的东西(首先要明确它不拥有该对象).
所有权应该是明确的,并且在 unique_ptr
中。如果需要,它然后可以调用 .release()
将所有权传递给原始指针;实际上,我会将您的签名更改为 unique_ptr&
,或者 return unique_ptr
.
当调用者想要使用其他对象生命周期管理系统时,调用者将调用 .release()
,或者该对象生命周期管理系统应该消耗 unique_ptr
s(因此非常清楚获得所有权东西)。
使用 non-hack 解决方案。
如std::shared_ptr<std::unique_ptr<T>>
。在这种情况下,您拥有一个独特所有权的共享所有权。
unique_ptr
的所有权可以被剥夺(通过 .release()
)。当它这样做时,所有仍然存在的 shared_ptr
的 unique_ptr
也将被清除。
这将共享的唯一所有权放在首位和中心位置,而不是将 non-deleter 黑客攻击成 shared_ptr 并悬而未决的 shared_ptr 认为他们对数据拥有所有权但实际上并不.
如果您使用 std::shared_ptr 在内部管理已分配对象的生命周期,并且 return 使用原始指针进行访问并且不希望该指针影响引用计数,您可以return 通过调用 shared_ptr.get() 的原始指针。
如果您使用 Swig 等工具为其他语言生成包装器,return 智能指针可能会出现问题。