将共享指针设置为NULL

时间:2016-09-07 21:07:40

标签: c++ pointers

假设我有以下内容:

int main() {
    int* test = new int;
    *test = 5;
    int* test2 = test;
}

然后,在某个地方,在某个功能中,我为test2释放内存并将其设置为NULL。有没有办法在同一个函数中将test设置为NULL而不将其传递给函数?

编辑:std::shared_ptr无法使用

5 个答案:

答案 0 :(得分:1)

默认情况下,当您将指针传递给函数时,您将传递值的副本:

void f(int* p) {
    // p has the same value as x below, but is independent
    delete p;
    p = nullptr;
    // p is null, but back in main 'x' still has the original value
}

int main() {
    int* x = new int;
    f(x);
    // 'x' is unmodified and now points to a deleted block of memory
}

您的选择是通过引用传递指针或将指针传递给指针:

#include <iostream>

void by_pointer(int** p) {
    delete *p;
    *p = nullptr;
}

void by_reference(int*& p) {
    delete p;
    p = nullptr;
}

int main() {
    int* x = new int;
    by_pointer(&x);
    std::cout << (void*)x << "\n";  // outputs null

    int* y = new int;
    by_reference(y);
    std::cout << (void*)y << "\n";  // outputs null
}

答案 1 :(得分:1)

将指针传递给引用,因为如果你使用了普通的指针,你只能更改指向的值,并且由于两个指针都指向同一个东西,所以不需要调用{两者都是{1}}:

change()

Example

顺便说一句,我推荐std::shared_ptr用于这样的目的,或std::unique_ptr

修改的 上面唯一的问题是#include <iostream> void change(int*& p) { delete p; p = nullptr; } int main() { int* test = new int; *test = 5; int* test2 = test; std::cout << *test; //5 std::cout << *test2; //5 change(test); } 被删除,而不是指向test2,但除非使用智能指针或不同的功能,否则无法更改。

答案 2 :(得分:1)

shared_ptrweak_ptr类完全符合您的要求。由于您无法使用它们,因此您最好的选择是重新实现它们所需的部分。我假设您不需要任何线程安全性,并且您不必关心优化的简单性。如果您这样做,请使用标准库。

您需要一个控制对象。它应该有一个指向真实对象和两个整数的指针,一个是强指针的数量,另一个是弱指针的数量。强指针和弱指针应该有一个指向控制对象的指针。

当强指针被破坏时,减少强指针计数。如果强指针计数为零,则删除该对象并将其指针设置为NULL。如果弱指针计数也为零,则丢弃控制对象。

当弱指针被破坏时,减少弱指针计数。如果两个指针计数均为零,则丢弃控制对象。

复制指针时,必须突破计数。当弱指针被提升为强指针时,碰撞强指针计数并且如果之前为零则操作失败。

这应该足以让你有这个想法。

答案 3 :(得分:1)

如果真的想要这个(虽然我强烈建议您重新考虑您的设计),那么以下内容可能适合您:

我们将指针包装在一个结构/类中,以便能够“挂钩”我们构造和销毁这些指针:

template<typename T>
struct pointer {

因为在释放存储值时,我们还需要修改仍然指向它的所有pointer,我们需要以某种方式跟踪它们。我会说将它们与价值一起存储:

  struct boxing {
    T value;
    std::set<pointer<T> *> references;
  };
  boxing * box;

接下来是构建一个指针。我在这里简化了。您可能希望添加完美转发,构建“空指针”的可能性,等等......

  pointer(T value) : box(new boxing{value}) {
    add_myself();
  }

如您所见,我们“添加自己”(到参考集)。当pointer被破坏时,我们需要再次从该集合中删除自己:

  ~pointer() {
    remove_myself();
  }

在构建副本时,我们只使用原作中的box并添加自己:

  pointer(pointer const & p) : box(p.box) {
    add_myself();
  }

当复制分配给我们时,我们首先需要从当前box中删除自己,使用原文的框并添加自己:

  pointer & operator=(pointer const & p) {
    remove_myself();
    box = p.box;
    add_myself();
  }

我很懒。自己实施移动建设/任务;)

  pointer(pointer &&) = delete;
  pointer & operator=(pointer &&) = delete;

我们希望能够使用pointer,因此我们将转换运算符添加到原始指针:

  operator T*(void) {
    return box ? &(box->value) : nullptr;
  }

最后,释放一个指针。我们将引用集中当前box的所有pointers成员设置为nullptr(这包括我们自己,因此附加指针b),然后删除框:< / p>

  void free() {
    boxing * b = box;
    for (pointer * p : b->references) {
      p->box = nullptr;
    }
    delete b;
  }

哦,最后但并非最不重要的是,添加和删除自己:

  private:
  void remove_myself() {
    if (box == nullptr) return;
    box->references.erase(this);
    if (box->references.size() == 0) {
      delete box;
    }
  }
  void add_myself() {
    if (box == nullptr) return;
    box->references.insert(this);
  }
};

一些功能。请注意,我传递值以强制执行另一个副本构造:

void foo(pointer<int> p) {
  p.free();
}

两个pointers,指向同一个盒装值:

int main(int, char **) {
  pointer<int> a{21};
  pointer<int> b = a;
  *b = 42;
  std::cout << *a << std::endl;
  foo(a);
  std::cout << "a is " << ((a == nullptr) ? "null" : "non-null") << std::endl;
  return 0;
}

Above example on ideone.

答案 4 :(得分:1)

独特拥有对象的共享控制器的想法当然是可怕的(原因将变得清晰)。

尽管如此,可以做到:

template<class T, class Deleter = std::default_delete<T>>
struct shared_unique
{
    struct control_block
    {
        control_block(Deleter del, T* p) : del_(std::move(del)), ptr_(p), refs_(1) {}

        Deleter del_;
        T* ptr_;
        std::size_t refs_;

        void addref()
        {
            ++refs_;
        }

        void release()
        {
            if (--refs_ == 0)
                delete this;
        }

        ~control_block() {
            if (ptr_)
                del_(ptr_);
        }
    };

    control_block* ctrl_;

    shared_unique(T* p = nullptr, Deleter del = Deleter()) : ctrl_(new control_block(std::move(del), p)) {}
    shared_unique(shared_unique const& r) : ctrl_(r.ctrl_) { ctrl_->addref(); }
    shared_unique& operator=(shared_unique const& r)
    {
        auto tmp = r;
        swap(r);
        return *this;
    }

    shared_unique(shared_unique&& r) : ctrl_(r.ctrl_) { r.ctrl_ = nullptr; }
    shared_unique& operator=(shared_unique&& r)
    {
        auto tmp = std::move(r);
        swap(tmp);
        return *this;
    }

    ~shared_unique()
    {
        ctrl_->release();
    }

    void swap(shared_unique& r) noexcept
    {
        std::swap(ctrl_, r.ctrl_);
    }

    void reset(T* p = nullptr)
    {
        std::swap(ctrl_->ptr_, p);
        delete p;
    }

    T* get() const {
        return ctrl_->ptr_;
    }
};

int main()
{
    shared_unique<int> su1(new int(5));
    assert( su1.get() );
    assert( *(su1.get()) == 5 );

    shared_unique<int> su2 = su1;
    assert( su2.get() );
    assert( *(su2.get()) == 5 );

    su1.reset();
    assert( su1.get() == nullptr );
    assert( su2.get() == nullptr );
}

问题是除非你提供某种“锁定”机制来保持指向对象在被访问时保持活动状态,否则不可能使这种安排成为线程安全的。

如果你想知道一个物体什么时候被摧毁,最好让它(或它的智能指针)在发生这种情况时发出信号并让感兴趣的观察者在插槽(或类似物)上进行侦听。