Pointers
- Memory address of a variable
1
2
| int *p1, *p2, v1, v2;
p1 = &v1; // sets pointer variable p1 "point to" int variable v1
|
- p1 → v1
- “p1 equals the address of v1”
- “p1 points to v1”
- ‘Address of’ Operator
& : Determines address of a variable - Dereference operator
* : pointer variable dereferenced. - “Get data that p1 points to”
1
2
3
| int *p1, *p2;
p2 = p1; // makes p2 point to where p1 points
*p2 = *p1; //"value pointed to" by p1, to "value pointed to" by p2
|
The new operator
- NO real need to have a standard identifier.
- Can dynamically allocate variables with
new p1 = new int; : Creates new nameless variable, assigns p1 to point to it.- Can access with
*p1 - Creates a new dynamic variable and returns pointer to the new variable
- If type is a class type : constructor is called, can invoke different constructor with initializer arguments
- Also can initialize non-class types
1
2
3
4
| MyClass *mcPtr;
mcPtr = new MyClass(32.0, 17);
int *n;
n = new int(17); // same as n= new int; *n=17;
|
- Pointers are full-fledged type : can be any function parameter, returned from function
int* findOtherPointer(int* p);- Has “pointer to an int” parameter
- Returns “pointer to an int” variable
Basic Memory Management
- Heap
- Also called ‘freestore’ (from top)
- Reserved for dynamically-allocated variables
- All new dynamic variables consume memory in heap
- ‘new’ operation will fail if heap is full : NULL is assigned to the pointer
- To check
new operation success
1
2
3
4
5
6
7
| int *p;
p = new int;
if (p==NULL)
{
cout<< "Error";
exit(1);
} //Old compilers
|
- For newer compilers, if
new operation fails, perogram terminates automatically w/ error message
NULL, nullptr, delete
NULL : Identical to 0nullptr : resolves the ambiguity problem of NULL (can only be assigned to a pointer)- De-allocate dynamic memory
- When the dynamic memory is no loner needed, returns memory to freestore(heap)
1
2
3
4
| int *p;
p=new int(5);
...// Do something...
delete p;
|
delete p; : Destroys dynamic memory but still p points there.- to avoid “dangling pointer problem”, assign pointer NULL or nullptr after delete
Comparison btw Dynamic and Automatic(Local) Variables
- Dynamic variables
- Created with
new operator - Created and destroyed while program runs
- Local variables
- Declared within a function definition
- Not dynamic : Created when th function is called, Destroyed when the function call completes
- Often called automatic variables
- Global variabels
- Variables declared outside any function/class definition
- Sometimes called as statically allocated variables
1
2
3
4
| int* p;
p = new int(5);
//p : Created with new, dynamic, stored in heap
//*p : Created locally, stored in stack
|
Defining Pointer Types
- Can “name” pointer types
typedef int* IntPtr; : make int* and IntPtr equivalent- It avoids of mistake of omitting
*
1
2
| int *p1, p2; // p2 is not pointer
IntPtr p1, p2 //p1, p2 are both pointers
|
- No Complication for call-by-reference for a pointer type
1
2
| void sampleFunction(IntPtr& pointerVariable);
void sampleFunction(int*& pointerVariable); //SAME!
|
Pitfall : Call-by-value pointer parameter
1
2
3
4
5
6
7
8
| void sneaky(int* temp){
*temp=99;
}
int main() {
int *p = new int(77);
sneaky(p);
cout<<*p<<endl; //99
}
|
- Even though the function parameter is call-by-value, it actually seems to be call-by-reference in perspective of the value.
- Temporally makes pointer that points the value that original parameter points to, change the value, destroys the temp parameter
- Comparing Call-by reference and pointers
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| void swap(int &v1, int& v2){
int temp;
temp = v1;
v1=v2;
v2=temp;
}
void swap2(int *v1, int *v2) {
int temp;
temp = *v1;
*v1 = *v2;
*v2 = temp;
}
int main() {
int a(1), b(2);
swap(a, b);
swap2(&a, &b);
}
|
Dynamic Arrays
- Array variables are really pointer variables
- For dynamic array, size is not specified at programming time.
1
2
3
4
5
| int v[10];
int *p;
p=v; //legal
v=p; //illegal (array are constant pointer)
cout<<p[4]; //same as cout<<v[4];
|
- Actually array variable
int v[10] is const int* type. - The variable v must point there always
Dynamic arrays
- assigned and deleted with
new and delete
1
2
3
4
| double *d;
d = new double[10];
delete[] d; //delete dynamic array
d=nullptr;
|
- Size can be an integer-valued expression. (no need to be a constant as in the standard array
- Array type is NOT allowed as a return-type of a function
1
2
| int[] someFunction(); //illegal
int* someFunction(); //legal
|
- Pointer Arithmetic : can perform arithmetic on pointers
1
2
3
| double* d;
d = new double[10];
cout<<*(d+1); //d[1]
|
- d : address of d[0], d+1 : address of d[1] and so on…
- Only use addition/subtraction on pointers, can use
++, -- as well
Multidimensional Dynamic Arrays
1
2
3
4
5
6
7
8
9
10
11
| typedef int* IntArrayPtr;
IntArrayPtr *m = new IntArrayPtr[3];
// Creates an array of three pointers
for (int i = 0; i<3;i++){
m[i] = new int[4];
}
// results in 3 x 4 dynamic array
for (int i = 0;i<3;i++){
delete[] m[i];
}
delete[] m;
|
Classes, Pointers and Dynamic Arrays
-> operator
- Shorthand notation that combines dereference operator,
* - Specifies member of class “pointed to” by given pointer
1
2
3
4
5
6
7
8
9
10
| struct Record
{
int number;
char grade;
}
Record *p;
p = new Record;
p->number = 2001; //*p.number = 2001;
p->grade = 'A'; //*p.grade='A';
//equivalent
|
this operator
- function definitions might need to refer to calling object
1
2
3
4
5
6
7
8
9
10
11
12
| class Sample {
public:
void showStuff() const;
private:
int stuff;
};
void Sample::showStuff() const {
cout<<stuff;
}
void Sample::showStuff() const {
cout<< this->stuff; //equivalent with the function above
}
|
Assignment Operator
- Assignment operator returns reference
- Assignment ‘chains’ are possible (
a=b=c;) - Can call
(a=b).f()
- Operator must return same type as its left-hand side.
- This makes operators to allow chains to work
- Cannot be overloaded via friend function (to not modify a constant)
s1=s2=s3; : Requires to be right associative
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| class StringClass {
public:
StringClass& operator=(const StringClass& rtSide);
private:
char *a; //Dynamic array for char
int capacity; // size of dynamic array
int length;//number of characters in a
};
StringClass& operator=(const StringClass& rtSide) {
if (this == &rtSide){
return *this;
} // cares when s=s
else {
capacity = rtSide.capacity;
length = rtSide.length;
delete [] a;
a = new char[capacity];
for (int i=0;i<length;i++){
a[i]=rtSide.a[i];
}
return *this;
}
}
|
The Big Three
- the
= assignment operator, the copy constructor, the destructor.. they go together - Why does the
= operator overloading necessary for PFArrayD (Partially filled array double) ? - If we don’t implement
= operator overloading, it uses default = operator (elementwise =)
1
2
| PFArrayD list1(10), list2(20);
list1=list2; //will make list1.a=list2.a;
|
- If
list1.a changed then list2.a is also changed. - We usually wand a completely independent copy.
Destructor
- Dynamically-allocated variables don’t go away until deleted
- When the object is deleted, the dynamically allocatd variables are not automatically freed from heap
- Default version only removes ordinary variables, not dynamic variables
1
2
3
4
| Myclass::~Myclass()
{
delete[] a;
}
|
Shallow and Deep Copies
- Shallow copy
- Assignment copies only member variable contents
- Default assignment and default copy constructors use shallow copy
- Deep copy
- Pointers, dynamic memory involved
- Must dereference pointer variables to “get to” data for copying
- Write your own assignment operator and copy constructor in this case
- Problems of shallow copy : without care, it copies the pointer variable not real variable.
Copy constructors
- A constructor that has one paramter with same type of the class (usually call-by-reference, const)
- Three main usages
- When a class object is being declared & initialized by another object of the same type
1
2
3
4
5
6
7
| PFArrayD b(20);
for (int i =0;i<20;i++)
b.addElement(i);
PFArrayD temp(b);
for (int i =0;i<20;i++)
temp.addElement(i+1);
// changing values of temp does not affect b anymore
|
- When an argument of the class type is “plugged in” for a call-by-value paramter
1
2
3
4
5
6
7
8
9
10
11
| void showPFArrayD(PFArrayD parameter) {
cout<< parameter[0];
}
//this destroys the dynamically allocated memory
//makes original object's pointer dangling
PFArray sample(2);
sample.addElement(5.5);
sample.addElement(6.6);
showPFArrayD(sample); //this destroys the pointer
cout<<sample[0]; //error
//It happens when the destructer is properly coded
|
Also causes the “double free” problem when dislocating sample.a
- When a function returns a value of the class type
1
| StringClass someFunction();
|
- When returning the value out, copy constructor called automatically
- Similar problem as case 2 happens