An STL vector collection of C++ interface pointers?

H

He Shiming

Hi,

I've developed a class that implements an interface definition. It looks
like this:

class IRecord {
public:
// define interface methods by pure virtual methods
// no member variables
}
class CRecord : public IRecord {
public:
// implement interface methods
}

Now, I'm creating a collection of the interface pointers using:
std::vector<IRecord*> vRecPtrs; The following code is used to create a
series of interface pointers and push them into the vector:

std::vector<IRecord*> vRecPtrs;
vRecPtrs.resize (vRecPtrs.size()+1);
vRecPtrs[vRecPtrs.size()-1] = (IRecord*) new CRecord();
// well, I do have to resize the vector to get a new position before I can
create the new interface instance in my actual case. I can't use
vector::insert, nor vector::push_back.

So I pushed 100 instances to the vector. I iterate through all of them,
calling their methods, and they turned out fine.

However, something will screw up after I call vector::erase. In a case, I
have to remove an item from the collection, say:

delete vRecPtrs[10];
vRecPtrs.erase(vRecPtrs.begin()+10);

The 10th instance will be removed successfully. But the last item
(vRecPtrs.size()-1) will no longer be a valid pointer. Accessing any methods
of the last item will result in an access violation reading crash. I checked
the the pointer value of the last item through debuggers, and it does look
like a real pointer. Its value is expected, and does look like others. But
why can't I use it anymore? Why is the last item? Should I use some "safer
vector" for pointers?

Thanks
 
V

Victor Bazarov

He said:
I've developed a class that implements an interface definition. It looks
like this:

class IRecord {
public:
// define interface methods by pure virtual methods
// no member variables
}
class CRecord : public IRecord {
public:
// implement interface methods
}
>
Now, I'm creating a collection of the interface pointers using:
std::vector<IRecord*> vRecPtrs; The following code is used to create a
series of interface pointers and push them into the vector:

std::vector<IRecord*> vRecPtrs;
vRecPtrs.resize (vRecPtrs.size()+1);

Here you essentially say

vRecPtrs.resize(1);

because the size is 0 when a vector is default-initialised.
vRecPtrs[vRecPtrs.size()-1] = (IRecord*) new CRecord();

There is no need to cast it. There is an implicit conversion from
a pointer to CRecord to a pointer to IRecord. Try to get into habit
of _avoiding_ C-style casts at all costs (pun intended).
// well, I do have to resize the vector to get a new position before I can
create the new interface instance in my actual case. I can't use
vector::insert, nor vector::push_back.

Why can't you?
So I pushed 100 instances to the vector. I iterate through all of them,
calling their methods, and they turned out fine.

However, something will screw up after I call vector::erase. In a case, I
have to remove an item from the collection, say:

delete vRecPtrs[10];
vRecPtrs.erase(vRecPtrs.begin()+10);

The 10th instance will be removed successfully. But the last item
(vRecPtrs.size()-1) will no longer be a valid pointer.

What does that mean?
> Accessing any methods
of the last item will result in an access violation reading crash.

Are you hanging onto a reference to that element in the vector?
> I checked
the the pointer value of the last item through debuggers, and it does look
like a real pointer. Its value is expected, and does look like others. But
why can't I use it anymore? Why is the last item? Should I use some "safer
vector" for pointers?

No. You need to realise that if you remove something from a vector, all
iterators and references to the elements at and after that position in the
vector become _invalid_.

I am guessing here, of course. If you need real help from this newsgroup,
post your real code.

V
 
M

Mike Wahler

He Shiming said:
Hi,

I've developed a class that implements an interface definition. It looks
like this:

class IRecord {
public:
// define interface methods by pure virtual methods
// no member variables
}
class CRecord : public IRecord {
public:
// implement interface methods
}

Now, I'm creating a collection of the interface pointers using:
std::vector<IRecord*> vRecPtrs; The following code is used to create a
series of interface pointers and push them into the vector:

std::vector<IRecord*> vRecPtrs;
vRecPtrs.resize (vRecPtrs.size()+1);
vRecPtrs[vRecPtrs.size()-1] = (IRecord*) new CRecord();
// well, I do have to resize the vector to get a new position before I can
create the new interface instance in my actual case. I can't use
vector::insert, nor vector::push_back.

So I pushed 100 instances to the vector. I iterate through all of them,
calling their methods, and they turned out fine.

However, something will screw up after I call vector::erase. In a case, I
have to remove an item from the collection, say:

delete vRecPtrs[10];
vRecPtrs.erase(vRecPtrs.begin()+10);

The 10th instance will be removed successfully. But the last item
(vRecPtrs.size()-1) will no longer be a valid pointer. Accessing any
methods of the last item will result in an access violation reading crash.
I checked the the pointer value of the last item through debuggers, and it
does look like a real pointer. Its value is expected, and does look like
others. But why can't I use it anymore? Why is the last item? Should I use
some "safer vector" for pointers?

The object type is not the issue, but the fact that when you 'erase'
an elements from a vector, any iterators pointing to it or any elements
after it (i.e. with higher subscripts), become invalid. This is the way
vectors work. One container type you might consider is a 'std::list',
which when an element is erased, only any iterators pointing to the erased
element will be invalidated, any others will remain valid. Otherwise if you
insist upon staying with the vector, you'll need to devise some way of
restoring
any iterators invalidated by an erase operation.

Book recommendation: www.josuttis.com/libbook

-Mike
 
H

He Shiming

Victor Bazarov said:
He said:
I've developed a class that implements an interface definition. It looks
like this:

class IRecord {
public:
// define interface methods by pure virtual methods
// no member variables
}
class CRecord : public IRecord {
public:
// implement interface methods
}

Now, I'm creating a collection of the interface pointers using:
std::vector<IRecord*> vRecPtrs; The following code is used to create a
series of interface pointers and push them into the vector:

std::vector<IRecord*> vRecPtrs;
vRecPtrs.resize (vRecPtrs.size()+1);

Here you essentially say

vRecPtrs.resize(1);

because the size is 0 when a vector is default-initialised.
vRecPtrs[vRecPtrs.size()-1] = (IRecord*) new CRecord();

There is no need to cast it. There is an implicit conversion from
a pointer to CRecord to a pointer to IRecord. Try to get into habit
of _avoiding_ C-style casts at all costs (pun intended).
// well, I do have to resize the vector to get a new position before I
can create the new interface instance in my actual case. I can't use
vector::insert, nor vector::push_back.

Why can't you?
So I pushed 100 instances to the vector. I iterate through all of them,
calling their methods, and they turned out fine.

However, something will screw up after I call vector::erase. In a case, I
have to remove an item from the collection, say:

delete vRecPtrs[10];
vRecPtrs.erase(vRecPtrs.begin()+10);

The 10th instance will be removed successfully. But the last item
(vRecPtrs.size()-1) will no longer be a valid pointer.

What does that mean?
Accessing any methods
of the last item will result in an access violation reading crash.

Are you hanging onto a reference to that element in the vector?
I checked
the the pointer value of the last item through debuggers, and it does
look like a real pointer. Its value is expected, and does look like
others. But why can't I use it anymore? Why is the last item? Should I
use some "safer vector" for pointers?

No. You need to realise that if you remove something from a vector, all
iterators and references to the elements at and after that position in the
vector become _invalid_.

I am guessing here, of course. If you need real help from this newsgroup,
post your real code.

V

I'm not referencing the old index of the elements of course.

I wouldn't call this fake code. The real code is as long as 5000 lines, and
I'm trying to simplify the problem.


Regards,
He Shiming
 
H

He Shiming

Mike Wahler said:
The object type is not the issue, but the fact that when you 'erase'
an elements from a vector, any iterators pointing to it or any elements
after it (i.e. with higher subscripts), become invalid. This is the way
vectors work. One container type you might consider is a 'std::list',
which when an element is erased, only any iterators pointing to the erased
element will be invalidated, any others will remain valid. Otherwise if
you
insist upon staying with the vector, you'll need to devise some way of
restoring
any iterators invalidated by an erase operation.

Book recommendation: www.josuttis.com/libbook

-Mike

I want random access to the contained elements. I would prefer using
vectors. However, I don't understand why iterators are preventing it from
working considering I'm not using any. It works like this:

vRecPtrs[99]->Method(); // vRecPtrs has 100 elements, and this call worked.
vRecPtrs.erase(vRecPtrs.begin()+10);
vRecPtrs[98]->Method(); // this one caused a crash.

Why is that related the iterators? Aren't the pointers the only thing I'm
keeping in this container?

Plus, if like you said, all iterators after it are being invalidated, why
only the last item didn't work?


Best regards,
He Shiming
 
V

Victor Bazarov

He said:
[...]
I'm not referencing the old index of the elements of course.

If you're not using the index (old or new), then how you access the
elements?
I wouldn't call this fake code. The real code is as long as 5000 lines, and
I'm trying to simplify the problem.

Well, once you get it down to 100 lines, and still haven't found the cause
of your troubles, post it here _in_full_.

V
 
V

Victor Bazarov

He said:
[...]
I want random access to the contained elements. I would prefer using
vectors. However, I don't understand why iterators are preventing it from
working considering I'm not using any. It works like this:

vRecPtrs[99]->Method(); // vRecPtrs has 100 elements, and this call worked.
vRecPtrs.erase(vRecPtrs.begin()+10);
vRecPtrs[98]->Method(); // this one caused a crash.

REALLY? The problem is not in the vector, then. It's somewhere else.
Why is that related the iterators? Aren't the pointers the only thing I'm
keeping in this container?

It doesn't seem related to the iterators.
Plus, if like you said, all iterators after it are being invalidated, why
only the last item didn't work?

There is no explanation. Can you trap the "crash" in the debugger and see
what causes it?

V
 
H

He Shiming

Victor Bazarov said:
He said:
[...]
I'm not referencing the old index of the elements of course.

If you're not using the index (old or new), then how you access the
elements?
I wouldn't call this fake code. The real code is as long as 5000 lines,
and I'm trying to simplify the problem.

Well, once you get it down to 100 lines, and still haven't found the cause
of your troubles, post it here _in_full_.

V

Okay I'll try.
 
H

He Shiming

Well, I hate to say this but I have solve the problem in the code deduction
process. It turned out that it wasn't the fault of vector or pointers. It's
the smart pointer mechanism I'm using, which isn't covered in the content of
my question. Sorry for that.

Best regards,
He Shiming
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top