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

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

我在我的游戏中为灯光创建了一个工厂 class,其中函数 "create" 创建了一个 unique_ptr 并将其存储在 STL 向量中,因此我可以收集灯光数据并将它发送到着色器,然后它 returns 对向量中 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 容器中的匹配指针值。这是预期的行为吗?如果是这样,why/how 有效吗?

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

的侥幸。

因为 std::vector<std::unique_ptr<Light>>Light 的所有者,return 引用这些而不是指向 Light 的指针几乎没有用处。这些引用仅在您需要替换值时才有用。我只是根据使用对象而不是拥有对象的 Light*Light const* 来旅行。您可以使用 get() 方法获取 std::unique_ptr<T> 持有的指针。

可以 return 一个 simple_ptr<Light> 而不是 return 一个 Light*,其中 simple_ptr<T> 本质上只是有一个构造函数采用 T* 和运算符 operator->() 其中 return 是 T*operator*() 其中 return 是 T&:

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();
}

或者,如果您希望矢量主要负责这些灯的生命周期,return 一个 std::weak_ptr,它知道什么时候悬空。

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

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

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

vector<Light> myVector;

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

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

此资源的使用者可以做什么?能消光吗?

而vector又能给消费者带来什么保障呢?这些指针是否会在列表的生命周期内被使用。

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