c++ review cs 302 – data structures review topics calling functions by value or reference pointers...
TRANSCRIPT
C++ Review
CS 302 – Data Structures
Review Topics • Calling functions by value or reference
• Pointers and reference variables
• Static and dynamic arrays
• Constructors, destructors and copy-constructors
• Operator overloading
FunctionsSection 2.2
Two ways to call a function:
• Call by “value”– Useful for “protecting” the values of the variables
passed to a function.
• Call by “reference”– Useful for "returning” multiple values.
• Need to define “formal” and “actual” function parameters first …
int FindMax(int x, int y){ int maximum; if(x>=y) maximum = x; else maximum = y; return maximum;}
“FindMax” Example
“FindMax” Example (cont.)
#include <iostream.h> int FindMax(int, int); // function prototype
int main(){ int firstnum, secnum, max; cout << "\nEnter two numbers: "; cin >> firstnum >> secnum; max=FindMax( firstnum, secnum); // the function is called here cout << "The maximum is " << max << endl; return 0;}
• The argument names in the function header are referred to as formal parameters.
int FindMax(int x, int y){ int maximum; if(x>=y) maximum = x; else maximum = y; return maximum;}
Formal Parameters
x, y are called“formal parameters”
Actual Parameters• The argument names in the function call are referred
to as actual parameters
#include <iostream.h> int FindMax(int, int); // function prototype int main(){ int firstnum, secnum, max; cout << "\nEnter two numbers: "; cin >> firstnum >> secnum; max=FindMax( firstnum, secnum); // the function is called here cout << "The maximum is " << max << endl; return 0;}
firstnum, secnum are called“actual parameters”
Calling a function by “value”
• The formal parameters receive a copy of the actual parameter values.
• The function cannot change the values of the actual parameters.
#include <iostream.h>
void newval(float, float); // function prototype
int main(){ float firstnum, secnum; cout << "Enter two numbers: "; cin >> firstnum >> secnum; newval(firstnum, secnum); cout << firstnum << secnum << endl; return 0;} void newval(float xnum, float ynum){ xnum = 89.5; ynum = 99.5;}
Calling a function by value (cont.)
Calling a function by “reference”
• The formal parameters become an alias for the actual parameters.
• The function can change the values of the actual parameters.
Note: formal parameters must be declared as “reference” variables.
#include <iostream.h>
void newval(float&, float&); // function prototype
int main(){ float firstnum, secnum; cout << "Enter two numbers: "; cin >> firstnum >> secnum; newval(firstnum, secnum); cout << firstnum << secnum << endl; return 0;} void newval(float& xnum, float& ynum){ xnum = 89.5; ynum = 99.5;}
Calling a function by reference (cont.)
reference variables!
The "const" modifier • Call by reference is the preferred way to pass
a large structure or class instances to functions, since the entire structure need not be copied each time it is used!
• C++ provides us with protection against accidentally changing the values of variables passed by reference with the const operator
int FindMax(const int& x, const int& y);
Pointers and ReferenceVariables
Reference variables • A reference variable stores the address of another
variable.
• Reference variables must be initialized during declaration. #include <iostream.h> void main(){ int x = 3; int& y = x; cout << "x= " << x << "y = " << y << endl; y = 7; cout << "x= " << x << "y = " << y << endl;}
Reference variables and pointers
• A reference variable is a constant pointer (after initializing a reference variable, we cannot change it again).
• Reference variables are dereferenced automatically (no need to use the dereferencing operator *).
int b; // using reference variables
int& a = b;
a = 10;
int b; // using pointers
int *a = &b;
*a = 10;
Reference variables and pointers (cont.)
#include <iostream.h>
void newval(float&, float&); // function prototype
int main(){ float firstnum, secnum;
cout << "Enter two numbers: "; cin >> firstnum >> secnum;
newval(firstnum, secnum);
cout << firstnum << secnum << endl;
return 0;}
void newval(float& xnum, float& ynum){ xnum = 89.5; ynum = 99.5;}
“Call by reference” - revisited
no dereferencing !
#include <iostream.h>
void newval(float *, float *); // function prototype
int main(){ float firstnum, secnum;
cout << "Enter two numbers: "; cin >> firstnum >> secnum;
newval(&firstnum, &secnum);
cout << firstnum << secnum << endl;
return 0;}
void newval(float *xnum, float *ynum){ *xnum = 89.5; *ynum = 99.5;}
Same example, using pointers …
dereferencing is required !
pass address explicitly
Static / DynamicArray Allocation
Section 2.2
Two ways to allocate memory for an array:
• “Static” array allocation– Could waste memory.– Less efficient when passing a statically allocated
array to a function.
• “Dynamic” array allocation– More memory efficient.– More efficient to pass a dynamically allocated
array to a function.
Static Arrays
• When a static array is created, the compiler automatically creates an internal pointer constant (i.e., cannot change its contents).
• The name of the array becomes the name of the pointer constant.
• The pointer stores the starting address (base address) of the array (i.e., address of first byte)
1D Static Arrays (cont.)
assume arr is of type “int” and that “int” takes 2 bytes
int arr[6];
Need 12 bytes of contiguous memory!
• Referring to arr[i] causes the compiler, internally, to make the following address computation:
&arr[i] = &arr[0] + (i * sizeof(int))
Example: offset to arr[3] = 3 x 2 = 6 bytes
1D Static Arrays (cont.)
base address offset
2D Static Arrays
• Static 2D arrays are always stored in memory as 1D arrays in contiguous memory.
e.g,
Dynamic Array Allocation • Allocate memory at run time using the new
operator:
– Reserves the number of bytes requested by the declaration.
– Returns the address of the first reserved location or NULL if sufficient memory is not available.
arr = newnew int[N];
must be a pointer
Dynamic Array Allocation (cont.)
• Individual elements can be processed using index notation:– e.g., arr[1]=10;
cout << "Enter array size: ";cin >> N;
int *arr;arr = newnew int[N];
Dynamic Array De-allocation
• To de-allocate any memory that has been previously allocated using new, we need to use the delete operator.– Releases a block of bytes previously reserved. – Needs the address of the first location only.
deletedelete [ ] arr;
Example: 2D arrays
Suppose we need to allocate a 2 x 6 array: arr[2][6]
480 482 484 486 488 490
Example: 2D arrays (cont.) cout << "Enter numRows and numCols: ";cin >> numRows >> numCols; int **arr2D;arr2D = new int* [numRows]; // allocation for(i=0; i<numRows; i++) arr2D[i] = new int[numCols]; for(i=0; i<numRows; i++) // deallocation delete [] arr2D[i];delete [] arr2D;
• Individual elements can be processed using index notation:– e.g., arr2D[0][2]=10;
Static Arrays as Function Arguments
• To pass the whole array to a function, you need to specify the name of the array.
• In the case of 2D arrays, the number of columns must be specified in the function prototype and heading.
#include <iostream.h>
float find_average(int [], int);
void main(){ const numElems = 5; int arr[numElems] = {2, 18, 1, 27, 16};
cout << "The average is " << find_average(arr, numElems) << endl;}
float find_average(int vals[], int n){ int i; float avg;
avg=0.0; for(i=0; i<n; i++) avg += vals[i];
avg = avg/n;
return avg;}
Static Arrays:1D Example
#include <iostream.h> float find_average(int [][2], int, int); // function prototype void main(){ const numRows = 2; const numCols = 2; int arr2D[numRows][numCols] = {2, 18, 1, 27}; cout << "The average is " << find_average(arr2D, numRows, numCols) << endl;}
Static Arrays:2D Example
float find_average(int vals[][2], int n, int m)
{
int i,j;
float avg;
avg=0.0;
for(i=0; i<n; i++)
for(j=0; j<m; j++)
avg += vals[i][j];
avg = avg/(n*m);
return avg;
}
Static Arrays:2D Example (cont.)
#include <iostream.h>
float find_average(int *, int); // function proptotype
void main(){ int *arr, numElems;;
cin << numElems;
arr = new int[numElems]; // then, initialize array …
cout << "The average is " << find_average(arr, numElems) << endl;}
Dynamic Arrays:1D Example
float find_average(int *vals, int n){ int i; float avg; avg=0.0; for(i=0; i<n; i++) avg += vals[i]; avg = avg/n; return avg;}
Dynamic Arrays:1D Example (cont.)
#include <iostream.h> float find_average(int **, int, int); // function prototype void main(){ int **arr2D; arr2D = new int*[numRows]; for(i=0; i<numRows; i++) arr2D[i] = new int[numCols]; // then, initialize array … cout << "The average is " << find_average(arr2D, numRows,
numCols) << endl;}
Dynamic Arrays:2D Example
float find_average(int **vals, int n, int m){ int i, j; float avg; avg=0.0; for(i=0; i<n; i++) for(j=0; j<m; j++) avg += vals[i][j]; avg = avg/(n*m); return avg;}
Dynamic Arrays:2D Example (cont.)
Constructors, Destructorsand Copy-Constructors
Section 2.3, p. 140, pp. 360-367
What is a constructor?
• A member function which initializes a class.
• A constructor has:
(i) the same name as the class itself
(ii) with or without arguments, no return type
Comments on constructors
• A constructor is called automatically whenever a new instance of a class is created.
• If you do not specify a constructor, the compiler generates a default constructor for you (expects no parameters and has an empty body).
class rectangle { private: float height; float width; int xpos; int ypos; public: rectangle(float, float); // constructor void draw(); // draw member function void posn(int, int); // position member function void move(int, int); // move member function}; rectangle::rectangle(float h, float w){ height = h; width = w; xpos = 0; ypos = 0;}
void main(){ rectangle rc(3.0, 2.0); rc.posn(100, 100); rc.draw(); rc.move(50, 50); rc.draw();}
• Warning: attempting to initialize a data member of a class explicitly in the class definition is a syntax error.
class rectangle { private: float height; float width; int xpos; int ypos; properties pr; // another object public: rectangle(float, float, int, int ); // constructor void draw(); // draw member function void posn(int, int); // position member function void move(int, int); // move member function};
Object Composition
A class may have objects of other classes as members.
Object Composition (cont.)
class properties { private: int color; int line; public: properties(int, int); // constructor}; properties::properties(int c, int l){ color = c; line = l;}
Object Composition (cont.)
rectangle::rectangle(float h, float w, int c, int l):pr(c, l){ height = h; width = w; xpos = 0; ypos = 0;}; void main(){ rectangle rc(3.0, 2.0, 1, 3); ………}
What is a destructor?
• A member function which deletes an object.
• A destructor has: (i) the same name as the class but is preceded by a
tilde (~)
(ii) no arguments and returns no values
Comments on destructors
• A destructor is called automatically whenever an object goes out of scope or when the program terminates.
• If you do not specify a destructor, the compiler generates a default destructor for you.
• When a class contains a pointer to memory you allocate, it is your responsibility to release the memory before the class instance is destroyed.
class string { private: char *s; int size; public: string(char *); // constructor ~string(); // destructor};
string::string(char *c) // constructor
{ size = strlen(c); s = new char[size+1]; strcpy(s,c);} string::~string() // destructor{ delete []s;}
• It is a member function which initializes an object using another object of the same class.
• A copy constructor has the following general function prototype:
class_name (const class_name&);
What is a copy constructor?
class rectangle { private: float height; float width; int xpos; int ypos; public: rectangle(float, float); // constructor rectangle(const rectangle&); // copy constructor void draw(); // draw member function void posn(int, int); // position member function void move(int, int); // move member function};
rectangle::rectangle(const rectangle& old_rc){ height = old_rc.height; width = old_rc.width; xpos = old_rc.xpos; ypos = old_rc.ypos;} void main(){ rectangle rc1(3.0, 2.0); // use constructor rectangle rc2(rc1); // use copy constructor ………… }
Comments on copy constructors
• In the absence of a copy constructor, the C++ compiler builds a default copy constructor for each class which is doing a member-wise copy between objects.
• The default copy constructor will not work well if the class contains pointer data members ...
#include <iostream.h>#include <string.h> class string { private: char *s; int size; public: string(char *); // constructor ~string(); // destructor void print(); void copy(char *);}; void string::print(){ cout << s << endl;}
Note: no user definedcopy constructor!
void string::copy(char *c){ strcpy(s, c);}
void main()
{
string str1("George");
string str2 = str1; // default copy constructor
str1.print(); // what is the output ?
str2.print();
str2.copy("Mary");
str1.print(); // what is the output now ?
str2.print();
}
Defining a copy constructor …
class string {
private:
char *s;
int size;
public:
string(char *); // constructor
~string(); // destructor
string(const string&); // copy constructor
void print();
void copy(char *);
};
string::string(const string& old_str){ size = old_str.size; s = new char[size+1]; strcpy(s,old_str.s);}
void main(){ string str1("George"); string str2 = str1;
str1.print(); // what is printed ? str2.print();
str2.copy("Mary");
str1.print(); // what is printed now ? str2.print(); }
Operator Overloading
pp. 367-370
What is operator overloading?
• Changing the definition of an operator.
• Need to write a function for the operator we are overloading.
Special cases
• The address operator (&) may be used with objects of any class without explicit overloading.– It returns the address of the object in memory.
• The assignment operator (=) may be used with every class without explicit overloading, however, its default behavior is a member-wise assignment of the data members of the class.
Example: overloading the assignment operator
• The default overloading is not enough for classes with pointer members.
void operator=(class_name&);
class string { private: char *s; int size; public: string(char *); // constructor ~string(); // destructor void operator=(string&); void print(); void copy(char *);};
void string::operator=(string& old_str){ delete [] s; // must release previously assigned memory size = old_str.size; s = new char[size+1]; // assign new memory strcpy(s, old_str.s);} void main(){ string str1("George"); string str2("Mary"); string str3("John"); str1.print(); // what is printed ? str2.print(); str3.print();
str3 = str1; str2 = str1; str1.copy(“Kate"); str1.print(); // what is printed now ? str2.print(); str3.print();}
Handling multiple assignments(e.g., x=y=z;)
string& string::operator=(const string& old_str) {
if (this != &old_str) { // make sure not same object delete [] s; // delete old name's memory size = old_str.size; s = new char[size+1]; // allocate new memory strcpy(s, old_str.s); // copy new string}return *this; // return ref for multiple assignment} //end operator=
What is the main difference between the copy constructor and the
assignment operator?
• The copy constructor creates a new object.
• The assignment operator works on an already valid object.