我想要一个带有指针成员变量的类。这个指针应该指向一个对象,该对象可能是堆栈分配的,也可能是堆分配的。但是,该指针不应具有任何所有权。换句话说,当指针超出范围时,根本不应该调用delete。我认为一个原始指针可以解决这个问题。。。然而,我不确定是否有比原始指针更好的C++11方法?
示例:
class foo{
public:
bar* pntr
};
int main(){
bar a;
foo b;
b.pntr=&a;
}
这里的原始指针非常好。C++11没有任何其他处理非拥有对象的"愚蠢"智能指针,因此您不能使用C++11智能指针。有一个针对非自有对象的"愚蠢"智能指针的建议:
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4282.pdf
已经实验性地实现为CCD_ 1(感谢@T.C.的提示)。
另一种选择是使用智能指针和一个什么都不做的自定义删除程序:
#include <memory>
int main()
{
int a{42};
auto no_op = [](int*){};
std::unique_ptr<int, decltype(no_op)> up(&a, no_op);
}
或者,正如@T.C.在评论中提到的,std::reference_wrapper
。
正如@Lightness Races in Orbit所提到的,std::weak_ptr
也可能是一个解决方案,因为后者也是一个非拥有的智能指针。然而,std::weak_ptr
只能由std::shared_ptr
或另一个std::weak_ptr
构成。一个严重的缺点是std::shared_ptr
是一个"重"对象(因为内部引用计数机制)。请注意,即使在这种情况下,std::shared_ptr
也必须有一个微不足道的自定义删除程序,否则它会破坏指向自动变量的指针的堆栈。
在这里使用原始指针是完全可以的,因为您不想让指针拥有所指向资源的所有权。
原始指针的问题在于无法判断它是否仍然指向有效对象。幸运的是,std::shared_ptr
有一个别名构造函数,您可以使用它将std::experimental::observer_ptr
0有效地生成为具有自动存储持续时间的类成员。示例:
#include <iostream>
#include <memory>
using namespace std;
struct A {
int x;
};
void PrintValue(weak_ptr<int> wp) {
if (auto sp = wp.lock())
cout << *sp << endl;
else
cout << "Object is expired." << endl;
}
int main() {
shared_ptr<A> a(new A);
a->x = 42;
weak_ptr<int> wpInt (shared_ptr<int>(a, &a->x));
PrintValue(wpInt);
a.reset(); //a->x has been destroyed, wpInt no longer points to a valid int
PrintValue(wpInt);
return 0;
}
打印:
42
对象已过期。
这种方法的主要好处是weak_ptr
不会阻止对象超出范围并被删除,但同时它可以安全地检测对象何时不再有效。缺点是增加了智能指针的开销,而且最终需要一个对象的shared_ptr
。也就是说,不能只对堆栈上分配的对象执行此操作。
如果"更好的方法"是指"更安全的方法",那么是的,我在这里实现了一个"非拥有"智能指针:https://github.com/duneroadrunner/SaferCPlusPlus.(无耻的插头警报,但我认为它在这里是相关的。)所以你的代码看起来像这样:
#include "mseregistered.h"
...
class foo{
public:
mse::TRegisteredPointer<bar> pntr;
};
int main(){
mse::TRegisteredObj<bar> a;
foo b;
b.pntr=&a;
}
TRegisteredPointer比原始指针"更聪明",因为它知道目标何时被破坏。例如:
int main(){
foo b;
bar c;
{
mse::TRegisteredObj<bar> a;
b.pntr = &a;
c = *(b.pntr);
}
try {
c = *(b.pntr);
} catch(...) {
// b.pntr "knows" that the object it was pointing to has been deleted so it throws an exception.
};
}
TRegisteredPointer通常具有比std::shared_ptr更低的性能成本。当您有机会在堆栈上分配目标对象时,成本会低得多。尽管它仍然是相当新的,而且还没有很好的文档记录,但该库包含了它的使用示例(在文件"msetl_example.cpp"的下半部分)。
该库还提供了TRegisteredPointerForLegacy,它比TRegisterdPointer慢一些,但几乎在任何情况下都可以用作原始指针的替代品。(特别是,它可以在目标类型完全定义之前使用,TRegisteredPointer的情况并非如此。)
就你的问题的观点而言,我认为这是正确的。到目前为止,C++程序员至少应该可以选择避免无效内存访问的不必要风险。原始指针也是一个有效的选择,但我认为这取决于上下文。如果这是一个复杂的软件,安全性比性能更重要,那么一个更安全的替代方案可能会更好。
只需动态分配对象并使用shared_ptr
。是的,它实际上会删除这个东西,但前提是它是最后一个有引用的东西。此外,它还防止其他人删除它。这正是正确的做法,既可以避免内存泄漏,也可以避免悬挂指针。还可以查看相关的weap_ptr
,如果指针对象的生存期要求不同,您也可以使用它。