This is a bad start:
ReferenceManager::ReferenceManager(int capacity):
arraySize(capacity)
{
Reference* arrayOfreferencePointers[capacity];
references = arrayOfreferencePointers;
}
You have declared arrayOfreferencePointers
within a function. This is called an automatic variable , and it gets destroyed when the function exits. This will leave your class member references
as a dangling pointer, that is, a pointer that does not point to a valid object.
Instead you need to use dynamic allocation for your array. The best way to do this is to use a class which manages the dynamic allocation. You could write your own, but there is one to do this in the standard library already, it is called std::vector
. You would replace References **references;
with std::vector<Reference *> references;
.
All of these pointers are pointing to NULL right now.
You never initialized any of them, so they are all wild pointers. You needed to use an initializer to set them to null. However if you follow my vector suggestion then you don't need to do this; growing a vector causes the new items to be initialized properly. (defaulting to null pointer in the case of pointers).
The insert
function could remain as you have it (with the bracketing fixed as the other answers show); however it would lead to tidier code to grow the vector as it is needed, instead of doing a null pointer search.
NB. If you only ever instantiate a ReferenceManager
with a size known at compile-time then this opens up another approach: making capacity
be a template parameter, and using a class member array instead of a vector.
Speaking more generally about the design now, this is not a good idea:
insert(Reference &reference)
The reason is that, by convention, a function that accepts something by reference should not store any references to that object; it should only access the object for the duration of the function. Otherwise you may generate dangling references again. For example consider this code:
void func()
{
Reference bob;
manager->insert(bob);
// oops...
}
Now the manager will contain a dangling reference, because bob
has been destroyed but bob's address lives on inside the manager. This leads to disaster.
To indicate that you may store the object it'd be better to have the insert function accept a pointer. This serves as self-documentation that the function may store the pointer. Of course, someone could still write:
Reference bob;
manager->insert(&bob);
but at least this is more likely to raise a red flag to someone reading your code who is familiar with the conventions I have described.
It would be expected that passing &bob
may store the address; but passing bob
would store a copy.
Recapping that; it's important to think about the lifetime of objects that your ReferenceManager is referring to. You've basically got two options:
- The manager also manages the lifetimes
- The lifetimes are managed elsewhere and you make sure never to destroy an object while the manager holds a reference to the object.
The first option would be done by having your container store smart pointers.