question about new and delete operator

L

LiDongning

Hi,
I'm working on a class which allocate some memory to store an array
(pointed by a pointer p_data). In constructor I use new to do the
allocation, in destructer I use delete to free the memory. In one
member function I wan to do something with the data (say, sort). In
that case, inside that function, I plan to allocate a piece memory to
store the processed data (p_processed), then free the original memory
(p_data), and point the p_data to p_process. The code will be
something like attached below. My question is, can i do something like
in the last two lines of code? Will this cause a memory leak or any
harmful effect? Thanks very much!

template <class T>
class data
{
int datanum;
T* p_data;
public:
data(int, T*);
~data();
void process();
};

template <class T>
data<T>::data(int in_n, T* in_data):datanum(in_n)
{
T* p_data = new T[datanum];
for (int i=0; i<datanum; i++) {p_data=in_data;}
}

template <class T>
data<T>::~data()
{
delete []p_data;
}

template <class T>
void data<T>::process()
{
T* p_processed= new T[datanum];
//do something, so p_processed will be filled with processed data
for (int i=1; i<datanum; i++) {p_processed=p_data;}
//processing finished
delete []p_data; // can i
do this??
p_data = p_processed; // can i do
this??
}
 
J

Jonathan Lee

HI LiDongning,
void data<T>::process()
{
     T* p_processed= new T[datanum];
     //do something, so p_processed will be filled with processed data
     for (int i=1; i<datanum; i++) {p_processed=p_data;}
     //processing finished
     delete []p_data;                                        // can i
do this??
     p_data = p_processed;                             // can i do
this??

}


That's fine. You've just exchanged the arrays. p_data will still
be a valid pointer for when the destructor is called.

But the way you've phrased it, why not just calculate the result
directly in p_data and then delete[] p_processed? It seems that they
are the same size and p_processed is initialized to be a copy
of p_data. I'm not sure what would stop you from making p_data
the destination for your processing. If you were really cautious
about the delete[], then that would be a solution.

--Jonathan
 
Y

Yan

     T* p_processed= new T[datanum];
     //do something, so p_processed will be filled with processed data
     for (int i=1; i<datanum; i++) {p_processed=p_data;}
     //processing finished


it's seems all good. i would however, if i were you, make sure that
the 'processing' part doesn't throw, because if it does the memory
allocated for p_processed won't get freed.
 
L

Lars Tetzlaff

LiDongning said:
Hi,
I'm working on a class which allocate some memory to store an array
(pointed by a pointer p_data). In constructor I use new to do the
allocation, in destructer I use delete to free the memory. In one
member function I wan to do something with the data (say, sort). In
that case, inside that function, I plan to allocate a piece memory to
store the processed data (p_processed), then free the original memory
(p_data), and point the p_data to p_process. The code will be
something like attached below. My question is, can i do something like
in the last two lines of code? Will this cause a memory leak or any
harmful effect? Thanks very much!

I would recommend to use std::vector like this:

#include <vector>

template <class T>
class data
{
std::vector<T> m_data;
public:
data(int, T*);
void process();
};

template <class T>
data<T>::data(int in_n, T* in_data)
: m_data( in_data, in_data + in_n )
{
}

template <class T>
void data<T>::process()
{
std::vector<T> processed( m_data.begin(), m_data.end() );
// do something with your copy

// assign processed data to m_data
m_data.swap( processed );
}


This code is shorter *and* exception safe.


Lars
 
L

LiDongning

HI LiDongning,
void data<T>::process()
{
     T* p_processed= new T[datanum];
     //do something, so p_processed will be filled with processed data
     for (int i=1; i<datanum; i++) {p_processed=p_data;}
     //processing finished
     delete []p_data;                                        // can i
do this??
     p_data = p_processed;                             // can i do
this??


That's fine. You've just exchanged the arrays. p_data will still
be a valid pointer for when the destructor is called.

But the way you've phrased it, why not just calculate the result
directly in p_data and then delete[] p_processed? It seems that they
are the same size and p_processed is initialized to be a copy
of p_data. I'm not sure what would stop you from making p_data
the destination for your processing. If you were really cautious
about the delete[], then that would be a solution.

--Jonathan


Jonathan,
Thanks for the reply. The reason I allocate a p_process array is that
I want to do a sorting for the data in the p_data array. If I use a
binary tree algorithm, the original data have to be kept until the
sorting is finished.
DOngning
 
L

LiDongning

I would recommend to use std::vector like this:

#include <vector>

template <class T>
class data
{
  std::vector<T> m_data;
public:
  data(int, T*);
  void process();

};

template <class T>
data<T>::data(int in_n, T* in_data)
: m_data( in_data, in_data + in_n )
{

}
Exactly! Thanks much!
 
J

Juha Nieminen

Hendrik said:
Why 'int'? Will there ever be a sequence of -5 bytes?

In my experience using signed types even in situations where negative
values have no rational meaning lessens problems in the long run.

For example, assume you are making a class which represents an image.
The class will have two integral members denoting the pixel width and
height of the image. Since it doesn't make sense to have images with
negative width or height, the most logical thing to do would be to make
these two integrals unsigned, right?

Well, no. Let's assume that in the application where the class is used
the images are drawn on screen, eg. as sprites. The image may be drawn
partially outside of the screen. In other words, the coordinates where
the image is drawn may well be negative.

Now, if you need to perform any arithmetic using the coordinates and
the dimensions of the image (which is very usual in such situations),
you will have a clash between signed and unsigned types. In certain
situations you might end up with incorrect results (eg. if floating
point calculations are involved, something might end up at the
coordinate 4294967295 or whatever). I have had this problem in practice,
so it's not purely theoretical stuff.
 
J

James Kanze

Why 'int'? Will there ever be a sequence of -5 bytes?

Because int is the "standard" type for integral values in C++.
Anything else should only be used if int won't do the job; the
only justification for not using int here would be that there
might be sequences of more that INT_MAX bytes.
 
A

Alf P. Steinbach

* Hendrik Schober:
I'm sorry I missed this. I'm not a regular reader. I don't
have enough time to be. Feel free to ignore any discussion
stemming from this.


Well, call me strict, but I happen to believe that, for
specifying sizes, unsignedness is strongly needed. I also
believe that, if a positive value is needed in order to
call a function, the function's interface should say so.
Yes, I know, implicit promotion etc. can wreak havoc when
you have huge unsigned numbers.

Apparently you don't understand the problems.

Still, if I have to decide
between having the right interface and maybe having to
find funny errors due to this, or having a wrong interface
and maybe having to find funny errors due to negative
values being past, I'd go for the right interface.

In C or C++, unsigned type is far more likely to yield "funny errors".

Choosing types in a good way is a matter of programming language.



Cheers & hth.,

- Alf
 
J

James Kanze

James said:
Lars Tetzlaff wrote: [...]
template <class T>
data<T>::data(int in_n, T* in_data)
: m_data( in_data, in_data + in_n )
{
}
Why 'int'? Will there ever be a sequence of -5 bytes?
Because int is the "standard" type for integral values in
C++. Anything else should only be used if int won't do the
job; the only justification for not using int here would be
that there might be sequences of more that INT_MAX bytes.
You state this as an axiom.

Because it is an axiom. It's part of the original definition of
C.
Note that I asked "why".

Because that's the way the language designers designed the
language. I'm not saying that it's right---I sort of liked
Modula-2's CARDINAL type. But C and C++ don't have one.
Had I subscribed to the axiom, I wouldn't have asked.
(To cut a few corners in this discussion: I subscribe
to the axiom of always choosing the data type that is
the best representation of the data. If the data is
natural, positive numbers, an unsigned type seems to
be the natural fit for the job.)

To be reasonable, you need to include behavior in the
representation. If the data is natural, positive numbers,
subtraction can still result in a negative value---when choosing
a type, either the type will allow a signed result in the case
of subtraction, or it won't support subtraction (case of
Modula-2). You also have to choose the type in the context of
the whole type system: in C++, if you use unsigned in an
expression with a signed, it changes the meaning of the signed
in strange ways.

In the end, a language is designed the way it is designed. You
can fight it, but you won't win. Kernighan and Richie decided
that the principal integral type in C would be int; all other
types have been added for specific needs, but int remains THE
integral type. Anytime you use anything else (for integral
values, of course), you're fighting the language: introducing
additional complexity and additional possibilities for errors.
 
B

Bo Persson

Hendrik said:
I'm sorry I missed this. I'm not a regular reader. I don't
have enough time to be. Feel free to ignore any discussion
stemming from this.


Well, call me strict, but I happen to believe that, for
specifying sizes, unsignedness is strongly needed. I also
believe that, if a positive value is needed in order to
call a function, the function's interface should say so.

But that doesn't save you from anyone passing a -5, as that will
"work" for an unsigned parameter as well.



Bo Persson
 
A

Alf P. Steinbach

* Hendrik Schober:
Bo said:
Hendrik said:
Paavo Helde wrote:
Hendrik Schober <[email protected]> kirjutas:
[...]
Why 'int'? Will there ever be a sequence of -5 bytes?
We have had this discussion before in this group, several times.
I'm sorry I missed this. I'm not a regular reader. I don't
have enough time to be. Feel free to ignore any discussion
stemming from this.

In
C++, the unsigned types are quite specific types with special
rollover effects and there are tricky promotion rules for
mixed-signedness arithmetic expressions. Unless unsignedness is
strongly needed, I would avoid those types.
Well, call me strict, but I happen to believe that, for
specifying sizes, unsignedness is strongly needed. I also
believe that, if a positive value is needed in order to
call a function, the function's interface should say so.

But that doesn't save you from anyone passing a -5, as that will
"work" for an unsigned parameter as well.

Of course, it doesn't. However, it does communicate
that -5 is not a valid value.

If you fail to communicate the function's contract to client programmers then
you have a problem. You seem to think that that communication problem can be
alleviated by using unsigned types in C or C++. Well that's backward: to anyone
competent you're just communicating that you don't know what you're about, so
that it would be wrong to expect *anything* to be reasonable, i.e., by using
unsigned type you actively engage in failing to do what you wanted, communicate.

And if the function fails to enforce its contract to the degree possible you
have a much more serious problem, because all experience shows that without
automated error detection it's practically impossible to avoid errors.

The client code programmer may have a 100% perfect understanding of your
function's contract, but still manage to call it with invalid arguments.

And what happens when you pass -5 to your unsigned formal argument?

The language standard then guarantees wrapping, that you get the value 2^n-5
where n is the number of value representation bits, and do you check for that?
No? I thought so.


Cheers & hth.,

- Alf
 
J

James Kanze

Bo Persson wrote:

[...]
Of course, it doesn't. However, it does communicate that -5 is
not a valid value.

Not really, since -5 is a perfectly legal value to use when
initializing an unsigned. But that's not really the point: the
unsigned integral types in C++ are fairly special, and using
unsigned communicates (to someone who knows C++, at least) that
you require some of its special qualities: modulo arithmetic, or
correct behavior for bitwise operators.
 
J

James Kanze

James said:
James Kanze wrote:
Lars Tetzlaff wrote:
[...]
template <class T>
data<T>::data(int in_n, T* in_data)
: m_data( in_data, in_data + in_n )
{
}
Why 'int'? Will there ever be a sequence of -5 bytes?
Because int is the "standard" type for integral values in
C++. Anything else should only be used if int won't do
the job; the only justification for not using int here
would be that there might be sequences of more that
INT_MAX bytes.
You state this as an axiom.
Because it is an axiom. It's part of the original
definition of C.
Mhmm. I've never owned a C standard, so I only know from
hearsay, but so far I always assumed 'int' was "the native
integer type on the platform",

And how is this different from "the standard type"?
meaning that it is the one the platform is fastest with.

Speed doesn't necessarily have much to do with it. It's true
that on some platforms, anything involving unsigned is
significantly slower than signed, but on most platforms, it
makes absolutely no difference, and never had.
[...]
To be reasonable, you need to include behavior in the
representation. If the data is natural, positive numbers,
subtraction can still result in a negative value [...]
Yep. And when dividing with 'double', you have to be
careful, since a '0.0' might be fatal. What's the
fundamental different to subtracting unsigned integers?

Because there's no relationship whatsoever between them. The
subtraction of two natural numbers is a well defined operation;
division by 0 isn't.
Mhmm. Then who added 'size_t' to be used for sizes
to this language?

I don't know. That came a lot later. (Don't forget, however,
that size_t was originally designed to represent something very
low level, which often wouldn't fit in an int.)
 
N

Nick Keighley


ok. DbC and all that

not really as you can stuff -5 into an C++ unsigned type.
The point being that C++ unsigned types have odd (though useful)
semantics

yes

Right. Which is why I put great emphasis on communicating
this. In the language, rather than in comments.

Alf never said comments. In fact he talks about "automatic
detection". I think he's thinking asserts or similar measures.

You seem to think that that communication problem can be
alleviated by using unsigned types in C or C++. [...]

And you seem to think that communication between callers
and callees has to happen solely through comments. (Just
as stupid an accusation, I know. Couldn't resist, though.)

since he never said that, that makes most of the next paragraph
moot

If communication solely through comments was fine, all
functions should take and return only 'void*'. (Or they
might take an ellipsis. You can communicate the number
of arguments through comments, after all.) Since this not
only isn't done, but frowned upon, and since the language
even more and more develops towards the exact opposite
(expressing of constraints directly in the language,
instead of in comments), I conclude there's a general
consensus that this is better.

I fwe thought this was generally true we'd be writing Ada
The client code programmer may have a 100% perfect understanding of your
function's contract, but still manage to call it with invalid arguments..
And what happens when you pass -5 to your unsigned formal argument?
The language standard then guarantees wrapping, that you get the value 2^n-5
where n is the number of value representation bits, and do you check for that?
No? I thought so.

Does your implementation of
   std::vector<T>::eek:perator[](size_type)
check whether you pass -5 into it?

I believe there are checking versions. The STL generally
emphasises speed. It's up to you to validate arguments before you
pass
them to the STL. You wouldn't read the index from a terminal and then
pass it to the STL would you?
 
N

Nick Keighley

Because it is an axiom.  It's part of the original definition of
C.

I don't recall this (and *I* have read the ANSI C standard). You
seemed to be stating a *design* axiom. That you use int unless there
is a reason to the contrary. I don't see this being part of the
original
language definition. It may be sensible but I don't see it as being
axiomatic.


<snip>
 
J

James Kanze

I don't recall this (and *I* have read the ANSI C standard).

The ISO C standard isn't the original definition of C.
Kernighan and Richie are (but I think you've read them as well).
The original C didn't even have unsigned. (It was added, along
with long, before the K&R book was published, however. I don't
have my copy of the book handy to verify, but I do seem to
remember something about "int being the natural type" in there.)
You seemed to be stating a *design* axiom. That you use int
unless there is a reason to the contrary. I don't see this
being part of the original language definition. It may be
sensible but I don't see it as being axiomatic.

Well, the committee did redefine it (at least compared to the
compilers I was using at the time, including Johnson's pcc) in a
way that means that you can't effectively mix it with signed
integers, so that things like "someUnsigned < -5" may be true.
Which certainly implies that they didn't mean for it to be used
for numeric values.

I'm not sure what Kernighan and Richie intended when they first
introduced it. But at least the compilers I used which came
from AT&T at the time did interpret "someUnsigned < -5" as
always false. (At least as I remember it. It's been quite some
time since I've used pcc.)
 
G

Greg Herlihy

...that has a result which cannot be represented by
a natural number. <shrug>

In C++, the difference between any two unsigned numbers is always
another unsigned number. So the reason why an unsigned type is such a
poor choice to represent a natural number - is because an unsigned
type does not behave like a natural number. And since representing
data is as much about representing behavior as it is about
representing a value - it should be clear that - when representing
numeric values - an unsigned type should always be the choice of last
resort.

Greg
 
J

James Kanze

Nick said:
[old debate generously snipped]
Does your implementation of
std::vector<T>::eek:perator[](size_type)
check whether you pass -5 into it?
I believe there are checking versions. [...]
I wonder how they check this.

They don't have to. It's impossible. size_type must be
unsigned, so there's no possible way to pass a -5 through it.

Of course, the results of converting -5 to size_type are going
to be a very larger number. Almost (but not quite) surely large
enough, in most cases, that it will be out of bounds.
 
J

James Kanze

[...]
...that has a result which cannot be represented by
a natural number. <shrug>

Exactly. And if the language defines a type T such that T - T
-> T, then that type isn't an abstraction of the natural
numbers. For a type to be usable as an abstraction of the
natural numbers, the results of subtraction must be another
type, or subtraction must not be supported. (Modula-2, for
example, takes that latter course.) C++ doesn't have such a
type; the closest we can come is int.
[...]
In the end, a language is designed the way it is designed. You
can fight it, but you won't win. Kernighan and Richie decided
that the principal integral type in C would be int; all other
types have been added for specific needs, but int remains THE
integral type. Anytime you use anything else (for integral
values, of course), you're fighting the language: introducing
additional complexity and additional possibilities for errors.
Mhmm. Then who added 'size_t' to be used for sizes
to this language?
I don't know. That came a lot later. (Don't forget, however,
that size_t was originally designed to represent something very
low level, which often wouldn't fit in an int.)
It is recycled throughout most of the C++ std lib (as
'size_type').

I know. That's obviously an error in the conception of the
library, but it's too late to change it now, despite the
problems it causes.
 

Ask a Question

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

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

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,009
Latest member
GidgetGamb

Latest Threads

Top