C++ 如何将原始指针包装到共享\u ptr中,并防止共享\u ptr删除对象?
我需要将一个原始指针包装到一个共享的\u ptr中,以便将其传递给函数。函数返回后不保留对输入对象的任何引用C++ 如何将原始指针包装到共享\u ptr中,并防止共享\u ptr删除对象?,c++,shared-ptr,C++,Shared Ptr,我需要将一个原始指针包装到一个共享的\u ptr中,以便将其传递给函数。函数返回后不保留对输入对象的任何引用 { MyClass i; shared_ptr<MyClass> p(&i); f(p); // BAD: shared_ptr will delete i. } 如何防止shared_ptr删除引用的对象?如注释中所述,编写一个空的删除器: #include <type_traits> template <typename T&
{
MyClass i;
shared_ptr<MyClass> p(&i);
f(p);
// BAD: shared_ptr will delete i.
}
如何防止shared_ptr删除引用的对象?如注释中所述,编写一个空的删除器:
#include <type_traits>
template <typename T>
struct empty_delete
{
empty_delete() /* noexcept */
{
}
template <typename U>
empty_delete(const empty_delete<U>&,
typename std::enable_if<
std::is_convertible<U*, T*>::value
>::type* = nullptr) /* noexcept */
{
}
void operator()(T* const) const /* noexcept */
{
// do nothing
}
};
使用示例:
#include <iostream>
#include <memory>
struct noisy
{
noisy() { std::cout << "alive" << std::endl; }
~noisy() { std::cout << "dead" << std::endl; }
noisy(const noisy&);
noisy& operator=(const noisy&);
};
template <typename T>
void take(T& yours)
{
std::cout << "Taking..." << std::endl;
{
auto mine = std::move(yours);
}
std::cout << "Took." << std::endl;
}
int main()
{
std::unique_ptr<noisy> a(new noisy());
std::shared_ptr<noisy> b(new noisy());
std::unique_ptr<noisy, empty_delete<noisy>> c(new noisy());
std::shared_ptr<noisy> d(new noisy(), empty_delete<noisy>());
take(a);
take(b);
take(c);
take(d);
}
输出:
活的
活着的
活着的
活着的
拿
死去的
拿。
拿
死去的
拿。
拿
拿。
拿
拿走了
当然,此示例会泄漏内存。如注释中所述,请编写一个空的删除程序:
#include <type_traits>
template <typename T>
struct empty_delete
{
empty_delete() /* noexcept */
{
}
template <typename U>
empty_delete(const empty_delete<U>&,
typename std::enable_if<
std::is_convertible<U*, T*>::value
>::type* = nullptr) /* noexcept */
{
}
void operator()(T* const) const /* noexcept */
{
// do nothing
}
};
使用示例:
#include <iostream>
#include <memory>
struct noisy
{
noisy() { std::cout << "alive" << std::endl; }
~noisy() { std::cout << "dead" << std::endl; }
noisy(const noisy&);
noisy& operator=(const noisy&);
};
template <typename T>
void take(T& yours)
{
std::cout << "Taking..." << std::endl;
{
auto mine = std::move(yours);
}
std::cout << "Took." << std::endl;
}
int main()
{
std::unique_ptr<noisy> a(new noisy());
std::shared_ptr<noisy> b(new noisy());
std::unique_ptr<noisy, empty_delete<noisy>> c(new noisy());
std::shared_ptr<noisy> d(new noisy(), empty_delete<noisy>());
take(a);
take(b);
take(c);
take(d);
}
输出:
活的
活着的
活着的
活着的
拿
死去的
拿。
拿
死去的
拿。
拿
拿。
拿
拿走了
当然,这个示例会泄漏内存。对于注释中提到的.NET/clr,您应该实现ref类,传递托管句柄^,并让垃圾收集器管理生存期 ref类MyClassManaged:公共笔 { 公众: MyClassManaged:Pen{},本机{new MyClass{}}{} ~MyClassManaged{this->!MyClassManaged;} !MyClassManaged{删除本机u;} 私人: MyClass*本地的; }; //... { MyClassManaged^i=gcnew MyClassManaged{}; fManagedi; } TL;DR只需保持共享ptr的另一个副本处于活动状态,如果仍然需要,则在堆上而不是堆栈上分配i 情景1 { 共享\u ptr另一个\u p; { 我的第一类; 共享ptr p&i; 计划生育; //p尚未删除i 另一个_p=p;//增量引用计数 //p将减少引用计数 //由于堆栈展开,我将被删除 } //另一个p仍然保存着我在堆栈中的位置的引用 如果另一个 something;//是的,将调用某些内容 其他的 没什么;//不,这里什么也跑不动 //如果共享的\u ptr没有复制到其他地方,另一个\u p将再次尝试删除 } 情景2 { 共享\u ptr另一个\u p; { 自动p=使_共享; 自动&i=*p; 计划生育; //p没有删除i 另一个_p=p;//增量引用计数 //p将减少引用计数 //我不会被删除 } //另一个p仍然保存着我在堆中的位置的引用 如果另一个 something;//是的,将调用某些内容 其他的 没什么;//不,这里什么也跑不动 //如果共享的\u ptr没有复制到其他地方,另一个\u p将再次尝试删除 } //现在它将最终被删除
只需保存另一份共享的_ptr副本即可;无需处理空的删除程序。对于注释中提到的.NET/clr,您应该实现ref类,传递托管句柄^,并让垃圾收集器管理生存期 ref类MyClassManaged:公共笔 { 公众: MyClassManaged:Pen{},本机{new MyClass{}}{} ~MyClassManaged{this->!MyClassManaged;} !MyClassManaged{删除本机u;} 私人: MyClass*本地的; }; //... { MyClassManaged^i=gcnew MyClassManaged{}; fManagedi; } TL;DR只需保持共享ptr的另一个副本处于活动状态,如果仍然需要,则在堆上而不是堆栈上分配i 情景1 { 共享\u ptr另一个\u p; { 我的第一类; 共享ptr p&i; 计划生育; //p尚未删除i 另一个_p=p;//增量引用计数 //p将减少引用计数 //由于堆栈展开,我将被删除 } //另一个p仍然保存着我在堆栈中的位置的引用 如果另一个 something;//是的,将调用某些内容 其他的 没什么;//不,这里什么也跑不动 //如果共享的\u ptr没有复制到其他地方,另一个\u p将再次尝试删除 } 情景2 { 共享\u ptr另一个\u p; { 自动p=使_共享; 自动&i=*p; 计划生育; //p没有删除i 另一个_p=p;//增量引用计数 //p将减少引用计数 //我不会被删除 } //另一个p仍然保存着我在堆中的位置的引用 如果另一个 something;//是的,将调用某些内容 其他的 没什么;//不,这里什么也跑不动 //如果共享的\u ptr没有复制到其他地方,另一个\u p将再次尝试删除 } //现在它将最终被删除
只需保存另一份共享的_ptr副本即可;无需与空的删除程序混淆。您可以给它一个空的删除程序。为什么要这样做?我已经在堆栈上了,所以使用智能指针没有意义——事实上,正如你看到的那样,智能指针会把它搞砸。如果需要将指针传递到某个位置,请使用原始指针from&如果该函数不具有该对象的共享所有权,则该函数不应接受共享指针。函数应该接受观察指针,如原始指针或引用包装器。或者可能是reference@Andy我大体上同意,但也有一些情况
这不是一个所有权应该透明处理的地方。例如,.NET yes、yes有需要处理的笔对象,但也有用于操作系统颜色的缓存笔对象,不能处理。一个应该透明地使用一支笔并在事后丢弃它的类基本上不能被编写,这使得某些体系结构变得不必要的复杂。@Konrad:我不确定我是否理解你的意思,我不是.NET程序员:基本上你是说人们可能想编写一个同时接受常规笔对象和系统笔对象的函数,第一个函数应该在函数退出时释放,而后者不应该,对的但是在这种情况下,如果函数不存储对它的任何引用,为什么不让函数永远不释放对象呢?责任将属于调用方,然后您可以给它一个空的删除器。您为什么要这样做?我已经在堆栈上了,所以使用智能指针没有意义——事实上,正如你看到的那样,智能指针会把它搞砸。如果需要将指针传递到某个位置,请使用原始指针from&如果该函数不具有该对象的共享所有权,则该函数不应接受共享指针。函数应该接受观察指针,如原始指针或引用包装器。或者可能是reference@Andy我大体上同意,但也有一些情况,尽管这不是一个所有权应该透明处理的情况。例如,.NET yes、yes有需要处理的笔对象,但也有用于操作系统颜色的缓存笔对象,不能处理。一个应该透明地使用一支笔并在事后丢弃它的类基本上不能被编写,这使得某些体系结构变得不必要的复杂。@Konrad:我不确定我是否理解你的意思,我不是.NET程序员:基本上你是说人们可能想编写一个同时接受常规笔对象和系统笔对象的函数,第一个函数应该在函数退出时释放,而后者不应该,对的但是在这种情况下,如果函数不存储对它的任何引用,为什么不让函数永远不释放对象呢?那么责任就属于打电话的人了