C++ crash course Class 4 arrays, pointers, functions Agenda • • • • • • Boolean Logic Arrays and Pointers redux Functions Class functions Constructors Finishing the bookstore Boolean Logic • Computers deal in 1’s and 0’s • Many times we want to make decisions in terms of logic if( am_hungry AND like_apples) { eat_apple; } Boolean Logic • C++ (like many other languages) allows you to do this using logical operators • These are binary operators, meaning they operate on two operands (the quantities on either side) • Results in a bool-type expression && || ! logical and logical or logical not == != <, >, <=, >= tests equality tests non-equality comparison operators operate on bool-type expressions only can operate on many types Boolean Logic • How do we understand these operators? – Bool-expressions are either TRUE or FALSE • &&, ||, ! all change these values A B A && B FALSE FALSE FALSE FALSE TRUE FALSE TRUE FALSE FALSE TRUE TRUE TRUE Boolean Logic A B A || B FALSE FALSE FALSE FALSE TRUE TRUE TRUE FALSE TRUE TRUE TRUE TRUE A !A FALSE TRUE TRUE FALSE Arrays • • • We started talking about these yesterday Contiguous allocation in memory, accessed by pointers Arrays are a compound type – Need: type specifier, identifier, dimension (must be known at compile-time) const unsigned buf_size = 512, max_files = 20; int staff_size = 27; const unsigned sz = get_size(); //ok char input_buffer[buf_size]; string fileTable[max_files + 1]; int vars[12]; // this works, but is bad practice // don’t hardcode except in consts //bad double salaries[staff_size]; int test_scores[get_size()]; int vals[sz]; Arrays • • When we initialize an array, we can provide an initialization for each element Otherwise, arrays will be initialized as they are in vectors – default variable initializers const unsigned array_sz = 3; int ia[array_sz] = {0,1,2}; const unsigned array_sz2 = 5; int ia2[array_sz2] = {0,1,2}; // this results in {0,1,2,0,0}; • Character arrays are special, and can be initialized with character string literals char ca1[] = {‘C’, ‘+’, ‘+’}; // note: no null terminator! char ca2[] = {‘C’, ‘+’, ‘+’, ‘\0’}; // null terminated char ca3[] = “C++”; // automatically null-terminated; equivalent // to the previous line Arrays • Can’t initialize arrays with other arrays! int ia[] = {0,1,2}; // OK int ia2[](ia); // bad int ia3[3]; // ok, but uninitialized elements ia3 = ia; // bad • Arrays CANNOT get bigger once they’ve been created • Some compilers will let you do it anyway as a compiler extension, but you’re not guaranteed this Arrays • Operators – The size of an array is type size_t – No size function; should be set as a const somewhere though – Subscript operator can change elements (a[n]) • Unlike in other languages, it is PERFECTLY LEGAL to access an array element outside of the size of the array! – Don’t do this: you’re messing with memory you shouldn’t be. Always check your array size before access. Arrays • get_size() takes no arguments and returns an int • Which of these are legal definitions? unsigned buf_size = 1024; (a) int ia[buf_size]; (b) int ia[get_size()]; (c) ia[4 * 7 – 14]; (d) char st[11] = “fundamental”; Arrays • Which of these definitions are illegal? (a) int ia[7] = {0,1,1,2,3,5,8}; (b) vector<int> ivec = {0,1,1,2,3,5,8}; (c) int ia2[] = ia1; (d) int ia3[] = ivec; Pointers • Pointers can also be used for array access • We saw this yesterday – Pointers are compound types – Pointers are the iterators for arrays • Iterators can only be used for a specific container • Pointers can point to any kind of variable Pointers • * is the dereference operator – in a variable definition, this indicates that the variable is a pointer • & is the address-of operator – tells us the address of a variable – Remember this from references? In a reference, instead of setting the value of a variable when it’s declared, we set its address – this is why references are bound once they’re initialized Pointers • We can iterate through an array like so: const size_t array_sz = 4; int ia[array_sz] = {0,1,2,3}; for(int *ptr = ia,size_t ctr = 0; ctr < array_sz; ptr++, ctr++) cout << *ptr; • Why did we need the counter? Pointers • As mentioned, we can point to anything vector<int> *pvec; int *ip1, *ip2; string *pstring; double *dp; double dp, *dp2; // only dp2 is a pointer here Pointers • You can also write: string* ps1, ps2; • …but that can be confusing, since only ps1 is getting defined as a pointer Pointers • As always, make sure you initialize your pointers! • Pointers only hold addresses in memory • Just like when we fail to initialize ints or other types with no default constructor (usually basic types), pointers will try to interpret whatever’s in memory at the point it’s been assigned as the address it should be pointing to • If you don’t know where to point your pointer, point it at 0 – that way you can find out if it’s been initialized Pointers • Initialization can happen in four ways: 1. 2. 3. 4. Constant expression with value 0 Address of an object that is of the right type Address one past the end of an object Another pointer of the same type Pointers • Which of these are okay? int ival; int zero = 0; const int c_ival = 0; int *pi = &ival; pi = zero; pi = c_ival; pi = 0; Pointers • If you’re using the <cstdlib> header, you can also use NULL • contains the line #define NULL 0 …which tells the preprocessor to interpret any place in the program that has the word NULL as being 0 • If you try to dereference a null pointer, you will get a Segmentation Fault Pointers • There’s also void pointers, which can hold the address for any kind of type double obj = 3.14; double *pd = &obj; int myint = 3; void *pv = &obj; pv = pd; pv = &myint; Pointers • We have pointers so we can operate on the objects they point to string s(“hello world”); string *sp = &s; cout << *sp; • This dereferences the pointer Pointers • Which of the following are legal definitions? (a) int *ip; (b) string s, *sp = 0; (c) int i; double *dp = &i; (d) int *ip, ip2; (e) const int i=0, *p = i; (f) string *p = NULL; Pointers • Assigning through pointers string s1(“some value”); string s2(“another value”); string *sp1 = &s1; string *sp2 = &s2; *sp1 = “a new value”; cout << s1 << endl << s2 << endl; sp1 = sp2; *sp1 = “a new value”; cout << s1 << endl << s2 << endl; Pointers (to pointers) • Since pointers are also objects, we can point to them too int ival = 1024; int *pi = &ival; int **ppi = &pi; (Is your head exploding yet?) Pointers • What does this program do? int i = 42, j = 1024; int *p1 = &I, *p2 = &j; *p2 = *p1 * *p2; *p1 *= *p1; Pointers and Arrays • Yesterday we looked at iterator arithmetic • Pointer arithmetic is much the same int ia[] = {0,2,4,6,8}; int *ip = ia; ip = &ia[4]; ip = ia; int *ip2 = ip+4; // equivalent to &ia[4]; Pointers and Arrays • We can dereference an expression int last = *(ia + 4); // refers to the 4th value // in the array last = *ia + 4; // adds 4 to the 0th value // in the array Pointers and Arrays • Subscripting an array and subscripting a pointer are effectively the same operation int *p = &ia[2]; int j = p[1]; // 1 int in front of where p is int k = p[-2]; // 2 ints behind where p is Pointers and Arrays • Another way to write the for loop we used earlier const size_t arr_sz = 5; int int_arr[arr_sz] = {0,1,2,3,4}; for (int *pbegin = int_arr, *pend = int_arr + arr_sz; pbegin != pend; pbegin++) cout << *pbegin << ‘ ‘; Pointers • Where is p1 after this line of code? – p1 and p2 are both int pointers somewhere in the same array p1 += p2 – p1; Pointers • We can make pointers to objects for types we create • There’s a special operator, ->, for accessing public members of those objects • Shorthand for (*ptr). Sales_item item1, item2; Sales_item *pitem = &item1; pitem->same_isbn(item2); (*pitem).same_isbn(item2); Pointers and const • Pointers to const objects should not be able to change them • Pointers to const objects must use the const keyword const double mydbl = 17.0; const double mydbl2 = 43.0; double nonconst = 25; const double *cptr = &mydbl; // ok cptr = &mydbl2; // also ok: the ptr isn’t const Pointers and const double dval = 3.14; const double *cptr = &dval; dval = 3.14159; *cptr = 3.14159; double *ptr = &dval; *ptr = 2.72; cout << *cptr; Dynamic Arrays • We said that arrays have fixed size • Not completely true; it’s possible to create dynamically-sized arrays int *pia = new int[10]; // array of 10 // uninitialized ints int *pia = new int[10] (); // array of 10 // initialized ints Dynamic Arrays • Using the new keyword causes the allocation to occur • When we’re done with an array created dynamically, we need to get rid of it with the delete keyword • Otherwise it will result in a memory leak delete [] pia; Functions • So far the only one we’ve seen is main() • Functions are defined by a name, and a set of operands • Function prototype is the return type, name and parameters int gcd(int v1, int v2) { while (v2) { int temp = v2; v2 = v1 % v2; v1 = temp; } return v1; } Functions • In order to use a function, we use the call operator, or () // get values from standard input cout << “Enter two values: \n”; int i,j; cin >> i >> j; // call gcd on arguments i and j // and print their greatest common divisor cout << “gcd: “ << gcd(i,j) << endl; Functions • The parameters (int v1 and int v2 for gcd) of the function are initialized when the function is called • The arguments to a function become the parameters once inside the function – Sometimes you’ll hear these used interchangeably • The type of an argument must match its coresponding parameter • Any parameters and any variables defined inside the function have local scope – they exist only inside the body of the function • Must also have a return type, which says what kind of object will come out of the function Functions • Return types can be built-in types, class types, or compound types bool is_present(int *, int); int count(const string &, char); Date &calendar(const char*); void process(); • Can’t return a function or an array, but can return pointers to functions and arrays • You must specify a return type in Standard C++ (even if it’s void, or int) Functions • Parameter lists must specify the type for every parameter void process() { /* … */ } // legal void process(void) { /* … */ } // legal int manip(int v1, v2){ /* … */} // illegal int manip(int v1, int v2){/* … */} // legal • Names are optional on parameters, but to use them, they must have names Functions • Parameters are checked at compile-time to see if the argument types match gcd(“hello”, “world”); gcd(24312); gcd(42, 10, 0); • All of the above cause compile-time errors gcd(3.14, 6.29); // no error; converts doubles to ints Functions • What’s wrong with these function definitions (if anything)? (a) int f() { string s; // … return s; } (b) f2(int i) { /* … */ } (c) int calc(int v1, int v1) { /* … */ } (d) double square(double x) return x * x; Functions • • Parameters that are not references are initialized by copying the arguments Changes to the values inside the function will not change them outside the function void swap(int a, int b) { int c = a; a = b; b = c; return; } int main() { int x = 5, y = 4; swap(x,y); cout << “The values are: “ << x << “ “ << y << endl; return 0; } The values are: 5 4 Functions • In order to change the values, we need to use pointers! void swap(int *a, int *b) { int c = *a; *a = *b; *b = c; return; } int main() { int x = 5, y = 4; swap(&x,&y); cout << “The values are: “ << x << “ “ << y << endl; return 0; } The values are: 4 5 Functions • Or we can use references: void swap(int &a, int &b) { int c = a; a = b; b = c; return; } int main() { int x = 5, y = 4; swap(x,y); cout << “The values are: “ << x << “ “ << y << endl; return 0; } The values are: 4 5 Functions • Do not return a reference to a local variable // Disaster: Function returns a reference to a local object const string &manip(const string& s) { string ret = s; // transform ret in some way return ret; // Wrong: returning a reference to a local object! } Class Functions • Back to Sales_item class Sales_item { public: // operations on Sales_item objects double avg_price() const; bool same_isbn(const Sales_item &rhs) const { return isbn == rhs.isbn } private: std::string isbn; unsigned units_sold; double revenue; } Class Functions • Looking at same_isbn: bool same_isbn(const Sales_item &rhs) const { return isbn == rhs.isbn } • Body is a block • isbn is private, but this is not an error – private members can be accessed by class functions Class Functions • Member functions of a class have an extra parameter that isn’t in the parameter list • That parameter is this – a reference to the object on which the function was called • It’s almost as if a call gets translated like so: total.same_isbn(trans); Sales_item::same_isbn(&total, trans); Constructors • We’ve talked about the default constructor… …but what’s a constructor? • A constructor is a member function that gets run when an object of the type is initialized – In turn, constructors should make sure that every data member is initialized • Constructors have the same name as the class • Sales_item needs to define the default constructor, which takes no arguments • Constructors must be public, or it is not possible to create objects of that type! Constructors class Sales_item { public: // operations on Sales_item objects double avg_price() const; bool same_isbn(const Sales_item &rhs) const return isbn == rhs.isbn } // default constructor needed to initialize basic-type members Sales_item(): units_sold(0), revenue(0.0) {} private: std::string isbn; unsigned units_sold; double revenue; } { Constructors • The colon and what follows is the constructor initialization list • List of member names, followed by the initial value in parentheses • We don’t need to specify an initial value for isbn, since std::string has its own default constructor (makes it the empty string) • If we don’t explicitly define a default constructor, a synthesized default constructor will be generated for us – Sets all basic types to 0; rest use default constructor Overloaded Functions • You can rewrite functions so that they behave differently when used with a different parameter list • Sales_item will use this to redefine the basic operators Sales_item class • We had enough yesterday to start to write the headers • Now we can finish writing the code! Sales_item class • Operations: – Use addition operator + to add two Sales_items – Use input operator << to read a Sales_item – Use output operator >> to write a Sales_item – Use assignment operator = to assign one Sales_item to another – Call same_isbn function to see if two Sales_items are the same book Administrivia Updates • Exam on Tuesday – First half of the class will be review / question session; second half will be the midterm – Covering anything we did from this week • Project #2 due date will be moved to Wednesday 10pm – Will be released tomorrow • HW #2 still due beginning of class Tuesday