Assume I have a vector of points vector<obj*> v and I need to free the
memory.
More over, some of the element in v are already deleted (null
pointer).
Is it ok if I do like this:
for (vector<Obj*>::iterator it(v.begin()), ite(v.end()); it!= ite; +
+it{
if (*it != 0){
delete *it;
}
}
Thanks a lot.
zl2k
a) Deleting the pointers will not invalidate the iterator. Thus, the idea in
itself is sound.
b) The check for 0 before the delete is unnecessary: deleting a 0 pointer is
a null-op.
c) You might want to do "*it = 0" after the delete: for one thing, you seem
to be using the convention of signalling by 0 that memory has been
deallocated (although that is a questionable practice); also it can be
problematic to keep singular pointer values inside the vector: when the
vector reallocates those values will be moved around and that causes
undefined behavior.
Best
Kai-Uwe Bux
Looks fine, but can be extremely simplified (apart that you mistyped
something up there):
for(int i = 0; i < v.size(); ++i) {
delete v[i];
}
You can safely call delete on a null pointer.
--
FSC - http://userscripts.org/scripts/show/59948
http://fscode.altervista.org - http://sardinias.com
Having invalid pointers into a vector of pointers and reallocating the
vector really does call for UB? I'm not sure I'm catching the point
(I've read that assertion before, but I still haven't wrapped my mind
around this, I'd like to make it clear once for all).
Besides, the advice of zeroing the pointer is good, but the best would
be to simply drop the vector or to clear it out, since all the pointers
it contains are dangling or null, by now.
It does in C++03. I am not sure about C++0x as that will have move
semantics. As of now, reallocating a vector involves assignment or copy-
construction, either of which necessitate an lvalue-rvalue conversion (maybe
a move will do that too). Although the language in [4.1/1] leaves out
singular values, it seems pretty clear that the intent is to make it UB to
invoke an lvalue-rvalue conversion on an invalid pointer. Under the hoods,
that happens when a vector of pointers reallocates and an invalid pointer is
involved.
There is some discussion / disagreement / open question as to whether
_purely and absolutely formally_ even the mere existence of singular values
in a pointer triggers undefined behavior. A common sense reaction is,
however, to not bother unless actual operations on the vector are performed
that may reasonably invoke UB. This suffices to stay clear of UB on all
implementations known to man (or at least in this forum, nobody ever pointed
out a less sane implementation).
> I'm not sure I'm catching the point
> (I've read that assertion before, but I still haven't wrapped my mind
> around this, I'd like to make it clear once for all).
>
> Besides, the advice of zeroing the pointer is good, but the best would
> be to simply drop the vector or to clear it out, since all the pointers
> it contains are dangling or null, by now.
I felt the urge to point out possible pitfalls since we do not know what
happens to the vector afterwards. I agree that the cleanest would be, to
clear out the vector or to have it destroyed.
Best
Kai-Uwe Bux
I see, I've also read some threads I've found hanging here and there, it
seems that this issue gets raised from time to time and the debates that
it generates might not be worth the time spent on it - as the practical
advantages of such a case being "well defined" would be around zero.
Now I've understood the issue and I'm happy considering as "definitive
UB" the fact of doing anything with a dangling pointer other than
zeroing it or assigning a valid pointer to it, thank you for your
explanation Kai-Uwe.
That's curious. And it doesn't fit with my mental model of how the
language works under the hood.
I have no trouble understanding why accessing through an invalid pointer
is UB.
In the case of the vector of pointers (vector<Obj*> in the OP) why would
copying an entry cause the pointer to be de-referenced?
And what would happen if the data in the vector was a union, say of an
int and a pointer - which are the same size in most architectures?
Andy
Dereferencing an invalid pointer is not the only UB with regards to
invalid pointers. Simply reading the value is UB, AIUI.
The only defined operations on an invalid pointer: set it to null, or
set it to a valid location.
--
Daniel Pitts' Tech Blog: <http://virtualinfinity.net/wordpress/>
It does not. However, dereferencing an invalid pointer is not the only way
of using an invalid pointer to yield UB. See [4.1/1].
Consider:
int* p = new int();
delete p;
int* q ( p );
That is UB even though p is not dereferenced. Similarly, and for essentially
the same reason, the following is UB:
int* p;
int* q ( p );
> And what would happen if the data in the vector was a union, say of an
> int and a pointer - which are the same size in most architectures?
Still UB. If you write a pointer to the union, even reading the value as an
int is UB. If the pointer value is singular, copying is UB even if the
value, interpreted as an int, would not be singular. The point is that the
union at this point holds an object of type int* with a singular value. That
triggers [4.1/1]. Anyhow, a conforming program can not see that the value as
an int would be regular for reading that value is UB.
Best
Kai-Uwe Bux
In addition to the other replies, sizeof(int) != sizeof(void*) on most
64 bit architectures.
--
Ian Collins
You said : also it can be
problematic to keep singular pointer values inside the vector
How can this be solved?
ankur
[...]
> You said : also it can be
> problematic to keep singular pointer values inside the vector
>
> How can this be solved?
By not doing it :-)
Depending on the case, this ranges from easy to very hard. The easy case is
when the vector contains pointers whose values occur nowhere else in the
program (or the vector is the sole owner of all pointees of its elements).
In this case, you have control over invalidating operations and you can
remove singular values from the vector as (or before) they arise.
Problematic is when pointer values inside the vector can be invalidated by
operations elsewhere. This is a general problem with pointers. If you have
two pointer object sharing the same value, calling delete on one of them
also invalidates the value of the other. Even if you destroy or null the
pointer which you delete, the other hangs around just waiting to cause UB.
This is especially tricky as you have no way of telling by looking at a
pointer whether its value is singular. So, the best thing is to take care of
all "aliases" of a pointer at the time you want to delete one of them. How
to handle this problem, however, depends on the application. The main lesson
here is that the vector<T*> case is not really special.
Best
Kai-Uwe Bux
I know of no implementation where this won't work, but I think a
better thing is:
// Everyone and their mother already have
// a variant of code below, right? RIGHT? ;-)
template<typename ptr>
void del_null(ptr& val)
{
ptr temp = val;
val = 0;
delete temp;
}
template<class container>
void del_null_container_elements(container& c)
{
std::for_each(c.begin(), c.end(), &del_null<container::value_type>);
}
template<class container>
void del_null_container_elements(container& c)
{
delete_container_elements(c);
c.clear();
}
Goran.
It is not. As other said, it is UB.
Better way would be to use something like SharedPtr instead of raw
pointer. Then you can remove the element, and let the smart pointer do
the deallocation.
> Francesco S. Carta wrote:
>>
>> Besides, the advice of zeroing the pointer is good, but the best would
>> be to simply drop the vector or to clear it out, since all the
>> pointers it contains are dangling or null, by now.
>>
>
> It is not. As other said, it is UB.
What "it is not", and what "it is UB", regarding the text you quoted
from me? What I suggested above is exactly about _avoiding_ UB.
> Better way would be to use something like SharedPtr instead of raw
> pointer. Then you can remove the element, and let the smart pointer do
> the deallocation.
Smart pointers are a good suggestion.
[Snipped too much - restoring now]
> c) You might want to do "*it = 0" after the delete: for one thing,
> you seem to be using the convention of signalling by 0 that memory
> has been deallocated (although that is a questionable practice);
> also it can be problematic to keep singular pointer values inside
> the vector: when the vector reallocates those values will be moved
> around and that causes undefined behavior.
>>>
>>> Besides, the advice of zeroing the pointer is good, but the best would
>>> be to simply drop the vector or to clear it out, since all the
>>> pointers it contains are dangling or null, by now.
>>>
>>
>> It is not. As other said, it is UB.
>
> What "it is not", and what "it is UB", regarding the text you quoted
> from me? What I suggested above is exactly about _avoiding_ UB.
Advise to use raw pointers is bad. Specially to do the suggestion c, as
that is UB (nicely explained by Kai-Uwe Bux). It would most likely work
as expected, but using raw pointers complicates code.
Ah, I understand now. Yes, raw pointers are dangerous, but are sometimes
a necessary evil, paraphrasing the C++ FAQ.
I think I've found something interesting about all of this stuff, I'll
post it as a separate topic because I think it needs the appropriate
attention.
Here the term "singular value" is used to mean "invalid pointer", as it also is
some places in the standard.
However AFAICS it's not defined in the standard.
Is it defined? And what the heck does it /mean/ (that leads to its use for
invalid pointer or iterator values)?
Cheers,
- Alf
--
blog at <url: http://alfps.wordpress.com>
... or destroy it, probably. :)
(Otherwise, { int* p; } would invoke UB... or does it?)
So, do we really need to set the pointers to null before destroying
the vector?
--
Seungbeom Kim
> On 2010-09-02 15:50, Daniel Pitts wrote:
>>
>> The only defined operations on an invalid pointer: set it to null, or
>> set it to a valid location.
>
> ... or destroy it, probably. :)
> (Otherwise, { int* p; } would invoke UB... or does it?)
No it doesn't, because there is no lvalue to rvalue conversion in your
example.
> So, do we really need to set the pointers to null before destroying
> the vector?
As it seems, formally, yes [*], we need to nullify them, because the
actions performed by the vector during reallocation or during
destruction might lead to that lvalue to rvalue conversion, thus calling
UB into play.
In practice there seems to be no danger on most architectures, but read
all the other posts to get deeper insight, and read the "UB while
dealing with invalid raw pointers, the std::uninitialized_fill case"
thread too (Bo Persson's posts in particular).
[*] In layman terms, the point is that a pointer pointing to
appropriately allocated space (even if that space doesn't contain any
valid object) is safe to be copied and compared (but not dereferenced),
while a pointer pointing to {unallocated/random/deallocated} space is a
timed bomb. But once more, see the other posts from more knowledgeable ones.
> > So, do we really need to set the pointers to null before destroying
> > the vector?
> As it seems, formally, yes [*], we need to nullify them,
> because the actions performed by the vector during
> reallocation or during destruction might lead to that lvalue
> to rvalue conversion, thus calling UB into play.
Formally, you need to nullify the pointer in the vector before
deleting it, since nullifying it (assigning NULL to it) involves
accessing into the vector, which could theoretically lead to an
lvalue to rvalue conversion. Practically, of course, there
won't be any problems, even on architectures where the lvalue to
rvalue conversion can cause problems.
--
James Kanze
[...]
> Ah, I understand now. Yes, raw pointers are dangerous, but are
> sometimes a necessary evil, paraphrasing the C++ FAQ.
This is a widespread myth. Globally, raw pointers are probably
less dangerous than std::shared_ptr; at least, the object
doesn't get deleted unless you explicitly ask for it. Other
shared pointers fare better, but in any reasonable application,
most pointers are used for navigation, and there's absolutely no
reason to use a smart pointer for that.
--
James Kanze
> On Sep 6, 10:16 am, "Francesco S. Carta"<entul...@gmail.com> wrote:
>> Seungbeom Kim<musip...@bawi.org>, on 06/09/2010 01:50:09, wrote:
>
>>> So, do we really need to set the pointers to null before destroying
>>> the vector?
>
>> As it seems, formally, yes [*], we need to nullify them,
>> because the actions performed by the vector during
>> reallocation or during destruction might lead to that lvalue
>> to rvalue conversion, thus calling UB into play.
>
> Formally, you need to nullify the pointer in the vector before
> deleting it, since nullifying it (assigning NULL to it) involves
> accessing into the vector, which could theoretically lead to an
> lvalue to rvalue conversion.
So to be formally fine I need something like this...
vector<sometype*> vec;
vec.push_back(new sometype);
sometype* ptr = vec[0];
vec[0] = 0;
delete ptr;
...or variations thereof. Thank you for the correction.
> Practically, of course, there
> won't be any problems, even on architectures where the lvalue to
> rvalue conversion can cause problems.
And you say that because you're confident that most decent
implementations out there will not perform the conversion during the
element assignment - otherwise, your mention about program crashes,
else-thread, would conflict with this statement.
> James Kanze <james...@gmail.com>, on 06/09/2010 10:10:33, wrote:
>
>> On Sep 6, 10:16 am, "Francesco S. Carta"<entul...@gmail.com> wrote:
>>> Seungbeom Kim<musip...@bawi.org>, on 06/09/2010 01:50:09, wrote:
>>
>>>> So, do we really need to set the pointers to null before destroying
>>>> the vector?
>>
>>> As it seems, formally, yes [*], we need to nullify them,
>>> because the actions performed by the vector during
>>> reallocation or during destruction might lead to that lvalue
>>> to rvalue conversion, thus calling UB into play.
>>
>> Formally, you need to nullify the pointer in the vector before
>> deleting it, since nullifying it (assigning NULL to it) involves
>> accessing into the vector, which could theoretically lead to an
>> lvalue to rvalue conversion.
>
> So to be formally fine I need something like this...
>
> vector<sometype*> vec;
> vec.push_back(new sometype);
> sometype* ptr = vec[0];
> vec[0] = 0;
> delete ptr;
>
> ...or variations thereof. Thank you for the correction.
Well, that is a somewhat contested issue. More importantly, though, it is
based upon an interpretation of language that is subject to heavy changes in
C++0x. My preliminary understanding of is that a contorted work-around like
the above is not necessary under the C++0x rules.
>> Practically, of course, there
>> won't be any problems, even on architectures where the lvalue to
>> rvalue conversion can cause problems.
>
> And you say that because you're confident that most decent
> implementations out there will not perform the conversion during the
> element assignment - otherwise, your mention about program crashes,
> else-thread, would conflict with this statement.
An implementation would have to be deliberately mean and waste resources on
being mean to trigger lvalue-rvalue conversions for no reason.
Best
Kai-Uwe Bux
For your very words, it has to be proved formally necessary even in
C++03, for that matter.
>>> Practically, of course, there
>>> won't be any problems, even on architectures where the lvalue to
>>> rvalue conversion can cause problems.
>>
>> And you say that because you're confident that most decent
>> implementations out there will not perform the conversion during the
>> element assignment - otherwise, your mention about program crashes,
>> else-thread, would conflict with this statement.
>
> An implementation would have to be deliberately mean and waste resources on
> being mean to trigger lvalue-rvalue conversions for no reason.
Uhm... the standard does not forbid implementations to be mean, does it?
ehehehehe ;-)
Kidding apart, I'm just interested in the process of clarifying dark
corners of the standard, in these very posts, and about this, I'd like
to read some elaboration on your first sentence above.
If I understand you correctly, your reading of the standard does not
allow a std::vector implementation to perform lvalue to rvalue
conversions during element assignment, is that what you meant?
Ok, for C++0x some of the issues for interpreting the standard are:
a) How does 4.1/1 imply that lvalue-rvalue conversion of invalid pointers is
UB? The wording is:
If the object to which the lvalue refers is not an object of type T and is
not an object of a type derived from T, or if the object is uninitialized,
a program that necessitates this conversion has undefined behavior.
Code example:
int* p = new int();
delete p;
int* q = p; // lvalue-rvalue conversion
So, in this example, is the object to which p refers not of type int*? or is
the the object analogous to an uninitialized object?
Assuming the first alternative, we find that
std::vector< int* > v;
v.push_back( new int () );
delete v.back();
creates a vector whose elements are not objects of type int*. In fact, it
would be hard to say what the type of the stored object is. In particular,
is the type of the object in the vector CopyConstructible?
b) How to precisely interpret CopyConstructibility (table 30). E.g.,
int* p = new int();
delete p;
// is int* CopyConstructible at this point?
If one would arge a strong NO here, we arrive at _very_ unwelcome
consequences:
std::vector<int*> v;
v.push_back( new int () );
int* p = new int ();
delete p;
std::vector<int*> w ( v );
The last line would be UB if int* was not CopyConstructible. :-)
Note: I don't wand to suggest how to resolve these issues of interpretation.
>>>> Practically, of course, there
>>>> won't be any problems, even on architectures where the lvalue to
>>>> rvalue conversion can cause problems.
>>>
>>> And you say that because you're confident that most decent
>>> implementations out there will not perform the conversion during the
>>> element assignment - otherwise, your mention about program crashes,
>>> else-thread, would conflict with this statement.
>>
>> An implementation would have to be deliberately mean and waste resources
>> on being mean to trigger lvalue-rvalue conversions for no reason.
>
> Uhm... the standard does not forbid implementations to be mean, does it?
> ehehehehe ;-)
No, it does not :-)
> Kidding apart, I'm just interested in the process of clarifying dark
> corners of the standard, in these very posts, and about this, I'd like
> to read some elaboration on your first sentence above.
>
> If I understand you correctly, your reading of the standard does not
> allow a std::vector implementation to perform lvalue to rvalue
> conversions during element assignment, is that what you meant?
No, that's not what I mean. Consider:
reference operator[] ( size_type n ) {
value_type dummy ( the_data[n] );
return ( the_data[n] );
}
I don't see any language in the standard that would render the above a non-
conforming implementation.
What I mean is: no sane implementation does that.
Best
Kai-Uwe Bux
So, that means { delete a[i]; } is fine just before destroying an array
a, but for vectors even { delete v[i]; v[i] = 0; } is insufficient,
and we should do { T* p = v[i]; v[i] = 0; delete p; } ...?
Isn't this an unnecessary burden for the users of vector? Isn't it
against the C++ design rule "Provide as good support for user-defined
types as for built-in types."?
(It is already a difficult task to make people convinced that it is UB
just to read the value of, not to dereference, an invalid pointer. Sigh.)
> Practically, of course, there
> won't be any problems, even on architectures where the lvalue to
> rvalue conversion can cause problems.
So I'm wondering; if some extra steps have to be done just to avoid
invoking UB formally but not doing so will cause no problems on any
real implementations in practice, then why not let the standard reflect
the reality and make it officially acceptable not to do the extra steps?
I.e. by specifying that an element access for a vector as an lvalue
shouldn't cause an lvalue-to-rvalue conversion, and that neither should
the destruction of the vector.
--
Seungbeom Kim
I think that would work. :-)
At first millisecond I thought the last requirement would be problematic as
stated, but destruction of an object of class type doesn't involve lvalue to rvalue?
Disclaimer: just out of bed and no coffee yet.
Uh... we have quite some unresolved interpretation issues with the
current standard, I was somehow hoping that some of those issues were
going to be dissipated in the upcoming one...
Ah, but as you put it in first place, it seemed that you were speaking
about the formal interpretation under C++03.
All right, as far as I can tell, the weird and contorted work-around is
still necessary to formally avoid any potential UB under the current
standard.
At the practical stage, I will happily send to death any vector of
dangling pointers without many worries ;-)