I'm sorry if this is very simple but I haven't played with C++ for more than 15 years.
Consider this simple example:
A vector contains objects of type A
.
An object of class B
must reference an A
object that resides in the vector. (Edit for clarification - class B
must have a member that references the A
instance)
Back in the day you would just declare an A*
and be done with it, but how would one do it today with smart pointers? I don't want to store shared or unique pointers in the vector, because I don't want the A objects allocated all over the heap. They have to be in the vector itself.
You have a few options, based on your requirements.
A*
There is nothing wrong with having a non-owning raw-pointer in modern C++. If B
needs a nullable reference to A
and the A
can be guaranteed to outlive B
then a raw-pointer is perfectly fine. One reason that the reference might need to be nullable is if you need to default construct B
and then set the reference later:
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&
If the reference does not need to be nullable. If the reference is set in the constructor of B
and never changes then you can use a reference 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>
One problem with using a reference is that you can't reseat the reference to point to a different a
which means you can't have a setA
member function and you can't have an assignment operator B::operator=(const B&)
. You could just use a non-owning raw-pointer and enforce that the raw-pointer should never be null. But the standard library now provides a convenience called std::reference_wrapper
which can not be null like a reference but can be reseated like a pointer:
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
One common case is where the vector
of A
is growing and so it might re-allocate and invalidate references, pointers and iterators. In this case you could store an index to an element in the vector
. This index will not be invalidated when the vector
grows and also you can check the index is within bounds of the vector and not dangling:
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();
}
If you are adding and removing from your vector
of A
then none of these approaches will work and you might need to reconsider your design.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With