CSCE 121:509-512 Introduction to Program Design and Concepts, Honors Dr. J. Michael Moore Spring 2015 Set 16: Vector and Free Store CSCE 121-200: Set 16: Vector and Based on slides created by Bjarne Free Store Stroustrup and Jennifer Welch 1 Vector’s Virtues • Vector is most useful container: use it to store elements unless you have a good reason not to – Simple – Compactly stores element of a given type – Efficient access – Expands to hold any number of elements – Option for range checking • How is it done? CSCE 121:509-512 Set 16: Vector and Free Store 2 How is Vector Implemented? We will study this and learn about • Pointers • Free store: allocating, accessing, deallocating • Destructors • Initialization, copying and moving • Arrays • Changing size • Templates • Range checking and exceptions CSCE 121:509-512 Set 16: Vector and Free Store 3 Rationale for Studying Vector Implementation • Real-world case study to motivate these concepts and their C++ instantiations • Understand bridge from low-level hardware to high-level services • Hardware only provides memory and addresses: – Untyped – Fixed size – No checking CSCE 121:509-512 Set 16: Vector and Free Store 4 Review of Computer Memory • Local variables “live on the stack” • Global variables are “static data” • Executable code is in the “code section” • Free store (heap) is subject of this lesson CSCE 121:509-512 Set 16: Vector and Free Store 5 Vector, Take 1 //very simplified vector of doubles class vector { int sz; // number of elements double* elem; // pointer to first element public: vector(int s); // constructor; more later int size() const { return sz; } }; What is going on with double*? CSCE 121:509-512 Set 16: Vector and Free Store 6 Pointer Values • Pointer values are memory addresses – Think of them as a kind of integer – The first byte of memory has address 0, the next 1, etc. – A pointer variable p can hold the address of a memory location • A pointer points to an object of a given type – E.g., a double* points to a double, not to a string • A pointer’s type determines how the memory referred to by the pointer’s value can be used 0 1 2 p 600 CSCE 121:509-512 Set 16: Vector and Free Store 600 220-1 7 7 Pointer Syntax and Terminology • To declare a variable p as a pointer to an object of type int (p holds the address of an object of type int): int* p; – Note: You can also say “int *p;” but be careful when declaring multiple variables on same line – “int *px, py;” makes px a pointer and py an int) • To get to the object that p points to (to access its value), we have to dereference p: *p = 5; // changes memory whose address is stored // in p, not the data stored in location p int y = 4 + *p; // y holds 9 • Pointers can point to object of any type CSCE 121:509-512 Set 16: Vector and Free Store 8 Pointer vs. Reference • Think of a reference as an automatically dereferenced pointer – you do not need to use the * (dereference) operator to get to the value – thus it is an alternative name for an object • Differences from pointer: – reference must be initialized – value of a reference cannot be changed after initialization (reference always refers to the same object) CSCE 121:509-512 Set 16: Vector and Free Store 9 Pointer vs. Reference Examples int x = 7; int y = 8; int* p = &x; *p = 9; p = &y; // OK: can change which object p points to int& r = x; x = 10; r = &y; // error: cannot change which object r // refers to CSCE 121:509-512 Set 16: Vector and Free Store 10 Pointer Hazards • Pointers (are supposed to) hold addresses • They really hold bit patterns that are interpreted as addresses • If a pointer somehow holds arbitrary bits, it can refer to: – address that doesn’t exist – address that exists but is definitely not where you want to be! • Reading from this (random) location returns garbage • Writing to this (random) location corrupts memory • Bad effects (run-time errors): – crash the operating system – cause memory protection exception – produce incorrect results CSCE 121:509-512 Set 16: Vector and Free Store 11 Mitigating Pointer Hazards • Avoid Using Pointers When Possible – E.g., use vector instead of arrays • Only use pointers in very constrained situations • When you do use pointers, be sure to initialize them properly: int* pi = nullptr; // or NULL or 0 CSCE 121:509-512 Set 16: Vector and Free Store 12 Pointing to Objects on the Stack • You can use pointers to get the address of objects stored in the stack using the & (address-of) operator int main() { int i; int *pi = nullptr; pi = &i; // pi gets address of i *pi = 502; // assigns to i cout << "i = " << i << endl; // outputs 502 cout << "pi = " << pi << endl; // outputs, e.g., 0x7fff503ebbb8 cout << "*pi = " << *pi << endl; // outputs 502 return 0; } CSCE 121:509-512 Set 16: Vector and Free Store 13 Pointing to Objects on the Heap • An important use of pointers is to point to objects that are allocated on the heap (free store) CSCE 121:509-512 Set 16: Vector and Free Store 14 Dynamic Memory Allocation • Use the operator new to allocate a chunk of memory on the heap – specify the type of object to be stored and – specify the number of objects of that type • new returns a pointer to the first object in the chunk • Memory allocated with new is not automatically deallocated! – you must do that explicitly yourself with the delete operator – thus you can allocate objects that outlive the function that creates them CSCE 121:509-512 Set 16: Vector and Free Store 15 Example Use of new int s = 100; double* dp = new double[s]; // Allocates space for 100 // consecutive doubles on the heap. // To check whether new was // successful: if (dp == 0) error(“new failed”); // Continue and use the space. // If you only want one double, // then leave off the [s] CSCE 121:509-512 Set 16: Vector and Free Store 16 Vector, Take 1, Constructor vector::vector(int s) // vector’s constructor : sz(s), // store the size s in data member sz elem(new double[s]) // allocate s consecutive doubles in free store, // store pointer to the first double in // data member elem {} // new does not initialize the elements (but the // standard vector does) Free store: sz: 4 elem: A pointer CSCE 121:509-512 Set 16: Vector and Free Store 17 Accessing Memory Allocated on the Heap • Individual objects allocated in a batch by new are accessed using the [ ] notation for indexing, starting at 0 int* pi pi[0] = pi[1] = int x = int y = = new int[5]; 7; 9; pi[1]; *pi; // means pi[0] CSCE 121:509-512 Set 16: Vector and Free Store 18 A Pointer Pitfall • A pointer actually holds the address of a byte – the first byte of the object or sequence of objects • A pointer does not know how many objects have been allocated – doesn’t know where to stop int* pi = new int[5]; int y = -3; pi[5] = 72; // compiles like a charm :( pi[-4] = 12; // ditto CSCE 121:509-512 Set 16: Vector and Free Store 19 Assigning a Pointer to a Pointer • What happens in this case? double* p1 = new double; double* p2 = new double[100]; p1[17] = 9.4; // run-time or logic error p1 = p2; p1[17] = 9.4 // OK now • the assignment “p1 = p2” copies the data in p2 (the address of the start of the 100 doubles) into p1 CSCE 121:509-512 Set 16: Vector and Free Store 20 Pointers and Types • A pointer does know the type of the object that it is pointing to int* pi1 = int* pi2 = double* pd char* pc = new int(7); // initialize to 7 pi1; // OK = pi1; // compile-time error pi1; // compile-time error • There are no implicit conversions between a pointer to one value type to a pointer to another value type CSCE 121:509-512 Set 16: Vector and Free Store 21 Pointer-Related Errors • The following errors are often caused by uninitialized or otherwise invalid pointers: – segmentation fault – bus error – core dump CSCE 121:509-512 Set 16: Vector and Free Store 22 Vector, Take 1, Access class vector { int sz; double* elem; public: vector(int s) : sz(s), elem(new double[s]) { } // constructor double get(int n) const { return elem[n]; } // access: read void set(int n, double v) { elem[n] = v; } // access: write int size() const { return sz; } // the current size }; // ... vector v(10); for (int i = 0; i < v.size(); ++i) { v.set(i,i); cout << v.get(i); } CSCE 121:509-512 Set 16: Vector and Free Store 23 A Problem: Memory Leak double* calc(int result_size, int max) { double* p = new double[max]; double* result = new double[result_size]; // use p to calculate values to be put in result return result; } // ... double* r = calc(200,100); • We did not give the memory allocated for p back to the free store but there is no way to access it once calc is finished • Memory leak (lack of deallocation) – can be a serious problem in real-world programs CSCE 121:509-512 Set 16: Vector and Free Store 24 Memory Leaks • A program that needs to run “forever” (e.g., an operating system) cannot afford any memory leaks – eventually memory leaks will use up all available memory • All memory is returned to the system at the end of the program • Programs that run to completion with predictable memory usage may leak without causing problems CSCE 121:509-512 Set 16: Vector and Free Store 25 Preventing Memory Leak: Deallocation • Use operator delete (for single object) or delete[] (for multiple objects, i.e., array): double* calc(int result_size, int max) { int* p = new double[max]; double* result = new double[result_size]; // use p to calculate values to be put in result delete[] p; // deallocates memory pointed to by p return result; } // ... double* r = calc(200,100); // use r delete[] r; // but this is easy to forget CSCE 121:509-512 Set 16: Vector and Free Store 26 Another Memory Leak void f() { double* p = new double[27]; // ... 1st value p = new double[42]; // ... p: delete[] p; } 2nd value // first array // (of 27 doubles) // leaked CSCE 121:509-512 Set 16: Vector and Free Store 27 Preventing Memory Leaks More Systematically • One option is to have the run-time system, instead of the programmer, find inaccessible memory and return it to the free store – called garbage collection – some languages, including Java, have this – C++ does not (efficiency issues) • Another option, relevant to C++, is to avoid using new and delete except in very constrained ways – mostly rely on features, like vector, that do it for us CSCE 121:509-512 Set 16: Vector and Free Store 28 Vector, Take 1, Leaks Memory void f(int x) { // constructor allocates x doubles on the // free store vector v(x); // ... use v ... } // How can we give those x doubles back to // the free store before f returns? // v.elem is a private data member in vector CSCE 121:509-512 Set 16: Vector and Free Store 29 Destructor • C++ classes allow us to define a special function, called a destructor • Destructor is automatically called whenever a variable of that class goes out of scope – for example, when the function that defined the variable is about to return • The destructor code can do “clean up” – most notably, it can delete any memory that has been allocated on the free store • Destructor for class T is denoted ~T() CSCE 121:509-512 Set 16: Vector and Free Store 30 Vector, Take 1, Destructor // a very simplified vector of doubles class vector { int sz; double* elem; public: // constructor: vector(int s) : sz(s) elem(new double[s]) { } // ... get, set, size ... // destructor: ~vector() { delete[] elem; } }; CSCE 121:509-512 Set 16: Vector and Free Store 31 Important Resource Management Technique • Acquire resources in a constructor • Release resources in the destructor Examples of resources: • memory • files • locks • threads • sockets CSCE 121:509-512 Set 16: Vector and Free Store 32 Comparing Code Styles Managing Memory Yourself void f(int x) { int* p = new int[x]; // ... use p ... delete[] p; } Managing Memory in Constructors and Destructors void f(int x) { vector v(x); // ... use v ... } CSCE 121:509-512 Set 16: Vector and Free Store 33 Free Store Summary: Allocation • Allocate using new (preferably in a constructor) – new allocates an object on the free store, sometimes initializes it, and returns a pointer to it int* pi = new int; // default initialization // (none for int) char* pc = new char(‘a’); // explicit // initialization double* pd = new double[10]; // allocation of // (uninitialized) array – new throws a bad_alloc exception if it cannot allocate (out of memory) CSCE 121:509-512 Set 16: Vector and Free Store 34 Free Store Summary: Deallocation • Deallocate using delete and delete[] (preferably in a destructor) – delete and delete[] return the memory of any object allocated by new to the free store for use in future allocations delete pi; // deallocate an individual object delete[] pd; // deallocate an array of objects – delete of a zero-valued pointer (“null pointer” or nullptr) does nothing: char* p = nullptr; delete p; // harmless CSCE 121:509-512 Set 16: Vector and Free Store 35 Allocating a Vector on the Stack vs. Heap Allocate Vector on Stack vector v(5); v.set(0,10); cout << v.get(0); v.set(1,40); cout << v.get(1); Allocate Vector on Heap vector* pv = new vector(5); (*pv).set(0,20); cout << (*pv).get(0); // Parentheses necessary! // shorthand -> notation pv->set(1,40); cout << pv->get(1); CSCE 121:509-512 Set 16: Vector and Free Store 36 Allocating a Vector on the Stack vs. Heap • The difference is where the “bookkeeping” goes (sz and elem) • In both cases, the array of objects pointed to by elem is on the heap • If vector is allocated on the stack (vector v(5)), then sz and elem are on stack • If vector is allocated on the heap (vector* pv = new vector(5)), then sz and elem are on the heap CSCE 121:509-512 Set 16: Vector and Free Store 37 Deallocation of Vector Declared on the Stack • When vector variable goes out of scope: – destructor is called automatically to deallocate the array of elements – the bookkeeping (sz and elem), which were on the stack, will automatically be deallocated when the stack frame for that scope is popped • Safe! No memory leak CSCE 121:509-512 Set 16: Vector and Free Store 38 Deallocation of Vector Declared on the Heap • When pointer variable goes out of scope: – the space for the pointer is automatically deallocated when the stack frame for that scope is popped • But you are responsible for deallocating the bookkeeping (sz and elem) and the array! • Note: destructor is automatically called when delete is invoked on a pointer to an object of that class CSCE 121:509-512 Set 16: Vector and Free Store 39 Managing a Vector on the Stack vs. Heap Vector on Stack void f() { vector v(5); v.set(0,10); cout << v.get(0); } Vector on Heap void f() { vector* pv = new vector(5); pv->set(0,10); cout << pv->get(0); delete pv; } // delete pv automatically calls // destructor, which frees array, // then delete frees sz and elem CSCE 121:509-512 Set 16: Vector and Free Store 40 void* • void* means “pointer to some memory whose type is unknown to the compiler” • Use void* to transmit an address between pieces of code that do not know each other’s types – example: arguments of a callback function • There are no objects of type void: void v; // error void f(); // f returns nothing; does not // return an object of type void CSCE 121:509-512 Set 16: Vector and Free Store 41 Assigning to void* • Any pointer can be assigned to a void* pointer: int* pi = new int; double* pd = new double[10]; void* pv1 = pi; pv1 = pd; CSCE 121:509-512 Set 16: Vector and Free Store 42 Using void* • Most operations on pointers are forbidden on void* pointers: – cannot dereference, cannot subscript, cannot increment, no implicit conversions • Copying (assignment), however, is allowed – that’s what it is for • To use what a void* pointer points to, we must explicitly tell the compiler what type of object it points to, using static_cast – use static_cast only when absolutely necessary! CSCE 121:509-512 Set 16: Vector and Free Store 43 Code Examples with void* void f(void* pv) { void* pv2 = pv; // OK – copying double* pd = pv; // error – implicit conversion *pv = 7; // error – dereference pv[2] = 9; // error – subscript pv++; // error – increment int* pi = static_cast<int*>(pv); // OK – // explicit conversion // ... can access the memory via pi } CSCE 121:509-512 Set 16: Vector and Free Store 44 void* and FLTK Callbacks • void* is the closest C++ has to a plain machine address • Some system facilities require a void* • Remember FLTK callbacks? The parameters were of type Address, which is just a synonym for void*: typedef void* Address; void Lines_window::cb_next(Address,Address); CSCE 121:509-512 Set 16: Vector and Free Store 45 Acknowledgments • Photo on slide 1: by mekuria getinet, licensed under CC BY2.0 • Core C++: A Software Engineering Approach, Victor Shtern, Prentice Hall, 2000 • Slides are based on those for the textbook: http://www.stroustrup.com/Programming/17_free_store.ppt CSCE 121:509-512 Set 16: Vector and Free Store 46