Class miniVector (continued)

advertisement
687286748
-1-
Chapter 5
Pointers and Dynamic Memory
Assignment and Initialization
The miniVector Class
The Matrix Class
687286748
-2-
Pointers and Dynamic Memory
The memory allocation operator new - C++ uses the operator new to
allocate memory for data during program execution. If sufficient memory is
not available the operator returns 0 (NULL). The operator new requires a
data type T as a parameter, memory for the variable of type T is allocated,
and the address of the newly created memory is returned.
T *p;
p = new T;
int
*ptr1; // size of int is 2 - address of an int is assigned to ptr1
long *ptr2; // size of long is 4 - address if a long is assigned to ptr2
ptr1 = new int;
// pointer1 points to an integer
ptr2 = new long; // pointer2 points to a long
The contents of the allocated memory has no initial value. Initial value can
be supplied as a parameter;
P = new T ( value ) ;
ptr2 = new long(100000);
Dynamic Array Allocation
p = new T [n] ; // allocates an array of n items of type T
long *p;
p = new long [5] ; // Allocates an array of 5 longs
if ( p == NULL )
{
cerr << “ Memory allocation error! << endl;
exit (1);
}
The Memory Deallocation Operator delete
long *p;
p = new long [50 ];
delete [ ] p; // deallocates 50 long integers
int *p = new(10);
delete p;
// deallocates one integer
687286748
-3-
Dynamically Allocated Objects
Essential actions of member functions in handling dynamically allocated
objects:
#include <iostream.h>
template <class T>
class DynamicClass
{
private:
// variable of type T and a pointer to data of type T
T
member1;
T
*member2;
public:
// constructors
DynamicClass ( const T& m1, const T& m2 );
DynamicClass ( const DynamicClass<T>& obj);
// destructor
~DynamicClass ( void );
// assignment operator
DynamicClass<T>& operator= ( const DynamicClass<T>& rhs );
};
// constructor with parameters to initialize member data
template <class T>
DynamicClass<T>::DynamicClass(const T& m1, const T& m2 )
{
member1 = m1;
member2 = new T (m2 );
cout << “constructor: “ << member1 << ‘/’ << *member2 << endl;
}
687286748
-4-
Dynamically Allocated Objects
Ex:
DynamicClass<int>
staticObj ( 1, 100 ); // DynamicClass object
DynamicClass<int>
*dynamicObj;
// pointer variable
dynamicObj = new DynamicClass<int> ( 2, 200);// allocate object
// and init data
// using constructor
Deallocating Object Data: the Destructor
The destructor is called whenever the object is deleted - when a program
terminates ( for global objects ) or when a program exits a block ( for local
objects) If the destructor does not call delete the object may be destroyed
but the memory is not freed up.
template <class T>
DynamicClass<T> : : ~DynamicClass(void)
{
cout << “Destructor: “ << member1 << ‘/’ << *member2 << endl;
delete member2; // deallocates memory
}
687286748
-5-
More than one dynamic object
#include <iostream.h>
#include “dynamic.h”
void DestroyDemo( int m1, int m2 )
{
DynamicClass<int> obj( m1, m2 );
// upon return Objects are
// destroyed memory is not
// deallocated without a
// destructor
}
void main ( void )
{
DynamicClass<int> Obj_1 ( 1, 100 );
DynamicClass<int> *Obj_2;
// create automatic object
// Declare Pointer to an object
// Allocate dynamic object and initialize members
Obj_2 = new DynamicClass<int> ( 2, 200 );
// Call function DestroyDemo passing parameters
DestroyDemo ( 3, 300 );
// Explicitly delete Obj2_2
delete Obj_2;
cout << ready to exit program.” << endl;
}
/* Run of program */
Constructor: 1/100
Constructor: 2/200
Constructor: 3/300
Destructor: 3/300
Destructor: 2/200
Ready to exit program.
Destructor: 1/100
687286748
-6-
Assignment and Initialization
Assignment IssuesDynamicClass<int> objA( 1,2 ), objB(3,4);
Before Assignment:
objA
Member1=1
objB
Member2
Member1=3
Member2
4
2
Heap memory
*objA.member2 =2
Heap memory
*objB.member2 = 4
objA = objB ; // causes a byte by byte assignment of object B to object A
After Assignment:
objA
member1=1
objB
member2
2
Unreferenced Heap memory
member1=3
member2
4
Heap memory - now
referenced by two objects
because pointers are assigned - and both objects member2 pointer variable
points to the same memory location. When returning from the function -
687286748
-7-
deallocating memory by the destructor could be a fatal.
To solve the problem what is required is an overloaded assignment operator.
After Assignment using overloaded assignment operator
The assignemt
objA = objB;
objA
member1=1
objB
member2
member1=1
4
member2
4
Heap memory
Heap memory
Copy value *objA.member3 to *objB.member2
The Overloading the Assignment Operator properly handles object
Assignment.
Syntax for overloaded assignment operator metnod:
DynamicClass<T>& operator= ( const DynamicClass<T>& rhs );
A = B;
// implemented as A.operator = (B)
The overloaded operator explicitly assigns all data including private and
public data members as well as the data pointed to by these members.
The right hand side is passed as a reference variable - ( const - prevents
alterations )
687286748
-8-
Implementation:
template <class T>
DynamicClass<T>& DynamicClass<T> ::operator=
( const DynamicClass<T>& rhs )
{
// copy static data member from rhs to the current object
member1 = rhs.member1;
*member2 = *rhs.member2;
cout << “Assignment Operator: “ << member 1 << ‘/’
cout *member2 << endl;
return *this; // returns reference to current object - allows
// chaining of two or more assignment statements
// C = B = A; assignment right to left
}
The This pointer
- Each C++ object has a pointer named this
- the identifier this is a reserved word and can only be used inside a class
member function.
- this point to the current object, *this is the object itself
( this->member1, the data value of member1 ) or (*this).member1
The assignment operator returns *this. The return value is a reference
argument – this allows for chaining= For example:
A=B=C; //is assigned from right to left
687286748
-9-
Initialization Issues:
Object initialization creates a new object that is a copy of another object.
DynamicClass<int> objA ( 3, 5 ) , objB = objA; // Instantiate objB and
// Initialize object objB
// with object objA
Initialization also occurs when:
- passing an object as a value parameter
- when returning an object as a value of a function
to properly handle dynamic memory C++ provides a copy constructor.
Creating a copy constructor
DynamicClass ( const DynamicClass<T>& X );
// Declaration
template <class T>
DynamicClass<T>:: DynamicClass ( const DynamicClass<T>& obj )
{
member1 = obj.member1;
// copy static member
// allocate dynamic memory and initialize it
member2 = new T (*obj.member2 );
cout << “ Copy Constructor: “ << member1 << ‘/’
<< *member2 << endl;
}
687286748
- 10 -
Using DynamicClass
#include <iostream.h>
#include “dynamic.h”
template <class T>
DynamicClass<int> Demo ( DynamicClass<T> one,
DynamicClass<T>& two,
T m
)
{
DynamicClass<T> obj (m, m);
// pass by value
// passed by ref
// pass by value
// calls constructor
// member1 = m, *member2 = m
return obj ; // a copy of obj is made and returned
// ALL local (stack) variables are destroyed
}
void main (void )
{
DynamicClass<int> A ( 3, 5 ), B = A, C ( 0, 0 );
C = Demo ( A, B, 5 );
// assignment operator
// all remaining objects are destroyed upon program exit
}
/* Run of program */
Constructor: 3/5
Copy constructor:3/5
Constructor : 0/0
Copy Constructor: 3/5
Constructor: 5/5
Copy constructor: 5/5
Destructor: 5/5
Destructor: 3/5
Assignment Operator: 5/5
Destructor: 5/5
Destructor: 5/5
Destructor: 3/5
Destructor: 3/5
// A(3,5)
// B = A
// C (0,0)
// Demo ( A ,
// in member function Demo obj(5,5)
// upon return from Demo
// Delete dynamic data
// Delete dynamic data
// C = Demo (...) assignment operator
// Destructor on exit
// Destructor on exit
// Destructor on exit
// Destructor on exit
687286748
- 11 -
Overloading the Index Operator
T& operator[] (int i);
// provides general access to elements using an index.
// Precondition: 0 <= i < vSize. if the index is out
// of range, throws the indexRangeError exception
// provides general access to array elements. constant version
template <typename T>
const T& miniVector<T>::operator[] (int i) const
{
if (i < 0 || i >= vSize)
throw indexRangeError(
"miniVector: index range error", i, vSize);
return vArr[i];
}
687286748
- 12 -
// File: prg5_3.cpp
// the program illustrates the use of exceptions with the miniVector
// class. during execution, the underflowError and indexRangeError
// exceptions each occur once
#include <iostream>
#include "d_vector.h"
using namespace std;
int main()
{
miniVector<int> v;
// try block attempts to erase from empty vector; catch block
// catches underflowError exception from pop_back()
try
{
v.pop_back();
}
catch (const underflowError& e)
{
cout << e.what() << endl;
// store element in v[0]
v.push_back(99);
}
cout << "The size of v = " << v.size() << endl;
// try block enables index bound checking ; catch block catches
// indexRangeError exception from operator[]
try
{
cout << "v[0] = " << v[0] << endl;
cout << "v[1] = " << v[1] << endl;
}
catch (const indexRangeError& e)
{
cout << e.what() << endl;
}
return 0;
}
/*
Run:
miniVector pop_back(): vector is empty
The size of v = 1
v[0] = 99
miniVector: index range error index 1 size = 1
*/
687286748
- 13 -
Part of Exception Class
#ifndef EXCEPTION_CLASSES
#define EXCEPTION_CLASSES
#include <strstream>
#include <string>
using namespace std;
class baseException
{
public:
baseException(const string& str = ""): msgString(str)
{
if (msgString == "")
msgString = "Unspecified exception";
}
string what() const { return msgString; }
// protected allows a derived class to access msgString.
// chapter 13 discusses protected in detail
protected:
string msgString;
};
.......
// index out of range
class indexRangeError: public baseException
{
public:
indexRangeError(const string& msg, int i, int size): baseException()
{
char indexString[80];
ostrstream indexErr(indexString, 80);
indexErr << msg << " index " << i << " size = " << size << ends;
// indexRangeError can modify msgString, since it is in
// the protected section of baseException
msgString = indexString;
}
};
// attempt to erase from an empty container
class underflowError: public baseException
{
public:
underflowError(const string& msg = ""):
baseException(msg)
{}
};
. . . . . . .
};
#endif
// EXCEPTION_CLASSES
687286748
- 14 -
Class miniVector
#ifndef MINI_VECTOR
#define MINI_VECTOR
#include "d_except.h"
// include exception classes
using namespace std;
template <typename T>
class miniVector
{
public:
miniVector(int size = 0);
// constructor.
// Postconditions: allocates array with size number of elements
// and capacity. elements are initialized to T(), the default
// value for type T
miniVector(const miniVector<T>& obj);
// copy constructor
// Postcondition: creates current vector as a copy of obj
~miniVector();
// destructor
// Postcondition: the dynamic array is destroyed
miniVector& operator= (const miniVector<T>& rhs);
// assignment operator.
// Postcondition: current vector holds the same data
// as rhs
T& back();
// return the element at the rear of the vector.
// Precondition: the vector is not empty. if vector
// is empty, throws the underflowError exception
const T& back() const;
// const version used when miniVector object is a constant
T& operator[] (int i);
// provides general access to elements using an index.
// Precondition: 0 <= i < vSize. if the index is out
// of range, throws the indexRangeError exception
const T& operator[] (int i) const;
// const version used when miniVector object is a constant
void push_back(const T& item);
// insert item at the rear of the vector.
// Postcondition: the vector size is increased by 1
687286748
- 15 -
Class miniVector (continued)
void pop_back();
// remove element at the rear of the vector.
// Precondition: vector is not empty. if the vector is
// empty, throws the underflowError exception
int size() const;
// return current list size
bool empty() const;
// return true if vector is empty and false otherwise
int capacity() const;
// return the current capacity of the vector
private:
int vCapacity;
int vSize;
T *vArr;
// amount of available space
// number of elements in the list
// the dynamic array
void reserve(int n, bool copy);
// called by public functions only if n > vCapacity. expands
// the vector capacity to n elements, copies the existing
// elements to the new space if copy == true, and deletes
// the old dynamic array. throws the memoryAllocationError
// exception if memory allocation fails
};
687286748
- 16 -
Implementation miniVector
// set the capacity to n elements
template <typename T>
void miniVector<T>::reserve(int n, bool copy)
{
T *newArr;
int i;
// allocate a new dynamic array with n elements
newArr = new T[n];
if (newArr == NULL)
throw memoryAllocationError(
"miniVector reserve(): memory allocation failure");
// if copy is true, copy elements from the old list to the new list
if (copy)
for(i = 0; i < vSize; i++)
newArr[i] = vArr[i];
// delete original dynamic array. if vArr is NULL, the vector was
// originally empty and there is no memory to delete
if (vArr != NULL)
delete [] vArr;
// set vArr to the value newArr. update vCapacity
vArr = newArr;
vCapacity = n;
}
// constructor. initialize vSize and vCapacity.
// allocate a dynamic array of vSize integers
// and initialize the array with T()
template <typename T>
miniVector<T>::miniVector(int size):
vSize(0), vCapacity(0), vArr(NULL)
{
int i;
// if size is 0, vSize/vCapacity are 0 and vArr is NULL.
// just return
if (size == 0)
return;
// set capacity to size. since we are building the vector,
// copy is false
reserve(size, false);
// assign size to vSize
vSize = size;
// copy T() into each vector element
for (i=0;i < vSize;i++)
vArr[i] = T();
}
687286748
- 17 -
Implementation miniVector (continued)
// copy constructor. make the current object a copy of obj.
// for starters, use initialization list to create an empty vector
template <typename T>
miniVector<T>::miniVector (const miniVector<T>& obj):
vSize(0), vCapacity(0), vArr(NULL)
{
int i;
// if size is 0, vSize/vCapacity are 0 and vArr is NULL.
// just return
if (obj.vSize == 0)
return;
// set capacity to obj.vSize. since we are building the vector,
// copy is false
reserve(obj.vSize, false);
// assign size to obj.vSize
vSize = obj.vSize;
// copy items from the obj.vArr to the newly allocated array
for (i = 0; i < vSize; i++)
vArr[i] = obj.vArr[i];
}
// destructor. deallocate the dynamic array
template <typename T>
miniVector<T>::~miniVector()
{
if (vArr != NULL)
// de-allocate memory for the array
delete [] vArr;
}
// replace existing object (left-hand operand) by rhs (right-hand operand)
template <typename T>
miniVector<T>& miniVector<T>::operator= (const miniVector<T>& rhs)
{
int i;
// check vCapacity to see if a new array must be allocated
if (vCapacity < rhs.vSize)
// make capacity of current object the size of rhs. don't
// do a copy, since we will replace the old values
reserve(rhs.vSize, false);
// assign current object to have same size as rhs
vSize = rhs.vSize;
// copy items from rhs.vArr to vArr
for (i = 0; i < vSize; i++)
vArr[i] = rhs.vArr[i];
return *this;
}
687286748
- 18 -
Implementation miniVector (continued)
// check vSize and throw an underflowError exception if the
// value is 0; otherwise return the element vArr[vSize-1]
template <typename T>
T& miniVector<T>::back()
{
if (vSize == 0)
throw underflowError(
"miniVector back(): vector empty");
return vArr[vSize-1];
}
template <typename T>
const T& miniVector<T>::back() const
{
if (vSize == 0)
throw underflowError(
"miniVector back(): vector empty");
return vArr[vSize-1];
}
// provides general access to array elements
template <typename T>
T& miniVector<T>::operator[] (int i)
{
if (i < 0 || i >= vSize)
throw indexRangeError(
"miniVector: index range error", i, vSize);
return vArr[i];
}
// provides general access to array elements. constant version
template <typename T>
const T& miniVector<T>::operator[] (int i) const
{
if (i < 0 || i >= vSize)
throw indexRangeError(
"miniVector: index range error", i, vSize);
return vArr[i];
}
687286748
- 19 -
Implementation miniVector (continued )
// insure that list has sufficient capacity, add the new item to the list, and increment vSize
template <typename T>
void miniVector<T>::push_back(const T& item)
{
// if space is full, allocate more capacity
if (vSize == vCapacity)
{
if (vCapacity == 0)
// if capacity is 0, set capacity to 1.
// set copy to false because there are
// no existing elements
reserve(1,false);
else
// double the capacity
reserve(2 * vCapacity, true);
}
// add item to the list, update vSize
vArr[vSize] = item;
vSize++;
}
// if not empty, just decrement the size
template <typename T>
void miniVector<T>::pop_back()
{
if (vSize == 0)
throw underflowError(
"miniVector pop_back(): vector is empty");
vSize--;
}
template <typename T>
int miniVector<T>::size() const
{
return vSize;
}
template <typename T>
bool miniVector<T>::empty() const
{
return vSize == 0;
}
template <typename T>
int miniVector<T>:: capacity() const
{
return vCapacity;
}
#endif // MINI_VECTOR
Download