int (*p)[5] = (int (*)[5])new int[5];
How do I delete the allocated memory?
Is it:
a) delete[] p;
b) delete[] *p;
c) delete[] (int*)p;
?? Any ideas??
You have casted the pointer type returned from new into a different pointer, but
regardless, new allocated an array of 5 ints, so you call delete [] on the
pointer that new returned.
delete [] reinterpret_cast<int*>(p);
my correction: should say you have casted the pointer type returned from new
into a different pointer "type"
I'd like to point out that AFAIK the first cast may result in a loss
of information. You're only guaranteed round-way trips through void*,
char*, and unsigned char*, (and an integer type of sufficient size).
Thus the delete[] could be UB on a conforming platform. That's right,
right?
i not use delete-new i use always malloc()-free()
or my version of these functions
I think that c) but why you reinterpret_cast at first place?
I start to wonder do you ever use raw dynamic arrays and if then why
do you use them? I don't. In C code I do but there are allocated with
malloc() and deallocated with free(). Usage of "delete[]" itself
usually indicates that some novice in team has copy-pasted something
cheesy from google or from a book into our code base.
In C++ it is often clear how a particular container of T's should be
used (as stack or as set or as queue or as vector). Sometimes it is
unclear. I usually take std::vector<T> first when it is unsure how
container will be used. Later it settles and usually it remains as
such for ages after.
Sometimes very rarely it later becomes apparent that a container
causes performance or maintainability issues related to nature or
usage of it. Then i revisit what exact underlying container type is
used. Refactoring vector into some other container may make sense at
that spot.
The chosen type depends on what is done with container and what is not
done with it. There are so lot of somewhat similar to vector
containers to choose. boost::array<T>, boost::scoped_array<T>,
std::map<int,T>, std::list<T>, boost::intrusive::slist<T> and so on.
Basically ... just describe exact nature and usage of container and no
doubt that there is very close template available and free to use.
Vector may become even raw static array, but i see no reasons to use
raw dynamic array in C++.
Raw static arrays i use for static tables. Sometimes initial
requirements described some "list" of information and vector was
chosen and later it did appear that it is actually static table of
information. There is so elegant (IMHO) syntax for initializing static
arrays in C so it is lot better to maintain. Like i understand C++0x
will allow using such syntax for initializing other containers as
well.
That depends on:
a) which cast is used and
b) what the alignment requirement for the involved types are.
According to [5.2.10/7] you can reinterpret_cast round trip from T1* to T2*
and back provided the alignment of T2 are not stricter than the ones for T1.
To me, the C-style cast within the expression
(int (*)[5]) new int[5]
looks like a reinterpret_cast. Thus, [5.2.10/7] applies. As to whether
int[5] may have stricter alignment requirements than int, I think the
standard remains silent.
Best,
Kai-Uwe Bux
template<typename T, int S, typename U= T (&)[S]>
class R{
public:
R():pp((int (*)[S])new int[S+1]), r(*(pp+1)){r[-1]=S;}
U getArray(){return r;}
void delete_it(){ delete[] (int*)pp; }
private:
int (*pp)[S];
T (&r)[S];
};
template<typename T>
void foo(T r){
std::cout<<"size of array:\t" << r[-1];
std::cout<< "\ntype of array:\t" << typeid(T).name();
}
int main() {
R<int, 7> r;
int (&r1)[7] = r.getArray();
foo(r.getArray());
std::cout<<"\ntype of r1:\t"<< typeid(r1).name();
r.delete_it();
}
>I start to wonder do you ever use raw dynamic arrays and if then why
>do you use them? I don't. In C code I do but there are allocated with
>malloc() and deallocated with free(). Usage of "delete[]" itself
>usually indicates that some novice in team has copy-pasted something
>cheesy from google or from a book into our code base.
OFC dynamic arrays are very usefull , especially if creating a specialised
container.
>In C++ it is often clear how a particular container of T's should be
>used (as stack or as set or as queue or as vector). Sometimes it is
>unclear. I usually take std::vector<T> first when it is unsure how
>container will be used. Later it settles and usually it remains as
>such for ages after.
std::vector is not always the best solution for use within specialised class
designs. I don't see std::vector as a replacement for dynamic arrays, its a
good quick and easy solution if you need a basic resizeable vector with safe
some built in safety.
>Sometimes very rarely it later becomes apparent that a container
>causes performance or maintainability issues related to nature or
>usage of it. Then i revisit what exact underlying container type is
>used. Refactoring vector into some other container may make sense at
>that spot.
I think in C++ we have the power to create a wide variety of specialised
types, data records etc. I don't like to think that there is a certain
limited amount of types, defined by the std lib , that we should use.
>The chosen type depends on what is done with container and what is not
>done with it. There are so lot of somewhat similar to vector
>containers to choose. boost::array<T>, boost::scoped_array<T>,
>std::map<int,T>, std::list<T>, boost::intrusive::slist<T> and so on.
>Basically ... just describe exact nature and usage of container and no
>doubt that there is very close template available and free to use.
>Vector may become even raw static array, but i see no reasons to use
>raw dynamic array in C++.
I prefer to design my own type for a spefic need, if there is a something
suitable in the std lib then great I will use it. but often they are just
not perfectly suited to the job.
>Raw static arrays i use for static tables. Sometimes initial
>requirements described some "list" of information and vector was
>chosen and later it did appear that it is actually static table of
>information. There is so elegant (IMHO) syntax for initializing static
>arrays in C so it is lot better to maintain. Like i understand C++0x
>will allow using such syntax for initializing other containers as
>well.
You are probably basing your views on personal experience as many of us do.
One of the major attractions to C++ , for me , is the ability to easily
create specialised types whereas assembly its a bit more complex, I like to
use this feature of the language often , and I get much satisfaction from
doing so.
As you may have guessed I am no big fan of the std lib, I do like some of
the template features but generally I don't bother with it unless it suits
the job perfectly.
AFAIK boost multi-array, and I don't think this is part of the std lib, is
the closest thing to my needs and its a bit overkill tbh.
Also learning about different ways of allocating memory is usefull for
learning purposes and enhances your programming skills, sometimes I prefer
to invoke the OS's API directly for memory allocation simply to become more
familiar with the API.
HTH
Paul.
As your question has been answered elsewhere, I'll just add an
interesting aside from a language design perspective. If C++ had a
more regular design the type of the "new int [5]" expression would not
degenerate to "int*". A regular design would render the array
overloads of new and delete superfluous, and make your cast here
unnecessary. Consider the irregularity of:
typedef struct {int a, b, c, d, e;} Struct; // aggregate type
typedef int Array [5]; // aggregate type
Struct* ps = new Struct; // Ok!
Array* pa = new Array; // Error! Should be "int* pa".
delete ps; // Ok!
delete pa; // Error! Should be "delete [] pa".
If instead C++ was regular and this was correct code, the type of the
pointer would carry the size information and allow the regular delete
operator to release the correct amount of memory. These irregularities
are avoided by std::array:
typedef std::array <int, 5> Array; // aggregate type
Array* pa = new Array; // Ok!
delete pa; // Ok!
Dereferencing the pointer, (*pa), gives you a reference (lvalue) to
the array as a whole, and an indexing operator is implemented for the
array class itself (not via degeneration into pointer arithmetic).
The new std::array circumvents the numerous irregularities between
pointers and arrays that causes so much confusion. I.e. you can copy
arrays, pass and return arrays by value to and from functions, and the
array will never implicitly convert to a pointer to the first element.
Hence I recommend its use instead of C-style arrays.
Regards,
Vidar Hasfjord
d) when you allocate memory using new T[], you use delete [], passing
it a T* returned to you by a prior cal to "new T[]". This is how new/
delete are supposed to work. You broke that with your casts, which is
the original sin of this code.
I would guess that all of a, b, and c will work on many
implementations, but I know of no guarantee for that. One can imagine
an implementation with bounds-checking where such code fails badly.
Your "R" example further down makes little sense, perhaps you should
have given it a bit more of thought. (E.g. what's with r[-1] = S, that
requires that T has operator=(int) or something, and S is a compile-
time constant anyhow!?)
Did you mean to create a variant of the old C trick with size,
followed by an array of struct, where size is a run-time value? There
was a +/- heated discussion some time ago about this here, somebody
made one half-decent C++ implementation then. (No conversion to an
array though, that wasn't the discussion's goal).
Goran.
For the size to be part of the pointer _type_ this size must be a
compile-time constant. But new[] does not require a compie-time
constant for the number of elements:
void foo(int x) {
new int[x]; // x is not a compile-time constant
}
So, the current new[] operator can do something that you can't emulate
with "new array<T,N>" properly.
Perhaps an alternate syntax would have been better and more regular:
void foo(int x)
{
int *p = new[x] int; // not real C++ code (unfortunately)
delete[] p;
int (*q)[5] = new int[5]; // not real C++ code (unfortunately)
delete q;
}
Anyhow, I consider new[]/delete[] and raw arrays to be nearly useless.
We have better alternatives and soon get some more: vector<T>,
array<T,N>, unique_ptr<array<T,N>>, for example. So, "fixing" arrays
and new[] w.r.t. regularity wouldn't buy you much. You'd mainly lose
compatibility with old code.
my 2 cents,
SG
Good point. But, if C++ was regular it would require the size to be a
compile-time constant; as it does everywhere else in the language
where an array type is expressed. I.e. the argument to new would
simply be a regular type expression.
The unrelated use of square brackets to specify a run-time argument is
highly irregular. And, more over, with templates in the language it is
unnecessary. Dynamic allocation can be implemented as a library
function. All you need is a low-level memory allocation function
(malloc) and placement-new (or a similar construct to allow you to
construct an object in allocated memory). E.g.
template <class T> T* construct (size_t n = 1); // replaces new
template <class T> void destruct (T*); // replaces delete
> Anyhow, I consider new[]/delete[] and raw arrays to be nearly useless.
> We have better alternatives and soon get some more: vector<T>,
> array<T,N>, unique_ptr<array<T,N>>, for example.
Agree. C++ programmers, except those dealing with legacy code or
implementing the standard library, can now avoid these irregularities.
Regards,
Vidar Hasfjord
This seems contradictory, the irregularity of array with runtime size
is AFAIS intended for providing a regular type expression syntax to
use with new operator.
> And, more over, with templates in the language it is
> unnecessary. Dynamic allocation can be implemented as a library
> function. All you need is a low-level memory allocation function
> (malloc) and placement-new (or a similar construct to allow you to
> construct an object in allocated memory). E.g.
>
> template <class T> T* construct (size_t n = 1); // replaces new
> template <class T> void destruct (T*); // replaces delete
And how do you handle constructor parameters for non-array
initialisation ?
For a workable solution, you would need perfect forwarding which isn't
yet standard.
Moreover, this means that unless you standardise it, every project
would have it's own syntax for allocation class-objects; if you
standardize it, why not make it an operator with a nice syntax.
> > Anyhow, I consider new[]/delete[] and raw arrays to be nearly useless.
> > We have better alternatives and soon get some more: vector<T>,
> > array<T,N>, unique_ptr<array<T,N>>, for example.
>
> Agree. C++ programmers, except those dealing with legacy code or
> implementing the standard library, can now avoid these irregularities.
Then, how do you implement a dynamic length array of non-copiable
objects ?
--
Michael
Neither copyable nor movable? One may consider either
boost::scoped_array<T> or std::vector<unique_ptr<T>> depending on
other constraints.
start using std::vector (or some other container) instead and forget about
manual memory management. leave it to compiler. you'll have much less memory
leaks.
since i started using stl i forgot about how stupid all that manual handling
of pointers and arrays used to be.
look how simpler it is:
// init
std::vector<int*> p(5);
// automatically deleted when it loses scope
Both statements are correct. What you actually need to correct is,
"...so you call delete [] on the pointer that new returned," to, "...so
you call delete [] on the same pointer type that new returned." Key
point here is that casting creates new objects.
>As your question has been answered elsewhere, I'll just add an
>interesting aside from a language design perspective. If C++ had a
>more regular design the type of the "new int [5]" expression would not
>degenerate to "int*". A regular design would render the array
>overloads of new and delete superfluous, and make your cast here
>unnecessary. Consider the irregularity of:
I don't think my question has bene answered, what I was asking was the
difference between each delete invocation, it seems that each is identical
but I get warning for delete *p, see code:
class Base{
public:
Base() {std::cout<<"Constructing base\n";}
~Base(){std::cout<<"Destructing base\n";}
};
int main() {
Base (*pA)[4] = (Base (*)[4])new Base[4];
Base* pB = new Base[3];
std::cout<<"\nDeleting pA:\n";
delete[] pA;
//delete[] *pA;
std::cout<<"\nDeleting pB:\n";
delete[] pB;
If you run the above code you see that each invocation of delete[] produces
the desired effect, even if you use *pA instead of pA. This however gives a
warning but I think the array is converted to a pointer. I did my tests on a
windows platform but it may not be the same on other platforms.
This suggests to me that the pointer-type is not rellevant, on my platform
at least, when calling delete[].
> typedef struct {int a, b, c, d, e;} Struct; // aggregate type
> typedef int Array [5]; // aggregate type
> Struct* ps = new Struct; // Ok!
> Array* pa = new Array; // Error! Should be "int* pa".
> delete ps; // Ok!
> delete pa; // Error! Should be "delete [] pa".
>If instead C++ was regular and this was correct code, the type of the
>pointer would carry the size information and allow the regular delete
>operator to release the correct amount of memory. These irregularities
>are avoided by std::array:
Yeah but it's never going to be a regular language, that is not the nature
of comp programming. All you can do is wrap it up in some class and make it
apper like regular syntax. I'm no big fan of unnecessary wrapping.
> typedef std::array <int, 5> Array; // aggregate type
> Array* pa = new Array; // Ok!
> delete pa; // Ok!
Yes but look at all the extra code you need to introduce behind the scenes
to get these semantics. I don't see any advantage in using an STL class over
a pointer in many cases, there are some cases where they are perfect and
really good , but not always.
>Dereferencing the pointer, (*pa), gives you a reference (lvalue) to
>the array as a whole, and an indexing operator is implemented for the
>array class itself (not via degeneration into pointer arithmetic).
Thats simple to achieve without the introduction of a STL class, for
example:
int (*pA)[6] = (int (*)[6])new int[6];
int (&rA)[6] = *pA;
The only advantage I see for using STL vectors is that they are expandable.
If you need a quick fix generic expandable array I agree they are the
solution. But for a simple fixed size array I wouldn't bother with an STL
class.
>The new std::array circumvents the numerous irregularities between
>pointers and arrays that causes so much confusion. I.e. you can copy
>arrays, pass and return arrays by value to and from functions, and the
>array will never implicitly convert to a pointer to the first element.
>Hence I recommend its use instead of C-style arrays.
I've never really used it TBH, my compiler setup doesn't seem to have that
library.
I don't really see the need for it in most cases, I'm quite happy with
normal C++ arrays, I'm just a bit rusty on C++ so I was trying to confirm
the behaviour of delete[] with the given cases.
TY for your post
Paul.
While it may not make a difference in this particular example, in
general it is very much relevant. The correct type is needed for
delete to call the correct destructor. Also note that both "delete"
and "delete[]" can be overloaded globally and per class, in which case
the correct type is needed to select the correct overload.
Regards,
Vidar Hasfjord
[...]
The above seems to be rather roundabout. Also, the templates indicate a
generality that is not really there: if you try to instantiate R<long,7> the
code will not compile. Furthermore, I do not understand why you would want
to store size information within the array when it is available through the
type system: after all, the array size is passed as a template parameter
whence it must be a compile time constant. Finally, the casting could invoke
undefined behavior as soon as you access array elements (any use other than
casting back is undefined, I think).
For comparison look at the free functions new_it<> and delete_it<>. To me,
it seems, that they do the same as the above class R<> except that the size
is not required to be a compile time constant. (Also, now the templates work
for types T!=int.)
#include <iostream>
#include <ostream>
#include <typeinfo>
template<typename T, int S, typename U= T (&)[S]>
class R{
public:
R():pp((int (*)[S])new int[S+1]), r(*(pp+1)){r[-1]=S;}
U getArray(){return r;}
void delete_it(){ delete[] (int*)pp; }
private:
int (*pp)[S];
T (&r)[S];
};
template < typename IntegerType >
IntegerType * new_it ( int size ) {
IntegerType * ptr = new IntegerType [size+1];
ptr[0] = size;
return ( ptr+1 );
}
template < typename IntegerType >
void delete_it ( IntegerType * ptr ) {
delete ( ptr-1 );
}
template<typename T>
void foo(T r){
std::cout<<"size of array:\t" << r[-1];
std::cout<< "\ntype of array:\t" << typeid(T).name();
}
int main() {
{
R<int, 7> r; // try R<long,7> here.
int (&r1)[7] = r.getArray();
foo(r.getArray());
std::cout<<"\ntype of r1:\t"<< typeid(r1).name();
r.delete_it();
}
{
std::cout << "\n";
int * ptr = new_it<int>(7); // try long * ptr = new_it<long>(7) here;
foo(ptr);
delete_it( ptr );
std::cout << "\n";
}
}
Best
Kai-Uwe Bux
I'm not talking about pointer-types , in general. I'm talking about pointer
to arrays and pointer to array-object types as in:
int* or int(*)[size];
Base* or Base (*)[size]
etc etc.
If I call delete[] on int*, it's no different to int (*)[size]? Or is it?
How does the compiler know how many destructors to call?
And what if call delete[] *(int (*)[size])? Is it implicitly converted to
int*?
>
>"Vidar Hasfjord" <vattila...@yahoo.co.uk> wrote in message
>news:80d512c9-799d-445d-bd99->58c30a...@l11g2000yqb.googlegroups.com...
>>On Mar 27, 5:34 am, "Paul" <pchris...@yahoo.co.uk> wrote:
>>> [Suppose I have the following code:
>>>
>>> int (*p)[5] = (int (*)[5])new int[5];
>>>
>>> How do I delete the allocated memory?
>
>>As your question has been answered elsewhere, I'll just add an
>>interesting aside from a language design perspective. If C++ had a
>>more regular design the type of the "new int [5]" expression would not
>>degenerate to "int*". A regular design would render the array
>>overloads of new and delete superfluous, and make your cast here
>>unnecessary. Consider the irregularity of:
>
>I don't think my question has bene answered, what I was asking was the
>difference between each delete invocation, it seems that each is identical
It is an issue of type as Vidar points and myself have pointed out. The only
thing that your three choices from the OP have in common is that the address
stored by the pointer is the same. Not to re-open a whole can of worms, but had
you walked away from the previous seven threads that you started in agreement
with those that replied, you would not be confused on this point.
I don't think that's what he means when he says regular language, but anyway, I
think it is more of an issue of backwards compatibility with C than simply the
nature of programming in general.
>
>> typedef std::array <int, 5> Array; // aggregate type
>> Array* pa = new Array; // Ok!
>> delete pa; // Ok!
>
>Yes but look at all the extra code you need to introduce behind the scenes
>to get these semantics. I don't see any advantage in using an STL class over
>a pointer in many cases, there are some cases where they are perfect and
>really good , but not always.
I see several advantages, even for many simple cases. See below.
>
>>Dereferencing the pointer, (*pa), gives you a reference (lvalue) to
>>the array as a whole, and an indexing operator is implemented for the
>>array class itself (not via degeneration into pointer arithmetic).
>
>Thats simple to achieve without the introduction of a STL class, for
>example:
>
>int (*pA)[6] = (int (*)[6])new int[6];
>int (&rA)[6] = *pA;
>
>The only advantage I see for using STL vectors is that they are expandable.
>If you need a quick fix generic expandable array I agree they are the
>solution. But for a simple fixed size array I wouldn't bother with an STL
>class.
std::vector gives you size management, memory management, and a set of
guarantees with regards to behavior. Because it is an STL container, you can
define custom allocators as well as use the class iterators to access the
container or perform standard algorithms on the container data. Keep in mind
that std::vector is the "quintessential generic" general purpose container.
Other containers give you a variety of other standardized behaviors and
functionality. Even for a fixed array, std::vector's type safety and
interoperability with the other C++ built in class types make it a worthy
consideration.
For me personally, only for the case of lookup tables of constants or tables of
data that does not change do I generally prefer raw arrays over STL containers.
>>The new std::array circumvents the numerous irregularities between
>>pointers and arrays that causes so much confusion. I.e. you can copy
>>arrays, pass and return arrays by value to and from functions, and the
>>array will never implicitly convert to a pointer to the first element.
>>Hence I recommend its use instead of C-style arrays.
>
>I've never really used it TBH, my compiler setup doesn't seem to have that
>library.
>I don't really see the need for it in most cases, I'm quite happy with
>normal C++ arrays, I'm just a bit rusty on C++ so I was trying to confirm
>the behaviour of delete[] with the given cases.
>TY for your post
>Paul.
Just as a side note and a bit off-topic (but not entirely out of context)
consider the benefits of a smart pointer class over using raw pointers in
general. As cases become more complex, such as with COM, for example, you stand
to benefit greatly from not having to manage things such as reference counts,
pointer validitiy, and internal allocation.
>
>"Vidar Hasfjord" <vattila...@yahoo.co.uk> wrote in message
>news:35e223bd-251f-4697-aa66->6809d0...@w7g2000yqe.googlegroups.com...
>>On Mar 28, 6:37 pm, "Paul" <pchris...@yahoo.co.uk> wrote:
>>> This suggests to me that the pointer-type is not rellevant, on my
>>> platform
>>> at least, when calling delete[].
>
>>While it may not make a difference in this particular example, in
>>general it is very much relevant. The correct type is needed for
>>delete to call the correct destructor. Also note that both "delete"
>>and "delete[]" can be overloaded globally and per class, in which case
>>the correct type is needed to select the correct overload.
>
>I'm not talking about pointer-types , in general. I'm talking about pointer
>to arrays and pointer to array-object types as in:
>int* or int(*)[size];
>Base* or Base (*)[size]
>etc etc.
Then yes, you are in fact talking about pointer types. I knew that this thread
inevitably was going to come to this. It was just a matter of time as the
preceding seven Paul discussion threads have shown.
agreed.
Granted, you can say a similarity of syntax is regularity, but the
semantic irregularity it causes is high, and it was the semantic
irregularities I alluded to here. I view it as undesirable that the
type-id syntax has been reused to specify a run-time parameter.
> > template <class T> T* construct (size_t n = 1); // replaces new
> > template <class T> void destruct (T*); // replaces delete
>
> And how do you handle constructor parameters for non-array
> initialisation ?
> For a workable solution, you would need perfect forwarding which isn't
> yet standard.
Sorry, these were meant to replace just "new[]" and "delete[]",
leaving "new" and "delete" as before, but with regular semantics.
Perfect forwarding would make it feasable to also replace "new" and
"delete", although the language would still need placement-new, or
something similar, to construct objects in allocated memory.
> Moreover, this means that unless you standardise it, every project
> would have it's own syntax for allocation class-objects; if you
> standardize it, why not make it an operator with a nice syntax.
I'm just hypothesising about a regular subset of C++ here, but yes,
you may assume it would have the necessary features in the language
and support in the library. But I don't see how an operator can be
much nicer than template syntax. This is the road we're on, anyway (C+
+0x):
auto pa = make_shared <A> (...); // Create A, return shared_ptr.
Regards,
Vidar Hasfjord
You may have to consult the C++ standard to see if it gives any
guarantees. Despite your casts, "int*" is what "new" returned, so that
seems the only sure thing. Maybe allowing "int (*)[size]" is a
concession from the compiler for having decayed the type in the first
place? :-)
> How does the compiler know how many destructors to call?
See:
http://www.parashift.com/c%2B%2B-faq-lite/freestore-mgmt.html#faq-16.14
> delete[] *(int (*)[size])? Is it implicitly converted to int*?
Yes, and Visual C++ 2010 also produces a warning in this case (warning
C4154: deletion of an array expression; conversion to pointer
supplied).
Regards,
Vidar Hasfjord
Yes I already knew about these techniques.
What my concern was that with delete [] called on int (*)[size] type
pointer...
C++ would call the destructor for the 1st element, then move
size*sizeof(int), that is sizeof( *(int (*)[size]) ) to look for the second
element. As oppose to moving only sizeof(int) per element, in the case of
delete[] int*
. Therefore I would be freeing more memory than I allocated, but this
doesn't seen to happen
>
>> delete[] *(int (*)[size])? Is it implicitly converted to int*?
>
> Yes, and Visual C++ 2010 also produces a warning in this case (warning
> C4154: deletion of an array expression; conversion to pointer
> supplied).
>
Ah nice that confirms that , for VC++ at least.
Ty again for your post.
You are repeatedly trying to get into UB land. You should __not__ ask
the question you are asking. You never allocated int (*)[size]. Can't
do that, perhaps because of array type erasure: as soon as the array
type hits operator new, array type is erased and new sees a pointer (a
double one, "**", but nonetheless). You never got an __array__ out of
new, and you can't pass an array to delete.
If you have TYPE* that you need to delete, you must know how you
allocated it, end of. If you did p = new TYPE(), you do delete p; if
you did p = new TYPE[1], you do delete [] p; (yes, even if you put 1
in []). If you did p = new TYPE(*)[1], you do delete [] p (but, p's
type is TYPE**).
> How does the compiler know how many destructors to call?
Not ours to know (hint: implementation's heap management internals
serve for that).
> And what if call delete[] *(int (*)[size])? Is it implicitly converted to
> int*?
I'd wager yes, due to array-type-erasure. But you did not __allocate__
that, now did you? (That is, you have some other problem in your code,
so this question should not be asked in the first place).
Goran.
>If you have TYPE* that you need to delete, you must know how you
>allocated it, end of. If you did p = new TYPE(), you do delete p; if
>you did p = new TYPE[1], you do delete [] p; (yes, even if you put 1
>in []). If you did p = new TYPE(*)[1], you do delete [] p (but, p's
>type is TYPE**).
The type is the same , both int type arrays, only the level of dereference
is different for example:
int (*p1)[5] = (int (*)[5])new int*[5]; /*2d array*/
int (*p2)[5] = (int (*)[5])new int[5]; /*1d array*/
int (*p3)[5] = new int[5][5]; /*2d array*/
delete[] p1;
delete[] p2;
delete[] p3;
What is the difference with the above deletes?
>
>> How does the compiler know how many destructors to call?
>
>Not ours to know (hint: implementation's heap management internals
>serve for that).
>> And what if call delete[] *(int (*)[size])? Is it implicitly converted to
>> int*?
>I'd wager yes, due to array-type-erasure. But you did not __allocate__
>that, now did you? (That is, you have some other problem in your code,
>so this question should not be asked in the first place).
I don't know this term "array type erasure". What do you mean by that?
An inane question since the case itself is inane. Not to mention this has been
asked and answered more than once.
What you should be concerned with asking yourself is why you are casting the
pointer types returned by new into a different types without at least first
storing the uncasted returned type .
Each of the above new expressions returns a different type of pointer. As others
have said more than once, it is a bad idea to cast the type of new's return and
then call delete [] on it because it opens the door to UB for every case where
new's return type does not match the type of pointer you pass to delete [].
In case it has not been said, allocating multidimensional arrays with new
creates complexity with the deallocation. Specifically referring to the third
case for your example, delete [] p3 fails to deallocate the memory allocated.
You must loop delete []. Yet another reason to prefer STL vector for your
dynamic array needs.
As far as I am concerned, difference is: first two are UB (platform-
specific at best), because of wrong casts in corresponding "new",
whereas the third one is correct.
> I don't know this term "array type erasure". What do you mean by that?
FAQ uses term "decay". I meant same thing (that was the term used to
explain the thing to me).
Goran.
Why are they UB? After having a read through the rellevant pages of the
standard I don't think that it is UB. The standard specifically states that
the pointer returned from new must be castable.
Consider this more complex example:
class Base{
public:
Base() {std::cout<<"Constructing base\n";}
virtual ~Base(){std::cout<<"Destructing base\n";}
};
class Derived: public Base{
public:
Derived() {std::cout<<"Constructing derived\n";}
~Derived(){std::cout<<"Destructing derived\n";}
};
int main() {
Base (*p2)[3] = (Base (*)[3])new Derived[2][3];
delete[] p2;
}
The cast is required , otherwise you'd need to loop through the array
columns for allocation and deallocation..
I could cast it back prior to deletion with:
delete[] (Derived (*)[3])p2;
But this is unneccessary because Base type is an acceptable pointer type.
The type is only neccessay for the correct destruction of objects. For
example:
Base *p2 = new Derived[3];
delete[] p2;
is identical to
Base (*p2)[3] = (Base (*)[3])new Derived[3];
deletep[] p2;
The type that is relevant here is that it is Base or Derived, the number of
elements destructed is decided by the C runtime. As long as the dereferenced
pointer passed to delete has the same value as the pointer returned by new
all is ok, because it is converted to void* anyway. The C runtime decides
whether it deletes 3 objects or 1 , not the type system.
If int[3] and int* both point to the same address, then converted to void*
they are identical.
The C++ type system is only interested in if its an array of int-typ,
Derived-type , or Base-type, It not up to the type system to deicde how many
destructors to call.
It would be UB if you converted the pointer to a different type altogether
for example:
double* p = (double*)new int[6];
delete[] p;
This would be UB , because you are converting to a new type of array..
>
>> I don't know this term "array type erasure". What do you mean by that?
>
>FAQ uses term "decay". I meant same thing (that was the term used to
>explain the thing to me).
Ok I see you are talking about implicit pointer conversion.
HTH
Paul.
>
> "Goran" <goran...@gmail.com> wrote in message
> news:bed4b5d7-1577-4a11...@k9g2000yqi.googlegroups.com...
>>On Mar 29, 1:07 pm, "Paul" <pchris...@yahoo.co.uk> wrote:
>>> int (*p1)[5] = (int (*)[5])new int*[5]; /*2d array*/
>>> int (*p2)[5] = (int (*)[5])new int[5]; /*1d array*/
>>> int (*p3)[5] = new int[5][5]; /*2d array*/
>>> delete[] p1;
>>> delete[] p2;
>>> delete[] p3;
>>>
>>> What is the difference with the above deletes?
>>
>>As far as I am concerned, difference is: first two are UB (platform-
>>specific at best), because of wrong casts in corresponding "new",
>>whereas the third one is correct.
>
> Why are they UB? After having a read through the rellevant pages of the
> standard I don't think that it is UB.
We shall see.
> The standard specifically states that the pointer returned from new must
> be castable.
Correct: [5.2.10/7] states:
A pointer to an object can be explicitly converted to a pointer to an
object of different type.65) Except that converting an rvalue of type
"pointer to T1" to the type "pointer to T2" (where T1 and T2 are object
types and where the alignment requirements of T2 are no stricter than
those of T1) and back to its original type yields the original pointer
value, the result of such a pointer conversion is unspecified.
That implies that after casting, you have an unspecified result. The only
thing you can do with it, is casting back, and even that presupposes
compatible alignment requirement.
In particular, passing the unspecified value obtained by casting to delete
or delete [] is undefined behavior.
> Consider this more complex example:
> class Base{
> public:
> Base() {std::cout<<"Constructing base\n";}
> virtual ~Base(){std::cout<<"Destructing base\n";}
> };
>
> class Derived: public Base{
> public:
> Derived() {std::cout<<"Constructing derived\n";}
> ~Derived(){std::cout<<"Destructing derived\n";}
> };
>
> int main() {
> Base (*p2)[3] = (Base (*)[3])new Derived[2][3];
> delete[] p2;
> }
>
> The cast is required , otherwise you'd need to loop through the array
> columns for allocation and deallocation..
> I could cast it back prior to deletion with:
> delete[] (Derived (*)[3])p2;
>
> But this is unneccessary because Base type is an acceptable pointer type.
What do you mean by "acceptable"?
Also, the value of the variable p2 is _unspecified_ as it is obtained
through casting.
> The type is only neccessay for the correct destruction of objects. For
> example:
> Base *p2 = new Derived[3];
> delete[] p2;
>
> is identical to
> Base (*p2)[3] = (Base (*)[3])new Derived[3];
> deletep[] p2;
>
> The type that is relevant here is that it is Base or Derived, the number
> of elements destructed is decided by the C runtime. As long as the
> dereferenced pointer passed to delete has the same value as the pointer
> returned by new all is ok, because it is converted to void* anyway. The C
> runtime decides whether it deletes 3 objects or 1 , not the type system.
Sameness of value is not guaranteed by the standard (pretending we know what
it means). Consider:
int main () {
Derived (*p1) [3] = new Derived [2][3];
Base (*p2) [3] = (Base (*)[3]) p1;
assert( p1 == p2 );
}
This does not even compile as p1 and p2 are of different type.
> If int[3] and int* both point to the same address, then converted to void*
> they are identical.
So this is what we mean by "sameness of values". Ok, let's consider
int main () {
Derived (*p1) [3] = new Derived [2][3];
Base (*p2) [3] = (Base (*)[3]) p1;
assert( (void*)p1 == (void*)p2 );
}
Now, the code compiles. However, contrary to expectations, the assert is
allowed to _fail_ because the value of p2 is unspecified. In fact, using the
unspecified value as an argument for a cast is UB. So, we could do:
int main () {
Derived (*p1) [3] = new Derived [2][3];
Base (*p2) [3] = (Base (*)[3]) p1;
assert( p2 == (Base (*) [3]) p1 );
}
This looks as though it could not fail. However, the two sides of the == are
both unspecified values. It is neither clear that they are equal nor that
comparing them does not result in a core dump.
If you see more guarantees in the standard that would render any of the
above defined behavior, please cite them.
[...]
Best,
Kai-Uwe Bux
> In particular, passing the unspecified value obtained by casting to delete
> or delete [] is undefined behavior.
What is unspecified , the cast does not make the pointer *unspecified*
>
>
>> Consider this more complex example:
>> class Base{
>> public:
>> Base() {std::cout<<"Constructing base\n";}
>> virtual ~Base(){std::cout<<"Destructing base\n";}
>> };
>>
>> class Derived: public Base{
>> public:
>> Derived() {std::cout<<"Constructing derived\n";}
>> ~Derived(){std::cout<<"Destructing derived\n";}
>> };
>>
>> int main() {
>> Base (*p2)[3] = (Base (*)[3])new Derived[2][3];
>> delete[] p2;
>> }
>>
>> The cast is required , otherwise you'd need to loop through the array
>> columns for allocation and deallocation..
>> I could cast it back prior to deletion with:
>> delete[] (Derived (*)[3])p2;
>>
>> But this is unneccessary because Base type is an acceptable pointer type.
>
> What do you mean by "acceptable"?
Acceptable pointer type, that is a base(with virtual destructor) or derived
class. There are probably many other acceptable pointer conversions which
are Implementation specific e.g: short to int where they are both the same
size.
>
> Also, the value of the variable p2 is _unspecified_ as it is obtained
> through casting.
Casting doesn't make it *unspecified*.
>
>> The type is only neccessay for the correct destruction of objects. For
>> example:
>> Base *p2 = new Derived[3];
>> delete[] p2;
>>
>> is identical to
>> Base (*p2)[3] = (Base (*)[3])new Derived[3];
>> deletep[] p2;
>>
>> The type that is relevant here is that it is Base or Derived, the number
>> of elements destructed is decided by the C runtime. As long as the
>> dereferenced pointer passed to delete has the same value as the pointer
>> returned by new all is ok, because it is converted to void* anyway. The C
>> runtime decides whether it deletes 3 objects or 1 , not the type system.
>
> Sameness of value is not guaranteed by the standard (pretending we know
> what
> it means). Consider:
>
> int main () {
> Derived (*p1) [3] = new Derived [2][3];
> Base (*p2) [3] = (Base (*)[3]) p1;
> assert( p1 == p2 );
> }
>
> This does not even compile as p1 and p2 are of different type.
That doesn't mean the values are not the same though does it.
>
>> If int[3] and int* both point to the same address, then converted to
>> void*
>> they are identical.
>
> So this is what we mean by "sameness of values". Ok, let's consider
>
> int main () {
> Derived (*p1) [3] = new Derived [2][3];
> Base (*p2) [3] = (Base (*)[3]) p1;
> assert( (void*)p1 == (void*)p2 );
> }
>
> Now, the code compiles. However, contrary to expectations, the assert is
> allowed to _fail_ because the value of p2 is unspecified. In fact, using
> the
> unspecified value as an argument for a cast is UB. So, we could do:
I don't know what your problem is with understand the meaning of two of the
same values.
its simple:
float f = 65;
int x = 65;
long l = 65;
same values.
>
> int main () {
> Derived (*p1) [3] = new Derived [2][3];
> Base (*p2) [3] = (Base (*)[3]) p1;
> assert( p2 == (Base (*) [3]) p1 );
> }
>
> This looks as though it could not fail. However, the two sides of the ==
> are
> both unspecified values. It is neither clear that they are equal nor that
> comparing them does not result in a core dump.
I don't have a clue what you are talking about, what is an unspecified
value? What fails?
A pointers' value is the address it points to , if two pointers point to the
same address they have they same value, simple as that.
>
> If you see more guarantees in the standard that would render any of the
> above defined behavior, please cite them.
>
What guarantees you talknig about here?
Return type of "new int(*)[5]" is int**. You cast that into int(*)[5],
using C cast. That, as far as I can see, ends up in same effect as
reinterpret_cast, and reinterpret_cast is UB (or platform specific at
best).
> Consider this more complex example:
> class Base{
> public:
> Base() {std::cout<<"Constructing base\n";}
> virtual ~Base(){std::cout<<"Destructing base\n";}
>
> };
>
> class Derived: public Base{
> public:
> Derived() {std::cout<<"Constructing derived\n";}
> ~Derived(){std::cout<<"Destructing derived\n";}
>
> };
>
> int main() {
> Base (*p2)[3] = (Base (*)[3])new Derived[2][3];
> delete[] p2;
>
> }
>
> The cast is required , otherwise you'd need to loop through the array
> columns for allocation and deallocation..
First, the cast is not required, because
Derived (*p2)[3] = new Derived[2][3];
delete [] p2;
works just as well.
Second, the problem with your exact example is that in practice just
does not work. And I mean, at all!
Try this:
class Base{
public:
Base() : i(0) {}
int i;
};
class Derived: public Base{
public:
Derived() : j(1) {}
int j;
};
Base (*p2)[3] = (Base (*)[3])new Derived[2][3];
const Base& b = p2[0][1];
assert(0 == b.i); // Fires on common implementations
assert(1 == b.i); // Does not fire on common implementations. Eh!?
Of course, problem is that pointer returned by "new Derived[2][3]" and
"Base (*)[3]" have unrelated types, and my code snippet shows why that
is required.
Goran.
> >FAQ uses term "decay". I meant same thing (that was the term used to
> >explain the thing to me).
>
> Ok I see you are talking about implicit pointer conversion.
Nice try ;-);
Goran.
[...]
Ok, so you are saying that the C-style cast you are using is _not_ governed
by clause [5.2.10/7]. Please specify which clause of the standard you are
relying on in determining the behavior of the cast expression.
Best,
Kai-Uwe Bux
But you did cast to a different type in both of the above cases that you casted:
1) from int** to int(*)[5]
2) from int* to int(*)[5]
or in language that even you should understand since it appears that you are
confused by the Standard's use of what pointer to and object of different type
means:
1) from "pointer to int pointer" to "pointer to array of int"
2) from "pointer to int" to "pointer to array of int"
Once again, this takes us back to all of the previous message threads that you
have started which demonstrate some form of your confusion with the fact that a
pointer to int and pointer to array of int are not the same thing.
Now whether or not there are stricter alignment requirements for T2 than T1 is
another story. I do not think there is under Windows for the above types, but
the idea is that UB is potentially there where C++ is concerned. Something that
could easily be remedied by not casting the return in the first place.
#include <iostream>
class BaseA{
public:
int k;
BaseA() {std::cout<<"Constructing base a\n";}
virtual ~BaseA(){std::cout<<"Destructing base a\n";}
};
class BaseB {
public:
int i;
BaseB() {std::cout<<"Constructing base b\n";}
virtual ~BaseB(){std::cout<<"Destructing base b\n";}
};
class Derived: public BaseA, BaseB {
public:
int j;
Derived() {std::cout<<"Constructing derived\n";}
~Derived(){std::cout<<"Destructing derived\n";}
};
int main() {
Derived (*p1) [3] = new Derived [2][3];
BaseB (*p2)[3] = (BaseB (*)[3])p1;
delete [] p2;
}
This program segfaults on my machine.
I think, it is allowed to do so.
Best,
Kai-Uwe Bux
So, as far as you can see, any use of a c-style cast will immediately cause
UB?
<snip>
>Of course, problem is that pointer returned by "new Derived[2][3]" and
>"Base (*)[3]" have unrelated types, and my code snippet shows why that
>is required.
Your code shows me nothing except that you are a noob who uses assert, If
you want to expalin something to me in code please don't use noob statements
like assert.
> >FAQ uses term "decay". I meant same thing (that was the term used to
> >explain the thing to me).
>
> Ok I see you are talking about implicit pointer conversion.
Nice try ;-);
What does this mean? What am I trying to do in your mind?
Sorry for snipping etc but dont have time to manually indent all that crap.
Not will. Could. Your cast up there __will__, however (and a very
simple example has shown that).
> >Of course, problem is that pointer returned by "new Derived[2][3]" and
> >"Base (*)[3]" have unrelated types, and my code snippet shows why that
> >is required.
>
> Your code shows me nothing except that you are a noob who uses assert, If
> you want to expalin something to me in code please don't use noob statements
> like assert.
First, assert is not a statement, it's a function. Second, it's use is
incidental to the problem. Noob or not, assert shows that your cast
does not work.
In slightly more detail: in common implementations, object's memory
layout is such that your cast, on your example, fails horribly. Line
const Base& b = p2[0][1];
does not produce a reference to b (and it should). It does not do it,
because the cast on the line where new is called is __wrong__.
In even more detail: what new allocated (on common implementations) is
6 consecutive instances of Derived. So let's presume a 32-bit platform
and a common implementation. New gives (memory layout):
32 bits: Base.i
32 bits: Derived.j
32 bits: Base.i
32 bits: Derived.j
... 6 of those in total
You tried to cast that to (memory layout on common implementations):
32 bits: Base.i
32 bits: Base.i
... 6 of those in total
Your cast causes "b" to "reference" Derived.j, which makes no sense
whatsoever. "assert" was only there to show you this. I hoped not so
many words were needed, but alas...
Goran.
------------------------------------------
Who the hell do you think you are ?
You cant even communicate properly I cant be arsed with arseholes who cant
communicate properly.
Im not gonna bother indenting and making an effort for someone who obviously
thinks he is abov eme.
I told you your example showed me nothing except that you are some noob who
uses *STATEMENTS* like assert
I was mistaken, I apologize. assert is not a function, it's rather a
macro.
It's not a statement though. Please refer to section 6 of the current
language standard for more detail on what a statement is.
Goran.
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
I dont care what its called in the standards I called it a statement and you
knew what i meant.
What I dont understand is what exactly your argument is, you fail to
communicate properly.
You said something was UB but no expalanation, just because it appeared to
be UB to you.
Now you are blabbing on about some assert thing. WHat exactly has all this
got to do with delete[] ?
Your cast is on the new side invokes UB. By consequence, your examples
are buggy, and so is your call to operator[].
You do not seem to accept that cast invokes UB, so I made you a piece
of code that shows why your cast is, and should be, UB.
For that piece of code, I used assert to ascertain what should be true
(that "0" equals "b.i"). If you compile this snippet without NDEBUG,
"assert(0 == b.i)" should not fire, because, clearly, Base() sets i to
0. And yet, this assert does fire (on common implementations). Why?
Because your cast invoked UB. Consequently, everything else is wrong,
including delete[] you're trying to pull off.
Goran.
Casting from derived to base is called "upcasting" not "downcasting".
/Leigh
Your cast is on the new side invokes UB. By consequence, your examples
are buggy, and so is your call to operator[].
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Why?
Please explain why it is UB?
Any time we cast in C++ we produce UB?
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
You do not seem to accept that cast invokes UB, so I made you a piece
of code that shows why your cast is, and should be, UB.
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Your code doesn't prove the cast is UB. I would accept it if it is UB but as
far as I can see all the standard says is that new must return a castable
pointer, this implies that its allowed to be casted.
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
For that piece of code, I used assert to ascertain what should be true
(that "0" equals "b.i"). If you compile this snippet without NDEBUG,
"assert(0 == b.i)" should not fire, because, clearly, Base() sets i to
0. And yet, this assert does fire (on common implementations). Why?
Because your cast invoked UB.
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Eh? Because you code asserts something to be not equal you assert this to
mean the cast produces UB? How do you come to that conclusion?
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Think about how you draw a family tree: ancestors toward the top;
descendants toward the bottom (the tree is upside down).
Anyway it doesn't matter how it *seems* to you; what is important is to
be technically accurate and to use correct terminology: casting from
derived to base is called "upcasting"; casting from base to derived is
called "downcasting".
/Leigh
A family tree is exact opposite of a class inheritance, a parent object is
the most derived object.
In a family tree the children are most derived.
>
> Anyway it doesn't matter how it *seems* to you; what is important is to be
> technically accurate and to use correct terminology: casting from derived
> to base is called "upcasting"; casting from base to derived is called
> "downcasting".
>
Yes says the proven idiot who thinks p doesn't point to an array with:
int* p = new int[5]
Because you are casting from a pointer of one type to a pointer of an
unrelated type (and that's what the compiler told you, too, when you
tried without a cast, now did it?)
> You do not seem to accept that cast invokes UB, so I made you a piece
> of code that shows why your cast is, and should be, UB.
> xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
> Your code doesn't prove the cast is UB.
It does. See below.
> For that piece of code, I used assert to ascertain what should be true
> (that "0" equals "b.i"). If you compile this snippet without NDEBUG,
> "assert(0 == b.i)" should not fire, because, clearly, Base() sets i to
> 0. And yet, this assert does fire (on common implementations). Why?
> Because your cast invoked UB.
> xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
> Eh? Because you code asserts something to be not equal you assert this to
> mean the cast produces UB? How do you come to that conclusion?
> xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
That something is b.i, which is initialized to 0 and never changed.
Yet, assert does fire.
My claim is that it fires because cast was UB, and, on common
implementations, result is that code sees that b.i equals one, not
zero.
My claim is that implementation is correct in everything it has done,
that code is correct in every detail, except for the UB cast. If cast
wasn't UB, assert(0 == b.i) would not fire. Since it does, it's
because cast is UB.
Goran.
A family tree was an example of a tree which you typically draw upside
down (the root is at the top); an inheritance hierarchy is typically
viewed as a tree where the base classes are at the root of the tree and
similar to a family tree the root is at the top hence the term "upcast".
>
>>
>> Anyway it doesn't matter how it *seems* to you; what is important is
>> to be technically accurate and to use correct terminology: casting
>> from derived to base is called "upcasting"; casting from base to
>> derived is called "downcasting".
>>
> Yes says the proven idiot who thinks p doesn't point to an array with:
> int* p = new int[5]
p is not a pointer to an array; p is a pointer to an int.
You are the one who continually provides evidence to everyone reading
your posts as to who the idiot actually is; you certainly have yet to
provide any evidence or proof that those who continually correct you are
idiots although giving a troll such as yourself any time probably *is*
idiotic.
If you are not a troll and you actually believe the bullshit you post
then your cause is a hopeless one.
/Leigh
Not _every_ cast. Just the one in your code :-)
> A cast from int* to int*[] doesn't seem like a major conversion of any
> major concern to me.
It is a conversion not governed by anything else than [5.2.10/7]. And the
governing clause [5.2.10/7] specifies that the result of the cast is
unspecified. Using those unspecified values farther down the road in any way
not listed in [5.2.10/7] is undefined behavior as per:
1.3.12 undefined behavior
behavior, such as might arise upon use of an erroneous program construct
or erroneous data, for which this International Standard imposes no
requirements. Undefined behavior may also be expected when this
International Standard omits the description of any explicit definition of
behavior.
Best,
Kai-Uwe Bux
I don't say that.
> Or are you saying that its only UB to cast a pointer of Derived (*)[size]
> to BaseB (*)[size], but its ok to cast a Derived* to a baseB*?
I don't even say that. What I say is that casting the pointer p1 (of type
Derived(*)[3]) to the type (BaseB(*)[3]) results in an unspecified value of
p2. What is UB is calling delete[] on the unspecified value p2.
> As far as I was aware is perfectly ok to downcast to a Base class. As long
> as the Base class has a virtual destructor the object destruction will
> destruct the whole Dervied object.
True. If we had
Derived* p1 = new Derived;
BaseB* p2 = (BaseB*)p1;
delete p2;
everything would be perfectly fine. Note, however, that the case is
_entirely_ different, starting with the C-style cast (BaseB*)p1 being a
static_cast<> as opposed to the reinterpret_cast<> from the previous
snippet. Now, the (redundant) cast is governed by [5.2.9/2]. The delete
expression is governed by [5.3.5/3].
Note, however, that this snippet has UB:
Derived* p1 = new Derived [3];
BaseB* p2 = (BaseB*)p1; // fine so far, but p2[1] would be UB
delete [] p2; // UB as per [5.3.5/3]
This involves the distinction of dynamic and static types: p2 has dynamic
type Derived* and static type BaseB*.
Best,
Kai-Uwe Bux
Because you are casting from a pointer of one type to a pointer of an
unrelated type (and that's what the compiler told you, too, when you
tried without a cast, now did it?)
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
No my code compiles fine, The types are not unrelated they are both int
types.
I can cast from int[] to int* implicitly because its a realated type.
UB doesn't mean that something will or will not work.
It means that the Standard simply does not guarantee the behaviour.
>>>So are you are saying is that its
>>> UB to cast a pointer to Derived into a pointer to BaseB?
>>
>> I don't say that.
>>
>>> Or are you saying that its only UB to cast a pointer of Derived (*)[size]
>>> to BaseB (*)[size], but its ok to cast a Derived* to a baseB*?
>>
>> I don't even say that. What I say is that casting the pointer p1 (of type
>> Derived(*)[3]) to the type (BaseB(*)[3]) results in an unspecified value
>> of
>> p2. What is UB is calling delete[] on the unspecified value p2.
>>
>I thought you might actually have a valid point, but now its quite clear
>your just another idiot who has no other objective than to prove anything I
>say is wrong.
Everybody that disagrees with Paul is an idiot, espectially when they use logic
or proper citation of the Standard to prove him wrong.
>
>"Leigh Johnston" <le...@i42.co.uk> wrote in message
>news:sNOdnZtNWPu3-A7Q...@giganews.com...
>> On 30/03/2011 18:06, Paul wrote:
>>>
>>> "Leigh Johnston" <le...@i42.co.uk> wrote in message
>>> news:dqydndSmfoUl1Q7Q...@giganews.com...
>>>> On 30/03/2011 15:38, Paul wrote:
>>>>> As far as I was aware is perfectly ok to downcast to a Base class. As
>>>>> long as the Base class has a virtual destructor the object destruction
>>>>> will destruct the whole Dervied object.
>>>>
>>>> Casting from derived to base is called "upcasting" not "downcasting".
>>>>
>>> Thats a rather back to front way of looking at it. Casting to a
>>> subobject is casting up?
>>
>> Think about how you draw a family tree: ancestors toward the top;
>> descendants toward the bottom (the tree is upside down).
>
>A family tree is exact opposite of a class inheritance, a parent object is
>the most derived object.
>In a family tree the children are most derived.
I am starting to think you are making shit up as you go along here. And when
you are called on your wrongness, you become pridefully defensive and argue the
most inane points instead of just acknowledging those who have helped you to be
correct.
TCPPPL 15.4 Class Hierarchies:
"Casting from base class to a derived class is often called a downcast because
of the convention of drawing inheritance trees growing from the root down.
Similarly, a cast from the derived class to the base is called an upcast."
Of course, you're entitled to your own language with its own vocabulary and its
own set of terms, but keep in mind that when you come to these forums, you are
going to be severely limited in such a way that you feel that everybody here is
trying to prove everything that you say is wrong.
>
>>
>> Anyway it doesn't matter how it *seems* to you; what is important is to be
>> technically accurate and to use correct terminology: casting from derived
>> to base is called "upcasting"; casting from base to derived is called
>> "downcasting".
>>
>Yes says the proven idiot who thinks p doesn't point to an array with:
>int* p = new int[5]
Ahh, I see, you're still confused. Well I am all too happy to help. That's
what we are here for :)
p is a pointer to the single int that was returned by new.
p is pointer of "int type", not "array of int type"
p only points to a single int
Anything else that you think p points to is meaningful only in your mind.
Consult ISO/IEC 14882 for further information and clarification if you are still
confused.
By the way, I proudly stand among those that you call idiots since this
distringuished group of idiots are all of those that have successfully used
simple logic and proper citation of The Standard to prove you wrong.
Welcome to the club. The rest of us realized this a month or two ago.
Just ignore the troll.
>
> You are the one who continually provides evidence to everyone reading your
> posts as to who the idiot actually is; you certainly have yet to provide
> any evidence or proof that those who continually correct you are idiots
> although giving a troll such as yourself any time probably *is* idiotic.
>
> If you are not a troll and you actually believe the bullshit you post then
> your cause is a hopeless one.
>
Very good but it's obvious to anyone, with isn't an idiot, that you are an
idiot. As for the idiots who agree with you, I don't really care what they
think .
:-)
Whilst p does point to the initial element of an array that does not
mean that p is a pointer to an array; p is a pointer to an int.
[...]
/Leigh
Irrelevant. There's gigabytes of source that compile fine, but does
not work.
> , The types are not unrelated they are both int
> types.
> I can cast from int[] to int* implicitly because its a realated type.
You can, but that is irrelevant for what you're trying to do. Besides,
cast is already implicit, so why would you!?
The problem is elsewhere: when you do "new TYPE[size1][size2]", the
type of result is "TYPE**", and that is __unrelated__ to "TYPE (*)
[size2]". Ppeople already have quoted you relevant portions of the
standard, and I even went through the hassle of showing you, in great
detail, __why__ what you are trying to do can't work well. In the
process of doing so, I offered you, effectively, a __proof__ that your
cast is UB (this type of proof is commonly called "proof by
contradiction", and I can rephrase my proof more formally if you
wish).
You should take note of this, I believe: by your own words: "I can
cast from int[] to int* __implicitly__ (emphasis mine) because its a
realated type". However, no implementation allowed you to assign
result of new TYPE[size1][size2] to TYPE(*)[size2]. There is __no__
implicit conversion there (and with a good reason, too, see above). So
you resorted to a cast. An UB one.
Goran.
I don't know what definition you are talking about, but either the
definition, or your interpretation thereof, is wrong.
Return type of expression "new TYPE[5]" is "TYPE*", not e.g. TYPE[5],
or, I dunno, TYPE(*)[5]. Array (as in TYPE[size]) is in no way
involved there. Arrays and pointers mix in some other contexts^^^.
It looks like the syntax (new TYPE[5]), that resembles an array
declaration, confuses you. You might want to think of it this way:
int* p = new int(1); // p is a pointer to int.
p = new int[2];
// p is __still__ a pointer to int
int i;
p = &i;
// p is __still__ a pointer to int
int a[3];
p = a; // ^^^ implicit array-to-pointer conversion
// p is __still__ a pointer to int
You seem to believe that there is something as pointer-to-array
conversion (or pointer-to-pointertoarray conversion). That belief is
wrong.
Goran.
I don't know what definition you are talking about, but either the
definition, or your interpretation thereof, is wrong.
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
The definition in the C++ standard says it points to the first element OF AN
ARRAY.
therefore by definition it points to an array you idiot.
<snip >
>
>"Goran" <goran...@gmail.com> wrote in message
>news:362fae78-1af4-4f00...@k22g2000yqh.googlegroups.com...
>On Mar 31, 3:22 am, "Paul" <pchris...@yahoo.co.uk> wrote:
>> >>>> Yes says the proven idiot who thinks p doesn't point to an array
>> >>>> with:
>> >>>> int* p = new int[5]
>>
>> >>> p is not a pointer to an array; p is a pointer to an int.
>> >> No p points to the initial element *of an array*, by definiton it
>> >> points
>> >> to an array.
>
>I don't know what definition you are talking about, but either the
>definition, or your interpretation thereof, is wrong.
>
>xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
>
>The definition in the C++ standard says it points to the first element
stop right there and you'll seem like less of a degenerate.
>OF AN ARRAY.
Pointing to the first element is not the same thing as pointing to the array.
The array is a different type than is the first element, no matter how many
times you beligerantly say otherwise.
>therefore by definition it points to an array you idiot.
lol, by your definition, which everybody who has responded to you has indicated
is meaningless.
your points are all meaningless paul. congratulations for all your time well
spent :)
First part, I agree. Second part (the "therefore" one), is false (and
is not in the standard, either).
Here, a practical example why __your__ "therefore" is wrong:
class A
{
public:
A() : i(0) {}
const int i;
};
class B : public A
{
public:
B() : j(1) {}
const int j;
};
int main(int, char*[])
{
const B b[2];
const A* a = b;
if (a->i == 0)
std::cout << "a points to the array b" << std::endl;
a++;
std::cout << "I did a++; now it points to the second element of the
array b;" << std::endl;
std::cout << "That's what pointers do, right?" << std::endl;
if (a->i == 1)
{
std::cout << "Hmmm... no object of type A has i that equals 1." <<
std::endl;
std::cout << "I must have invoked UB somewhere. I wonder where..."
<< std::endl;
std::cout << "Perhaps in the part where I said that a points to
the array b?" << std::endl;
std::cout << "Nooo... Un-possible!";
}
return 0;
}
Goran.
No, it is not. The type of this new expression is TYPE(*)[size2]
(assuming TYPE is not an array-type).
On 31 Mrz., 11:52, Paul wrote:
> [...]
> The definition in the C++ standard says it points to the first element
> OF AN ARRAY. therefore by definition it points to an array [...]
Wow. Apparently, the discussion is still about the banana vs bunch of
bananas thing. I still see no definition of the word "pointer" or the
expression "points to X" in the C++ ISO standard that would justify
your use of the words "therefore by definition" in this context.
Consider the possibility that you are (perhaps unintentionally)
augmenting the standard's text with your personal understanding of
what "points to X" means exactly.
Regardless of the interpretation of "points to X", an array is not a
pointer. Since you evaded revisiting this claim of yours in favour of
this stupid discussion about the semantics of "points to X", my
conclusion is you are too proud to admit that you were wrong about the
"an array is just a pointer" issue.
SG
By whose definition? Yours? Your own personal definitions of things
carry no weight here. The fact that p points to the initial element of
an array does not mean that p is a pointer to an array; p is a pointer
to an int.
> Were you born stupid or did you grow into it?
Occam's Razor my dear chap: which is more likely: everybody except Paul
is stupid or just Paul is stupid?
/Leigh
Whoops, indeed. Heat of the discussion ;-) ...
Goran.
First part, I agree. Second part (the "therefore" one), is false (and
is not in the standard, either).
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
It IS the defintioon in the standard, the standard states it points to the
first element OF AN ARRAY, therefore it cannot NOT point to an array.
The standard certainly doesn't state, that it doesn't point to an array, as
you are saying.
You do not accept the opinion of experts who say that a T* can point to a T,
or an array of T's.
<snip>
You are just going round in circles. Again: there is a difference
between the not so technical term "points to" and the technical term "a
pointer to".
A *pointer to an int* is *a pointer to an int* not *a pointer to an
array*; pretty simple IMO so I wonder why you don't "get" it.
/Leigh
> Regardless of the interpretation of "points to X", an array is not a
> pointer. Since you evaded revisiting this claim of yours in favour of
> this stupid discussion about the semantics of "points to X", my
> conclusion is you are too proud to admit that you were wrong about the
> "an array is just a pointer" issue.
>
This is absolute bollocks , I never said an array was the same as a pointer,
I say that in some situations it is, for all intensive purposes, the same.
Now you say you know more about my views than I do? Go to France you fin
idiot.
You obviously have an overwhelming desire to get your own back after the
many times i have proved you wrong. I have won this argument arleady and I
see no need to reply to your idiotic nonsense anymore.
Indeed; nobody said otherwise.
> This is a pointer to an array , not a pointer to a single int, pretty
> simple:
> int* p = new int[6];
p is a pointer to an int not a pointer to an array; you can bleat that
this is incorrect as much as you like but it will not change the fact
that it is a correct statement.
>
> You obviously have an overwhelming desire to get your own back after the
> many times i have proved you wrong. I have won this argument arleady and
> I see no need to reply to your idiotic nonsense anymore.
>
Get my own back? Why? You have not proven me or anybody else wrong so
far. If you ever do prove me wrong about something I will admit my
mistake; I certainly wouldn't want to "get my own back". Also: the ad
hominem is a logical fallacy.
/Leigh
First things first. You do understand now that casts you tried to pull
off are UB, right?
Goran.
OK, so, let's say that what you say is true... (proof by contradiction
follows...)
In my previous example... What does "a" in "const A* a = b" points to
then? Clearly, b is an array. So what does a points to? To an array of
A, an array of B, or what? Clearly, b is an array, code is legal, no
casting, no funny business...
Further, if a points to an array of A, why incrementing it gives
strange results? If it's an array of B, how can that even compile? (B
__is__ A, but A __is not__ B).
I know that you have no relevant answer to any of these questions, so
I'll give it to you: "a" points to __the first element of "b"__. It
points to no array, ever.
Goran.
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
What exactly is UB? Casting in general produces UB, or only when I cast?
Is it a case that you are not interested in discovering the true language
technicalities and that your sole intention is to prove that I am wrong
about something.
In your post here: http://groups.google.com/group/comp.lang.c++/msg/1948e01fd0971cac,
I claimed that first two casts were UB. You claimed that they were
not. I proved you that they were (read back, please).
Goran.
I quoted you correctly. You said "an array is just a pointer". You
used this as subject to one of the threads that spawned over 200 other
posts.
> I say that in some situations it is, for all intensive purposes, the same.
An array is never a pointer. What you tried to say should have been
worded like this: One can use an array expression in contexts where a
pointer is required due to the array-to-pointer standard conversion.
That's about it. You're just being overly broad and imprecise, as
ever. If you want to avoid looking dumb, you should put a little more
effort into precise wording.
Also, it strikes me as odd that a presumably native English speaker
who claims to be smarter than everybody who disagrees with him in here
says "intensive purposes" when "intents and purposes" is what he
meant.
> Go to France you fin idiot.
Oh, how could I forget the name calling. Yes, that's definitely
indicating cleverness.
SG
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
You haven't proved anything is UB, In C++ casting is not UB , its
conversion form one type to another. Initially you said casting the return
from new was UB but since I have stated the standards states the return from
new must be castable you have changed your tune to , "your cast is UB
because I say so". Your code with assets and comparisons does not prove that
a cast is UB it only proves you can write code that has bugs.
Are you saying that casting a pointer to an array is UB? , Who knows because
you are not being specific, you are simply stating my cast is UB because you
say so.
Is this UB in your mind then:
template <typename T, int S>
T* foo(){
return static_cast<T*>(::operator new(S * sizeof(T)));
}
int main() {
int (*p)[6] = foo<int[6], 6>();
}
.
You do not define what I say , I do.
*facepalm*
I kept wondering what was so intensive about the purposes...
Peter
lulz.
/Leigh
lol, priceless
I guess your repetitive misuse of that expression shows that you
literally "don't know what you're talking about", i.e. what the words
you fail to use actually mean.
I bet you're one of those who don't get there/their/they're right.
Your response only shows that you are unable to own up to your mistakes.
Peter
Admit to your mistakes you liar.
/Leigh
Intent sieve porpoises?
The very idea is full of holes.
--
Drew Lawson I had planned to be dead by now, but
the schedule slipped, they do that.
-- Casady
A friend just posted this link on another group:
http://www.politicsforum.org/images/flame_warriors/flame_77.php
I though it apt here as well.
--
Ian Collins
Yes I have. Read this again:
http://groups.google.com/group/comp.lang.c++/msg/e274c527afabc5f4
Now... (proof by contradiction follows... Do you know what "proof by
contradiction" means, BTW? I hope so. If not, I hope you can google
it.)
So... See the code? See the line:
Base (*p2)[3] = (Base (*)[3])new Derived[2][3];
?
You claimed that one should be able to do it, in this post:
http://groups.google.com/group/comp.lang.c++/msg/310cebb655d59715
So, presuming that your cast is not UB, I made you a bit of code that
uses your cast and doesn't work correctly. I'll repeat offending part,
slightly modified, since you seem to have an issue with assert macro
(a macro is not a statement, which is something you don't seem to
understand well):
Base (*p2)[3] = (Base (*)[3])new Derived[2][3]; // Presumption: no UB
in cast.
const Base& b = p2[0][1];
if (b.i) std::cout << "This is not possible!";
I'll also show you the correct code:
Derived(*p2)[3] = new Derived[2][3];
const Base& b = p2[0][1];
if (b.i) std::cout << "This is not possible!";
Contradiction is: the only difference between these two snippets is
the the cast^^^. If there's no UB in the cast, why doesn't first
snippet work? Answer: cast is UB; by consequence, anything that
follows is wrong.
^^^ Bar the use of Base type. But since e.g. "Base (*p2)[3] = new
Derived[2][3];" doesn't even compile, that's immaterial.
Goran.
Yes I have. Read this again:
http://groups.google.com/group/comp.lang.c++/msg/e274c527afabc5f4
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
This is your code that is wrong not the cast. You cannot even explain what
is wrong with the cast in your opinion , its just wrong?
I provided the following code as an alternative which you snipped and
ignored. And you replaced it with your buggy shit code.
The following is the same C cast but in C++ style, is this also UB in your
mind?
template <typename T, int S>
T* foo(){
return static_cast<T*>(::operator new(S * sizeof(T)));
}
int main() {
int (*p)[6] = foo<int[6], 6>();
}
<Snip>
http://groups.google.com/group/comp.lang.c++/msg/e274c527afabc5f4
?
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
This is because you think that the pointer-type Base (*)[3], points to an
array of Base[3], but it doesn't it points to an array of Derived[3].
Its your code that is wrong because you derefernce p2 as if it were a Base,
but it points to a Derived.
p2[i] returns i*sizeof(Base[3]), this is not the same as
i*sizeof(Derived[3]).
The memory layout of the object array is as follows:
[[Base][Derived]] [[Base][Derived]] [[Base][Dervied]]
You are trying to index this array in chunks sizeof(Base[3]).
This example to shows that the pointer-type does not define the object it
points to.
p2 points to an array of Derived, not an array of Bases.
Problem line is
Base (*p2)[3] = (Base (*)[3])new Derived[2][3];
and __you__ wrote it first.
I changed Base in a way that shows that your code won't work. But I
did not change the offending part.
It looks like you think that "not UB" means "seems to work on my
code"?
> You cannot even explain what
> is wrong with the cast in your opinion , its just wrong?
My explanation is fine compared to your lack of understanding what
"proof by contradiction" means.
> I provided the following code as an alternative which you snipped and
> ignored. And you replaced it with your buggy shit code.
Not true. Nothing in what __I__ wrote is wrong. Errors are in what
__you__ have written, that I took over. My purpose was clear: to show
you __how__ and __why__ what you wrote fails. You really seem to think
that "not UB" means "seems to work on my code".
> The following is the same C cast but in C++ style, is this also UB in your
> mind?
>
> template <typename T, int S>
> T* foo(){
> return static_cast<T*>(::operator new(S * sizeof(T)));}
>
> int main() {
> int (*p)[6] = foo<int[6], 6>();
>
> }
It does not matter whether cast is C or C++-style.
Yes, this is UB, and it's __much__ worse than your old examples. If
you try this on __any__ type that has a constructor, this is going to
fail __massively__. Just put e.g. this in your main:
std::string (*p)[6] = foo<string[6], 6>();
And watch this lunacy of yours fail.
You can legally use static_cast to go from void* to some other type,
that's OK, but that does not mean that code can't exhibit UB.
Goran.
Being polite when asking for help is somewhat better. You might want
to try e.g. this way:
"I don't understand compiler errors. I did ABC, compiler told me XYZ.
What should I do?"
Goran.