Case Study
CS 201
This slide set covers pointers and
arrays in C++. You should read
Chapters 9 and 10 from your Deitel
& Deitel book.
IntArray class
● Let’s implement an IntArray class to represent an array of integers
● This class will have the following features:
○ The array knows its size
○ Array items do not have garbage values
○ It always handles allocation and deallocation (such that as a user, you do not need to worry
about explicitly using the new and delete operators)
○ It always deep copies the array items when an array’s copy is needed or the assignment
operator is applied on the array
○ The subscript operator has array bound checking for accessing the array items
2
IntArray definition
class IntArray {
public:
// If a class has dynamically allocated data members, it is highly recommended to
// re-implement the destructor and copy constructor, and overload the assignment
// operator (instead of using the ones provided by the compiler)
IntArray( const int = 0 ); // constructor with a default argument
IntArray( const IntArray& ); // copy constructor
~IntArray(); // destructor
IntArray& operator=( const IntArray& ); // overloaded assignment operator
int& operator[]( const int ); // overloaded subscript operator
private:
int size; // number of array items
int* data; // dynamically allocated array
};
3
IntArray implementation (constructor)
● Constructors are called when an // Constructor with a single integer
object is constructed (either by // parameter (this parameter has a
// default value set in the class
declaration or using the new operator) // definition such that it also serves
○ The storage class of an object determines // as a default constructor)
when it is constructed IntArray::IntArray( const int aSize ) {
if ( aSize <= 0) {
size = 0;
● It is possible to implement multiple data = nullptr;
constructors as long as their }
else {
signatures are different size = aSize;
○ The compiler selects which one to use data = new int[ size ];
based on its argument(s) for ( int i = 0; i < size; i++ )
data[ i ] = 0;
○ If an object is initialized with another object
}
of the same type at its construction, the }
compiler calls the copy constructor
4
IntArray implementation (copy constructor)
● Copy constructor is called when // This copy constructor deep copies the
○ An object is initialized with another of the // array items as opposed to default copy
same type at its declaration (construction) // constructor provided by the compiler
IntArray::IntArray( const IntArray& arr )
IntArray prev; : size( arr.size ) {
IntArray current( prev ); if ( size > 0 ) {
IntArray next = current; data = new int[ size ];
for ( int i = 0; i < size; i++ )
○ An object is passed by value as an data[ i ] = arr.data[ i ];
argument to a function (pass-by-value) }
else
○ An object is returned by value from a data = nullptr;
function (however, C++ Standard allows }
compilers to optimize this)
● If not provided explicitly, the compiler It must receive its argument by reference (not by
value). Otherwise, it results in infinite recursion.
provides a default copy constructor
Its argument should also be const to allow a
performing memberwise shallow copy
constant object to be copied and to be used only as
○ It does not deep copy the data members an rvalue inside the function.
5
IntArray implementation (destructor)
● Destructor is a special member IntArray::~IntArray() {
function that is called implicitly when if ( data )
an object is destructed (either when delete[] data;
}
its lifetime ends or when the delete
operator is used) // Constructor call for a single
○ Destructor calls are usually made in the // dynamically created object
reverse order of their corresponding IntArray* a1 = new IntArray( 400 );
constructor calls // Default constructor call for every
○ However, the storage class of objects may // object in the array
alter this order IntArray* a2 = new IntArray[ 5 ];
// Destructor call for the single object
● Each class should have only one delete a1; //
destructor (no overloading is allowed) // Every object in the array receives a
// destructor call. If "delete a2;" is
● If not provided explicitly, the compiler // used, only the first object receives
// a destructor call
creates an “empty” destructor delete[] a2; 6
Operator overloading
● For every class, the following operators are provided by the compiler
○ Assignment operator (=) → performs memberwise assignment between two objects
○ Address operator (&) → returns the address of an object
● Although they are provided by the compiler, these operators can also be
overloaded by the programmer
● Other operators can also be overloaded except . :: ?: sizeof
● Operator overloading should be done for a class individually
○ By defining a member function (in that class) for this operator
○ Where the name of this function should be operator <operator-to-be-overloaded>
7
IntArray implementation (assignment operator)
● It is called when the left operand is an object
● If not provided explicitly, the compiler provides a default assignment operator
that assigns each data member of the right object to the same data member
of the left object IntArray& IntArray::operator=( const IntArray& right ) {
if ( &right != this ) { // to avoid self-assignment
● However, this default if ( size != right.size ) {
assignment operator if ( size > 0 )
delete[] data;
performs shallow copy size = right.size;
for the memberwise if ( size > 0 )
data = new int[ size ];
assignments else
data = nullptr;
}
for ( int i = 0; i < size; i++ )
One can also define additional data[ i ] = right.data[ i ];
assignment operators where the }
right operand is of another data return *this; // to allow cascading
type }
8
this pointer
● Every object has access to its own address static member functions
● A member function can be
through a pointer called this
declared as static if it does
○ The this pointer is not a part of the object
not access any non-static
○ The compiler passes it as an implicit argument to
data member or call any
a non-static member function call of this object
non-static member function of
its class
● An object uses its this pointer
● static data members of a class
○ Implicitly when accessing its members directly
exist in memory and its static
○ Explicitly when using the this keyword
member functions can be called
even when there exist no object of
● The type of the this pointer depends on this class in memory
the object’s type and whether the executing ● A static member function
member function is declared as const does not have the this pointer
9
IntArray implementation (subscript operator)
● Other operators can also be int& IntArray::operator[]( const int ind ){
overloaded if ( ind < 0 || ind >= size )
throw out_of_range("Invalid index");
else
● This subscript operator facilitates return data[ ind ];
array bound checking for accessing }
the array items
// You can throw and catch exceptions also
// in C++, as in the following example
#include <exception>
int main(){
The return type of this function should be of a IntArray arr(100);
reference type since its returned value can be try {
used both as an lvalue and as an rvalue arr[130] = 20;
}
catch ( const exception& e ){
IntArray arr( 5 ); cout << e.what() << endl;
cout << arr[ 3 ]; // used as an rvalue }
return 0;
arr[ 3 ] = 10;// used as an lvalue
} 10
Example: Given the following Test class Test {
public:
class, what are the outputs of the Test( int i = 0 ){
following programs? id = i;
cout << "Constructor " << id << endl;
}
These examples are for you to better ~Test(){
cout << "Destructor " << id << endl;
understand when the constructor, }
copy constructor, destructor, and Test( const Test& o ){
id = o.id;
assignment operator are called. cout << "Copy const " << id << endl;
}
Test& operator=( const Test& right ){
id = right.id;
cout << "Assignment " << id << endl;
return *this;
}
int id;
};
11
Example: Given the Test class Test t1( 10 );
Test t2( 20 );
above, what are the outputs of this
void foo( bool flag ){
program? Test t3( 30 );
static Test t4( 40 );
Do not forget that the constructor, if ( flag ){
Test t5( 50 );
copy constructor, destructor, and Test t6( 60 );
assignment operator are called }
Test t7( 70 );
only for an object. For example, }
int main() {
they are not called for an object
cout << "checkpoint 1---" << endl;
pointer or a class data member Test t8( 80 );
(unless this data member is an cout << "checkpoint 2---" << endl;
foo( false );
object of another class).
cout << "checkpoint 3---" << endl;
foo( true );
cout << "checkpoint 4---" << endl;
return 0;
} 12
Example: Given the Test class int main() {
above, what are the outputs of this cout << "checkpoint 1---" << endl;
Test *b1;
program?
cout << "checkpoint 2---" << endl;
b1 = new Test ( 100 );
Do not forget that the constructor, delete b1;
cout << "checkpoint 3---" << endl;
copy constructor, destructor, and b1 = new Test [2];
assignment operator are called b1[0].id = 200;
b1[1].id = 300;
only for an object. For example, delete[] b1;
they are not called for an object cout << "checkpoint 4---" << endl;
b1 = new Test [2];
pointer or a class data member b1[0].id = 400;
(unless this data member is an b1[1].id = 500;
delete b1;
object of another class). cout << "checkpoint 5---" << endl;
return 0;
}
13
Example: Given the Test class void bar ( Test a, Test* b, Test& c ) {
// ...
above, what are the outputs of this }
int main() {
program?
cout << "checkpoint 1---" << endl;
Test t1(11);
Do not forget that the constructor, Test& t2 = t1;
Test t3 = t1;
copy constructor, destructor, and t3.id = 33;
assignment operator are called cout << "checkpoint 2---" << endl;
bar( t1, &t2, t3 );
only for an object. For example,
cout << "checkpoint 3---" << endl;
they are not called for an object Test* t4;
pointer or a class data member Test* t5;
t4 = &t1;
(unless this data member is an t5 = t4;
*t4 = t1;
object of another class).
cout << "checkpoint 4---" << endl;
bar( *t5, &t1, *t4 );
cout << "checkpoint 5---" << endl;
return 0;
} 14