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

时间:2018-04-11 23:04:49

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

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

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

3 个答案:

答案 0 :(得分: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]);
}

参考A&

如果引用不需要为空。如果引用是在B的构造函数中设置的,并且永远不会更改,那么您可以使用引用A&

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

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

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

vector

中元素的索引

一个常见的情况是vector的{​​{1}}正在增长,因此它可能会重新分配并使引用,指针和迭代器无效。在这种情况下,您可以将索引存储到A中的元素。当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(); } vector A,那么这些方法都不会有效,您可能需要重新考虑您的设计。

答案 1 :(得分:3)

智能指针的主要目的是管理生命周期 观察者(即非拥有指针)仍然可以是原始指针。

答案 2 :(得分:1)

显而易见的答案是最简单的,存储对象的索引,而不是地址。