没有所有权的堆栈对象的指针

时间:2016-01-02 16:37:55

标签: c++ pointers c++11 smart-pointers

我想要一个带有指针成员变量的类。该指针应指向可以堆栈分配或堆分配的对象。但是,此指针不应具有任何所有权。换句话说,当指针超出范围时,根本不应该调用delete。我认为原始指针可以解决问题...但是,我不确定是否有比原始指针更好的C ++ 11方法?

示例:

class foo{
public:
    bar* pntr
};

int main(){
    bar a;
    foo b;
    b.pntr=&a;
}

5 个答案:

答案 0 :(得分:18)

原始指针在这里完全没问题。 C ++ 11没有任何其他“哑”智能指针处理非拥有对象,所以你不能使用C ++ 11智能指针。有一个针对非自有对象的“愚蠢”智能指针的提议:

Express docs

已经通过实验实施为http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4282.pdf(感谢@ 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::experimental::observer_ptr

正如Orbit中的@Lightness Races所提到的,std::reference_wrapper也可能是一个解决方案,因为后者也是一个非拥有的智能指针。但是,std::weak_ptr只能由std::weak_ptr或其他std::weak_ptr构建。一个严重的缺点是std::shared_ptr是一个“重”对象(因为内部引用计数机制)。请注意,即使在这种情况下,std::shared_ptr也必须有一个简单的自定义删除器,否则它会破坏堆栈以指示自动变量。

答案 1 :(得分:3)

在这里使用原始指针是完全正确的,因为您不打算让指针拥有指向的资源的所有权。

答案 2 :(得分:0)

如果通过&#34;更好的方法&#34;你是说&#34;更安全的方法&#34;然后是的,我已经实施了一个非拥有的&#34;智能指针在这里: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&#34;更聪明&#34;而不是原始指针,它知道什么时候目标被摧毁。例如:

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。当你有机会在堆栈上分配目标对象时,要低得多。它仍然相当新,但还没有很好的文档记录,但是该库包含了它的使用注释示例(在文件&#34; msetl_example.cpp&#34;,下半部分)。

该库还提供了TRegisteredPointerForLegacy,它比TRegisteredPointer慢一些,但在几乎任何情况下都可以用作原始指针的替代品。 (特别是它可以在完全定义目标类型之前使用,而TRegisteredPointer则不然。)

就您的问题的情绪而言,我认为它是有效的。到目前为止,C ++程序员至少可以选择避免无效内存访问的不必要风险。原始指针也可以是一个有效的选项,但我认为它取决于上下文。如果它是一个复杂的软件,其中安全性比性能更重要,那么更安全的替代方案可能会更好。

答案 3 :(得分:0)

原始指针的问题在于无法判断它是否仍指向有效对象。幸运的是,std::shared_ptr有一个aliasing constructor可用于有效地为具有自动存储持续时间的类成员创建std::weak_ptr。例如:

#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到对象的事实。即你不能专门用堆栈上分配的对象来做这件事。

答案 4 :(得分:-3)

简单地动态分配对象并使用shared_ptr。是的,它实际上会删除该东西,但前提是它是带引用的最后一个。此外,它也可以防止他人删除它。这是正确的做法,既可以避免内存泄漏,也可以避免悬空指针。另请查看相关的weap_ptr,如果指针对象的生命周期要求不同,您也可以使用它们。