在向量中引用对象(Modern C++)

Jes*_*per 5 c++ smart-pointers c++11

如果这很简单,我很抱歉,但我已经玩了超过15年的C++.考虑这个简单的例子:向量包含类型的对象A.类BA对象必须引用驻留在向量中的对象.(编辑说明 - 类B必须有一个引用该A实例的成员)

回到那一天你会宣布一个A*并完成它,但今天如何使用智能指针做到这一点?我不想在向量中存储共享或唯一指针,因为我不希望在堆上分配A对象.它们必须在矢量本身.

Chr*_*rew 5

根据您的要求,您有几个选择.

非拥有原始指针 A*

在现代C++中拥有一个非拥有的原始指针没有错.如果B需要一个可以为空的引用,A并且A可以保证寿命比较长,B那么原始指针就完全可以了.引用可能需要为空的一个原因是,如果需要默认构造B,然后稍后设置引用:

class B {
  A* a_ = nullptr;
public:
  void setA(A& a) { a_ = &a; }
};

int main() {
  std::vector<A> aVec(3);
  B b;
  b.setA(aVec[1]);
}
Run Code Online (Sandbox Code Playgroud)

参考 A&

如果引用不需要为空.如果在构造函数中设置引用B并且从不更改,那么您可以使用引用A&:

class B {
  A& a_;
public:
  B(A& a) : a_(a) {}
};

int main() {
  std::vector<A> aVec (3);
  B b(aVec[1]);
}
Run Code Online (Sandbox Code Playgroud)

std::reference_wrapper<A>

使用引用的一个问题是您不能重新引用指向不同的引用,a这意味着您不能拥有setA成员函数,并且您不能拥有赋值运算符B::operator=(const B&).您可以使用非拥有的原始指针并强制原始指针永远不应为null.但是标准库现在提供了一个方便的方法std::reference_wrapper,它不能像引用那样为null,但可以像指针一样重新设置:

class B {
  std::reference_wrapper<A> a_;
public:
  B(A& a) : a_(a) {}
  void setA(A& a) { a_ = a; }
};

int main() {
  std::vector<A> aVec (3);

  B b(aVec[1]);
  B otherB(aVec[2]);

  b = otherB;  // use auto-generated assignment operator
  b.setA(aVec[0]);
}
Run Code Online (Sandbox Code Playgroud)

索引到一个元素 vector

一种常见的情况是,其中vectorA增长,所以它可能会重新分配和无效的引用,指针和迭代.在这种情况下,您可以将索引存储到vector.当vector增长时,该索引不会失效,也可以检查索引是否在向量的范围内而不是悬空:

class B {
  std::vector<A>& aVec_;
  int             aIndex_;
public:
  B(std::vector<A>& aVec, int aIndex) : aVec_(aVec), aIndex_(aIndex) {}

  void useA() {
    if (aIndex_ >= 0 && aIndex_ < aVec_.size()) {
      auto& a = aVec_[aIndex_];
      // use a ...
    }
  }
};

int main() {
  std::vector<A> aVec (3);

  B b(aVec, 1);
  b.useA();
}
Run Code Online (Sandbox Code Playgroud)

如果您要添加和删除这些方法vector,A则这些方法都不起作用,您可能需要重新考虑您的设计.