DMA
Dynamic
Memory Allocation
DMA allocates
memory for use in program at the time the program runs. An example of this
is a word processor, which allocates memory "on the fly".
Routines for
allocating memory are in:
<cstring> // contains info on memcpy
<cstdlib> // info on malloc and
free
malloc:
Malloc in C stands for memory allocation. It
takes one argument, which is an integer representing the number of bytes you
want to allocate, and returns a pointer to a void.
void
* malloc (int n);
Example of
use:
int
*p = 0; // initializes pointer to 0
p =
(int *) malloc(sizeof(int)) // sizeof assures correct
number of bytes
now you can use
like…
*p =
7;
**
IMPORTANT**
Whatever you
allocate, you are responsible and MUST free up when done using it to avoid
memory leaks. This is done using the keyword free.
free:
free will return memory allocated using
malloc. An example of it's
use follows.
free(p);
//this free's up memory
allocated for *p
Note:
If this memory was allocated inside a function and was not freed up before
you left function, you would most likely have a memory leak. When you
leave the function you lose your variable but then you have allocated memory
that is not addressable from anywhere.
What about
memory for more than one thing?? Like an array of integers? Let's
try for 100 integers and only 100 integers…
int
ar = 0;
int
size;
.
.
.
size = 100;
ar
= (int ) malloc (sizeof (int) *size);
This will
allocate dynamic memory for 100 integers. Now you can take this pointer
and reference it as an array.
Example:
cout << ar[7];
free (ar);
There is more
information on "on the fly" memory allocation in the man pages under realloc.
Problem:
Malloc and free have one problem. Malloc does not initialize allocated memory.
This is why C++ came up with a new way to allocate memory using the
keyword new. This is the
method we will use in this class from now on.
new:
new is an operator which takes on the right side,
type of memory to allocate…
Example:
int
*p;
p =
new int;
notice that you
didn't need anything like sizeof. The compiler
takes care of this. Also, you don't have to do casting because it will
also check for that. Another added bonus is that new will allocate memory and call the constructor to
initialize it (yippee!).
Example:
class Widget
{
…
};
Widget *w, *v;
w =
(Widget *) malloc (sizeof(Widget)); // this is the C
way
v =
new Widget;
//
this is the way for C++
It's much easier
AND calls the constructor.
Freeing up the
memory is now replaces the command free, with
the keyword delete.
delete:
Delete takes a
pointer to the type to free up memory for.
free (w);
// this is what you did in C using malloc
delete v;
// this is what C++ uses when using new
IMPORTANT:
Under no
circumstances can you use free to replace memory
that was allocated using new or delete for memory allocated using malloc. You can use either or both in C++ but
you must use with corresponding types.
new and delete are only for C++ and are keywords so you don't
have to "include" anything.
Example:
now allocate an array of 10 Widgets…
Widget *x_ar;
x_ar = new Widget[10];
This allocates
memory and calls constructor for all 10 Widgets. But there is one minor wrinkle.
When you release memory for an array of Widgets it is slightly
different.
delete [] x_ar;
What would
happen if you were to code…
p =
new int[10];
delete p; // instead of delete []
p
or…
q =
new int;
delete [] q; // instead of delete q
not much at run
time but it will catch up with you so make sure you use proper delete
operator.
One more thing
about the new constructor and taking arguments.
Example:
to dynamically allocate memory for a complex variable…
complex *c, *d, *e;
c =
new complex;
d =
new complex(2.7);
e =
new complex(3, 1.7);
*** you can call
whatever constructor you want BUT you can't do it for arrays.
arrays will
always call the default constructor so always make default constructor if you
could possibly use an array in the class.
New
Implementation of String class Using DMA:
if we were to
use the String class we created earlier and ran over the size of memory that was
set aside for the array we would be toast. So…let's create a new String
class using DMA.
class
String
{
private:
char *data;
int len;
public:
String();
String(const char *);
// initialize from C style
string
.
.
.
};
/*******************************************************/
String::String()
{
len = 0;
data = 0; // makes sure
it doesn't point to something important
}
/*******************************************************/
String::String(const char *s)
{
len = s.len;
data = new char[len]
// loop to copy all characters from s into
data
for (int i=0; i<len; i++)
data[i] =
s[i];
}
Tip:
Look into memcpy in libraries manual for
more information
But wait a
minute… How are you supposed to free up memory here??
String s =
"disaster";
when it's
created you have…
|
*s |
len = 11 |
à |
d |
i |
s |
a |
s |
t |
e |
r |
when the
function ends it will free up *s but it will now free up the memory used by the
string. The solution for this is to use a destructor.
destructor:
in addition to
the constructor in the class declaration you need to include…
~String();
and in the .C
file you need the method:
String::~String()
{
delete [] data; // use this way to
delete memory for array of char's
}
the destructor will be called automatically so you don't
need to worry about calling but you must have it or you will run into big
problems.
delete will automatically call the destructor before
it frees up memory. This is why we need to delete arrays with
delete [] ptr. It will call
destructor on every single position in the array and free them
up.
now we will make
a slight change from the String class to a Vector class (very, very
similar).
Vectors: (one-dimensional array of
numbers)
say you have an
array and call a function named void func()
void func()
{
p = new Vector[10]; // allocates memory
for array of 10 Vectors
}
This will call
constructor and points to the beginning of the array. now you can create
DMA for each instance of that array.
*p
at the end of
the function you will type
delete[] p;
This calls the
destructor on each Vector element of p then it will free up the
memory.
Operator
overloading for arrays:
1st - []
takes two
arguments
array to be indexed
(outside the brackets)
integer indicating number
of element to access)
in Vector class
you have…
2nd - double & operator
[](int)
in the .C file
you would have
double & Vector::operator [](int)
{
return data[i];
}
say that you
want to create a constant Vector v2…
Vector v1;
const Vector v2;
cout << v2[3];
v1[5] = -1; // won't work - problem with
l-value
if you change to
2nd way you could assign to v1[5] but now cout <<
v2[3] won't work because it's retreiving a reference which is an l-value.
So… we have to create another operator subscript.
double & operator [] (int
i); // can change
double operator [](int
i)const; // current instance will remain constant
the first is not
a constant instance but the second is. The second method overload is the
same as the first but is returning a double.
we can use the
first one to say v1[5] =
-1;
how does this
effect arrays of vectors?
Vector ar[7];
ar
[3][5];
This gets
element 5 out of an array of 3 Vectors. Not to be confused with C++
vectors so watch out!
Let's
implement: (in Vector.h)
#ifndef VECTOR_H
.
.
.
class Vector
{
int len
double *data;
public:
Vector();
.
.
double & operator [] (int i);
double operator [] (int i)const;
~Vector();
friend ostream & operator <<(ostream &,
const Vector &);
};
now in
Vector.C
Vector::Vector()
{
data = 0;
len = 0;
}
/*************************************/
Vector::Vector(const double *data, int size)
{
len = size;
data = new double[size];
for(int i=0; i<len; i++)
data[i] = d[i];
}
/*************************************/
Vector::~Vector()
{
delete [] data;
}/*****************************************/
ostream & operator << (ostream & ostr, const Vector
&v)
{
for(int i=0; i<v.len; i++)
ostr << v.data[i]
<< " ";
return ostr;
}
/******************************************/
double Vector:: operator [](int index)
{
return data[index];
}
Now the driver
program so far…
#include
<iostream>
#include
"Vector.h"
double data[] =
{…some numbers};
int
main()
{
Vector v1;
Vector v2(data, sizeof(data); // gives size of
(double)
cout << v2[2];
cout << "*" << v1 << "*" <<
v2;
return (0);
}
say you have a
function that takes a vector..
function (Vector
v)
{
}
in the main
()…
Vector
v1;
function
(v1);
it's not a
reference so it's passed by copy. In calling the function it sets up
memory for v including the value of the pointer so v points to the DMA memory of
v1
When it is done
with the function it calls destructor on v which kills memory allocated for v1.
Now v1 points to something that doesn't exist. How can we fix this
problem?? With the copy
constructor.
Copy
Constructor:
in class
definition..
Vector(const
Vector &); // what's different is the assignments
this is so that
everything gets copied in, including DMA issues. Implement it in the .C
file like this
Vector::Vector (const Vector &v)
//
const because you're not changing value passed in
{
len = v.len;
data = new double[len];
for (int i=0; i<len; i++)
data[i] =
v.data[i];
}
When is this
called?? Three different places.
in variable instance
creation when initializing from same type.
example:
Vector v1;
Vector v2 = v1;
copying variables of
arguments
example:
func(Vector v);
copying return
values
example:
Vector function ( …)
{
.
.
.
return result;
}
this will copy into temporary location then cleanup is done
then it will return value to variable. Most compilers will copy straight
into variable if they are smart enough.
now look at this
substitution…
Vector v1,
v2;
v1 =
v2;
what will
happen?? now v1 points to v2.
Problems:
float ar1[] = {3.0, 5.0, -1.2};
float ar2[] = {0.0, -7.5};
Vector v1 (ar1, 3);
Vector v2 (ar2, 2);
in memory would
look like…
|
v1 |
len = 3 |
à |
3.0 |
5.0 |
-1.2 |
when you execute
the statement…
v1 =
v2;
there are two
problems:
there is a memory
leak
you have changed the value
of where it points to.
|