0% found this document useful (0 votes)
37 views31 pages

Lec8. Pointers, Dynamic Arrays, Copy Constructor

The document discusses pointers and dynamic arrays in C++. It defines pointers and how they store memory addresses. It covers using pointers to dynamically allocate memory on the heap using new and delete. It also discusses how arrays are really pointer variables and can be treated similarly.

Uploaded by

Majd AL Kawaas
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PPTX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
37 views31 pages

Lec8. Pointers, Dynamic Arrays, Copy Constructor

The document discusses pointers and dynamic arrays in C++. It defines pointers and how they store memory addresses. It covers using pointers to dynamically allocate memory on the heap using new and delete. It also discusses how arrays are really pointer variables and can be treated similarly.

Uploaded by

Majd AL Kawaas
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PPTX, PDF, TXT or read online on Scribd
You are on page 1/ 31

Chapter 10

Pointers and
Dynamic Arrays

Copyright © 2017 Pearson Education, Ltd.


All rights reserved.
Pointer Introduction
• Pointer definition: memory address of a variable
• You’ve used pointers already: Call-by-reference parameters
• Pointers are "typed“ and declared like other types
– Can store pointer in variable, not int or double but a POINTER to int, double, etc
– Adding "*" before variable name produces "pointer to" that type
double *p1, v1;
int *p2, v2;

– p1 is declared a "pointer to double" variable


• can hold pointers to variables of type double only.
– p2 hold pointers to int variables
– v1 is ordinary double variable
– v2 is ordinary int variable

10-2
Pointing
int *p, v; 42
p = &v;
– Sets pointer variable p to "point to" int variable v
– p and v refer to same variable
• Operator & determines "address of" variable
• Read like: "p equals address of v“ or "p points to v “
• Two ways to refer to v now: v = 0;
p = &v;
– Variable v itself: cout << v; *p = 42;
cout << v << endl;
– Via pointer p: cout *p; cout << *p << endl;

• Dereference operator, * • Produces output:


42
– Pointer variable "derereferenced" 42
– Means: "Get data that p points to"

10-3
Pointer Assignments
• Pointer variables can be "assigned":
– Assigns one pointer to another
int *p1, *p2;
– "Make p1 point to where p2 points" p1 = p2;

• Do not confuse with: *p1 = *p2;


– Assigns "value pointed to" by p1, to "value pointed to" by p2

10-4
Memory Management
• Stack: special memory region
– stores temporary variables created by each function,
– Automatically managed by the OS/CPU.
– Every time a function declares a new variable, it is
"pushed" onto the stack.
– When a function exits, all variables that it pushed
onto the stack, are deleted.

• Heap: memory region reserved for dynamically-allocated variables


– Also called "freestore"
– is not managed automatically and is not as tightly managed by the CPU.
– To allocate memory on the heap, you must use new operator.
– you responsibility to free that memory once you don't need it any more
• you must use delete operator to destroy/free/desallocate memory
• If you fail to do this, your program will have what is known as a memory leak.
10-5
– Future "new" operations will fail if freestore is "full“
The new Operator
• Can dynamically allocate variables
– new creates dynamic variable and returns pointer to the new variable
• int *P1;
p1 = new int;
– Creates new "nameless" variable, and assigns p1 to "point to" it
– access content with *p1 and use just like ordinary variable
• If type is class type: MyClass *mcPtr;
mcPtr = new MyClass(32.0, 17);

– Constructor is called for new object


– Can invoke different constructor with initializer arguments
• Can still initialize non-class types:
int *n;
n = new int(17); //Initializes *n to 17

10-6
Basic Pointer Manipulations Example:
#include <iostream>
using namespace std;
int main( ){
int *p1, *p2;
p1 = new int;
*p1 = 42;
p2 = p1;
cout << "*p1 == " << *p1 << endl;
cout << "*p2 == " << *p2 << endl;
*p2 = 53;
cout << "*p1 == " << *p1 << endl;
cout << "*p2 == " << *p2 << endl;
p1 = new int;
*p1 = 88;
cout << "*p1 == " << *p1 << endl;
cout << "*p2 == " << *p2 << endl;
cout << "Hope you got the point of this example!\n";
return 0;
} *p1 == 42
*p2 == 42
*p1 == 53
*p2 == 53
*p1 == 88
*p2 == 53
10-7
Hope you got the point of this example!
Checking new Success
• Test if null returned by call to new:
– If new succeeded, program continues

int *p;
p = new int;
if (p == NULL) // NULL represents empty pointer
{
cout << "Error: Insufficient memory.\n";
exit(1);
}

– If new fails: Newer compilers terminate program automatically


• Produces error message

• Still good practice to use NULL check

10-8
delete Operator
• De-allocate dynamic memory using delete operator:
– When no longer needed int *p;
p = new int(5);
– Returns memory to freestore
… //Some processing…
– Literally "destroys" memory
delete p;
p = NULL;
• delete p; // Destroys dynamic memory
– But p still points there! : "dangling pointer"
– If p is then dereferenced ( *p ) is unpredicatable!
– Avoid dangling pointers by assign pointer to NULL after delete

• Dynamic variables: Created with new operator and destroyed with


delete while program runs (you have to maintain them)
• Local or automatic variables: Created when function is called and
destroyed when function call completes
10-9
Define Pointer Types
• Can "name" pointer types to be able to declare pointers
like other variables
– Eliminate need for "*" in pointer declaration
typedef int* IntPtr; //defines a "new type" alias

– Now these declarations are equivalent :


IntPtr p;
int *p;

Copyright © 2017 Pearson Education, Ltd. All rights reserved. 10-10


Call-by-value Pointers Example:
//Program to demonstrate the way call-by-value parameters
//behave with pointer arguments.
#include <iostream>
using namespace std;
typedef int* IntPointer;
void sneaky(IntPointer temp);
int main( ){
IntPointer p;
p = new int;
*p = 77;
cout << "Before call to function *p == " << *p << endl;
sneaky(p);
cout << "After call to function *p == "<< *p << endl;
return 0;
}
void sneaky(IntPointer temp) {
*temp = 99;
cout << "Inside function call *temp == "<< *temp << endl;
10-11
}
Array Variables and pointer variable
• Array variables: really pointer variables!
– arrays stored in memory addresses, sequentially
– Array variable "refers to" first indexed variable, so it is a pointer!
• array pointer is CONSTANT pointer!
• Can perform assignments but be careful
int a[10];
typedef int* IntPtr;
IntPtr p; //a and p are both pointer variables!
p = a; // Legal. p now points where a points:
// To first indexed variable of array a
a = p; // ILLEGAL! Array pointer is CONSTANT pointer!

• Function that Returns an Array


– Array type NOT allowed as return-type of function
– Example: int [] someFunction(); // ILLEGAL!

– Instead return pointer to array : int* someFunction(); // LEGAL!


10-12
Arrays and Pointer Variables
//Program to demonstrate that an array variable is a kind of pointer variable.
#include <iostream>
using namespace std;
typedef int* IntPtr;
int main( ){
IntPtr p;
int a[10];
int index;
for (index = 0; index < 10; index++)
a[index] = index;
p = a;
Note that changes
for (index = 0; index < 10; index++)
to the array p
cout << p[index] << " ";
are also changes
cout << endl; to the array a.
for (index = 0; index < 10; index++)
p[index] = p[index] + 1;
for (index = 0; index < 10; index++)
cout << a[index] << " ";
cout << endl;
return 0;
0123456789
}
1 2 3 4 5 6 7 8 9 10 10-13
Dynamic Arrays
• Standard array: Fixed size that is specified when it is declared
• Dynamic array : Size not specified at programming time but
determined while program running
– Created using new operator typedef double * DoublePtr;
– Treated like standard arrays DoublePtr d;
d = new double[10]; //Size in
brackets
… //Processing
delete [] d;
d = NULL;
• Deleting Dynamic Arrays: should be destroyed at run-time
– delete [] d de-allocates all memory for dynamic array
– Brackets indicate "array" is there
– Recall: d still points there!
• Should set d = NULL;

10-14
#include <iostream>
using namespace std;
typedef int* IntPtr;
Dynamic
void fillArray( int a[], int size);
int search( int a[], int size, int target);
Arrays
int main( ) {
cout << "This program searches a list of numbers.\n";
int arraySize;
cout << "How many numbers will be on the list? ";
cin >> arraySize;
IntPtr a; This program searches a list of numbers.
a = new int[arraySize]; How many numbers will be on the list? 5
fillArray(a, arraySize); Enter 5 integers.
int target; 12345
cout << "Enter a value to search for: "; Enter a value to search for: 3
cin >> target;
3 is element 2 in the array.
int location = search(a, arraySize, target);
if (location == -1) cout << target << " is not in the array.\n";
else cout << target << " is element " << location << " in the array.
n";
delete [] a;
return 0;
}
void fillArray( int a[], int size){
cout << "Enter " << size << " integers.\n";
for ( int index = 0; index < size; index++) cin >> a[index];
}
int search( int a[ ], int size, int target) {
int index = 0;
while ((a[index] != target) && (index < size)) index++;
if (index == size) index = -1; //if target is not in a. 10-15
return index;
Pointer Arithmetic
• Can perform arithmetic on pointers: "Address" arithmetic
typedef double* DoublePtr;
– d contains address of d[0] DoublePtr d;
– d + 1 evaluates to address of d[1] d = new double[10];
– d + 2 evaluates to address of d[2]
• Equates to "address" at these locations

• Alternative Array Manipulation


– "Step thru" array without indexing:
for (int i = 0; i < arraySize; i++)
cout << *(d + i) << " " ;
– Equivalent to:
for (int i = 0; i < arraySize; i++)
cout << d[i] << " " ;

– Only addition/subtraction on pointers : No multiplication, division


– Can use ++ and -- on pointers
10-16
Back to Classes and this pointer
• The -> operator : shorthand notation
MyClass *p;
p = new MyClass;
p->grade = "A"; //Equivalent to: (*p).grade = "A";
– Combines dereference operator, *, and dot operator
– Specifies member of class "pointed to“ by given pointer

• Member function definitions might need to refer to calling object


– Use predefined this pointer which automatically points to calling object:
Class MyClass{
public:
void showStuff() const;
private:
int stuff;
};
• Two ways for member functions to access:
cout << stuff; or cout << this->stuff;
10-17
Overloading Assignment Operator
• Assignment operator returns reference
– So assignment "chains" are possible
– e.g., a = b = c;
• Sets a and b equal to c

• Operator must return "same type" as it’s left-hand side


– To allow chains to work
– The this pointer will help with this!
• Assignment operator when overloaded must be member of the class
– It has one parameter
– Left-operand is calling object
s1 = s2;
• Think of like: s1.=(s2);
• s1 = s2 = s3;
– Requires (s1 = s2) = s3;
– So (s1 = s2) must return object of s1"s type
• And pass to " = s3";
10-18
//Objects of this class are partially filled arrays of doubles.
class PFArrayD {
public: Parray.h
PFArrayD( );
//Initializes with a capacity of 50.
PFArrayD( int capacityValue);
PFArrayD( const PFArrayD& pfaObject); //copy constructor
void addElement( double element);
//Precondition: The array is not full.
//Postcondition: The element has been added.
bool full( ) const { return (capacity == used); }
//Returns true if the array is full, false otherwise.
int getCapacity( ) const { return capacity; }
int getNumberUsed( ) const { return used; }
void emptyArray( ){ used = 0; }//Empties the array.
double& get( int index); //get reference of a [index]
//double& operator[]( int index); // replace get by overload []
//Read and change access to elements 0 through numberUsed - 1.
PFArrayD& operator =( const PFArrayD& rightSide);
~PFArrayD( );
private:
double *a; //For an array of doubles
int capacity; //For the size of the array
int used; //For the number of array positions currently in use
10-19
};
#include <iostream>
using namespace std;
#include "parray.h" Parray.cpp
PFArrayD::PFArrayD( ) :capacity(50), used(0){
a = new double[capacity];
}

PFArrayD::PFArrayD( int size) :capacity(size), used(0){


a = new double[capacity];
}

PFArrayD::PFArrayD( const PFArrayD& pfaObject)


:capacity(pfaObject.getCapacity( )), used(pfaObject.getNumberUsed( )){
a = new double[capacity];
for ( int i = 0; i < used; i++)
a[i] = pfaObject.a[i];
}
void PFArrayD::addElement( double element){
if (used >= capacity){
cout << "Attempt to exceed capacity in PFArrayD.\n";
exit(0);
}
a[used] = element;
used++;
}
10-20
double& PFArrayD::get( int index){
if (index >= used){
cout << "Illegal index in PFArrayD.\n"; Parray.cpp
exit(0);
}
return a[index];
}

PFArrayD& PFArrayD::operator =( const PFArrayD& rightSide){


if (capacity != rightSide.capacity){ //make sure left array capacity
delete [] a; // is same as right
a = new double[rightSide.capacity];
}
capacity = rightSide.capacity;
used = rightSide.used;
for ( int i = 0; i < used; i++)
a[i] = rightSide.a[i];
return * this; //this refers to the invoking object => left operand
}

PFArrayD::~PFArrayD( ){
delete [] a;
}

10-21
#include <iostream>
using namespace std;
#include "parray.h" parrayDemo.cpp
void testPFArrayD( );
//Conducts one test of the class PFArrayD.
int main( )
{
cout << "This program tests the class PFArrayD.\n";
char ans;
do
{
testPFArrayD( );
cout << "Test again? (y/n) ";
cin >> ans;
} while ((ans == 'y') || (ans == 'Y'));
return 0;
}

To compile : g++ .\parray.cpp .\parraydemo.cpp -o myprogram


Or
g++ -c .\parray.cpp
g++ -c .\parraydemo.cpp
g++ .\parray.o .\parraydemo.o
10-22
void testPFArrayD( ){
int cap; parrayDemo.cpp
cout << "Enter capacity of this super array: ";
cin >> cap;
PFArrayD temp(cap);
cout << "Enter up to " << cap << " nonnegative numbers.\n";
cout << "Place a negative number at the end.\n";
double next;
cin >> next;
while ((next >= 0) && (!temp.full( ))){
temp.addElement(next);
cin >> next; This program tests the class PFArrayD.
} Enter capacity of this super array: 10
cout << "You entered the following " Enter up to 10 nonnegative numbers.
<< temp.getNumberUsed( ) Place a negative number at the end.
<< " numbers:\n"; 1.1
int index; 2.2
int count = temp.getNumberUsed( ); 3.3
4.4
for (index = 0; index < count; index++)
-1
cout << temp.get(index)<< " ";
You entered the following 4 numbers:
// cout << temp[index] << " ";
1.1 2.2 3.3 4.4
cout << endl; (plus a sentinel value.)
cout << "(plus a sentinel value.)\n"; Test again? (y/n) n
}
10-23
Overloaded [] Operator Definition
for (index = 0; index < count; index++)
cout << temp.get(index)<< " ";
// cout << temp[index] << " ";

double& get( int index);


// double& operator[]( int index);
double& PFArrayD::get( int index){
if (index >= used){
cout << "Illegal index in PFArrayD.\n";
exit(0);
}
return a[index];
}

// double& PFArrayD::operator[]( int index){


// if (index >= used){
// cout << "Illegal index in PFArrayD.\n";
// exit(0);
// }
// return a[index];
// } 10-24
Destructor
• Dynamically-allocated variables do not go away until
"deleted"
• If pointers are only private member data
– They dynamically allocate "real" data in the constructor
– Must have means to "deallocate" when object is destroyed
• Destructor: Opposite of constructor
– Automatically called when object is out-of-scope
– Default version only removes ordinary variables, not dynamic
variables
– Defined like constructor, just add ~

MyClass::~MyClass() PFArrayD::~PFArrayD( ){
{ delete [] a;
//Perform delete clean-up duties }
}
10-25
Copy Constructors
• constructor that has one parameter of the same type as the class
– parameter must be a call-by-reference parameter normally preceded const
PFArrayD( const PFArrayD& obj);

PFArrayD b(20);
for ( int i = 0; i < 20; i++)
b.addElement(i);
PFArrayD temp(b); //Initialized by the copy constructor
PFArrayD::PFArrayD( int size) :capacity(size), used(0){
a = new double[capacity];
}

PFArrayD::PFArrayD( const PFArrayD& obj)


:capacity(obj.getCapacity( )), used(obj.getNumberUsed( )){
a = new double[capacity];
for ( int i = 0; i < used; i++)
a[i] = obj.a[i];
}

10-26
Copy Constructors
• Automatically called when:
1. Class object declared and initialized to other object
2. When function returns class type object
3. When argument of class type is "plugged in“ as actual argument to call-by-
value parameter
• Requires "temporary copy" of object
– Copy constructor creates it
• Default copy constructor
– Like default "=", performs member-wise copy
• Pointers  write own copy constructor!

10-27
Shallow and Deep Copies
• Shallow copy
– Assignment copies only member variable contents over
– Default assignment and copy constructors
– Works fine if there are no pointers or dynamically allocated data
involved
• Deep copy
– Pointers, dynamic memory involved
– Must dereference pointer variables to "get to" data for copying
– Write your own assignment overload and copy constructor in this
case!

10-28
For you to think of them
int i = 3;
int* p = i; // invalid conversion from int to int*

int i = 3;
int* p = &i;

int i = 3;
int* p = &i;
int* q = p;

int i = 3;
int* p = &i;
int** q = &p;
For you to think of them

int* p = new int;


*p = 3;

int& q = *p;
q = 4;

cout << *p << endl;

int* r = &q;
*r = 5;

cout << *p << endl;

int*& s = p;
*s = 6;

cout << *p << endl

Copyright © 2017 Pearson Education, Ltd. All rights reserved. 10-30


how a local variable s of type std::string might look when stored in memory

Copyright © 2017 Pearson Education, Ltd. All rights reserved. 10-31

You might also like