std::unique_ptr 及其对 STL 容器中匹配指针的影响

std::unique_ptr and its effect on matching pointers in STL containers

本文关键字:指针 影响 STL unique ptr std      更新时间:2023-10-16

我在游戏中为灯光创建了一个工厂类,其中函数"create"创建一个unique_ptr并将其存储在 STL 向量中,因此我可以收集灯光数据并将其发送到着色器,然后它返回对向量中该unique_ptr的引用,如下所示:

std::unique_ptr<Light>& create() {
    myVector.push_back(std::unique_ptr<Light>(new Light));
    return &myVector.back();
}

但是,当我添加更多光源时,unique_ptr引用(不是原始指针)将失效,因为它引用了矢量中的unique_ptr对象。 因此,在创建 unique_ptr 引用后传递它会导致错误。 所以,我想出了这个解决方案:

std::unique_ptr<Light> create() {
    Light* light = new Light();
    myVector.push_back(light);
    return std::unique_ptr<Light>(light);
}

这解决了失效问题,但现在,当unique_ptr超出范围时,它将 myVector 中的原始指针替换为矢量中的下一个指针。 所以,如果我有一个指针向量:[1, 2, 3],那么在unique_ptr超出范围后,向量就变成了[2,2,3]。 当然,我不想要冗余数据,所以我用 STL 集替换了向量。 现在,当unique_ptr超出范围时,集合会查找冗余数据并删除一个副本,因此集合正确包含指针:[2, 3]。 这有效,我没有收到任何错误,但我不明白unique_ptr为什么或如何更改 STL 容器中的匹配指针值。 这是预期行为吗? 如果是这样,为什么/如何工作?

PS:我知道unique_ptr的目的是使指针由单个对象拥有。 因此,我更喜欢将唯一的所有权保留在调用 create() 函数的对象中。 因此,工厂只是简单地创建灯光并存储对其创建的灯光的引用,以便于收集,尽管工厂本身完成的存储只是一种临时措施。 但是,我喜欢 STL 集在超出范围时自动释放unique_ptr自动维护的性质。 所以,我想保留这种通用设计,前提是它不是unique_ptr的侥幸。

由于std::vector<std::unique_ptr<Light>>Light s 的所有者,因此返回对这些引用而不是指向 Light s 的指针几乎没有用处。仅当需要替换值时,引用才有用。我只是按照Light*Light const*旅行,这些物品是使用而不是拥有的。您可以使用 get() 方法获取std::unique_ptr<T>持有的指针。

您可以返回一个simple_ptr<Light>,而不是返回Light*,其中simple_ptr<T>本质上只有一个构造函数接受T*和运算符operator->()返回一个T*和返回T&operator*()

template <typename T>
class simple_ptr {
    T* ptr;
public:
    explicit simple_ptr(T* ptr): ptr(ptr) {}
    T* operator->() const { return this->ptr; }
    T& operator*() const { return *this->ptr; }
};

生成的函数只是做正确的事情(即根据需要复制指针)。如果不跳过箍,就不可能delete这个指针。如果为Light类型提供自定义一元operator&()则可能会使显式访问指针变得更加困难。不过,Tt并非不可能,因为您可以随时致电ptr.operator->()来获取它。但是,你应该只防御墨菲,而不是马基雅维利。

既然你不想在工厂函数的调用方向量的指针有指向它的时候删除对象,为什么不使用std::shared_ptr呢?

你试图拥有std::unique_ptr的多个副本,从而与的设计相矛盾。

这可以通过使用std::shared_ptr来解决。

std::shared_ptr<Light> create() {
    myVector.push_back(std::make_shared<Light>());
    return myVector.back();
}

或者,如果您希望矢量主要负责这些光源的生命周期,请返回一个std::weak_ptr,它知道它何时悬空。

std::weak_ptr<Light> create() {
    myVector.push_back(std::make_shared<Light>());
    return myVector.back();
}

智能指针是关于解决资源所有权问题的。这并不意味着您总是使用shared_ptr而从不使用"原始"指针。

您似乎想要一个光源矢量,该矢量负责资源。为此,您甚至不需要指针,因为向量在内部处理指针。

vector<Light> myVector;

您可以从向量或此创建函数的返回中获取此资源的句柄。

const Light& create() {
      Light light;
      myVector.push_back(light);
      return myVector.back();
}

允许此资源的使用者做什么?它可以删除灯光吗?

载体能给消费者带来什么保证? 这些指针是否会在列表的生存期内使用。

  • 传递unique_ptr意味着传递所有权。
  • 创建shared_ptr意味着删除最后一个引用时,将清理资源。
  • weak_ptr可用于不关心资源是否被清理的观察者。
  • 原始指针和引用不关心任何事情。但是,如果您只是就地获取句柄并读取和写入该实例,则无需处理share_ptr开销。