#include #include "Array.hpp" // Compile this file together with Array.cpp. // // This file contains a main() function. A C++ program can // have only one main() function. If your project already // contains a main.cpp file, either: // // 1. remove main.cpp from the project or build, or // // 2. temporarily rename its main() function to old_main(). // // The second option allows the original demonstration // program to remain in the project while this program is // being compiled and run. int main() { // PROBLEM 1: // // The user may forget to initialize an Array object. // // Array data; // // In that case, capacity_ and array_ have indeterminate // values. In particular, array_ may be a wild pointer. // // We will not actually do this because subsequently // using such an object may result in undefined behavior. // Instead, correctly initialize the object. Array data{}; std::cout << "After Array data{}:" << std::endl; std::cout << "data.capacity_ = " << data.capacity_ << std::endl; std::cout << "data.array_ = " << static_cast( data.array_ ) << std::endl; // Allocate an array containing five entries. array_init( data, 5, 3.0 ); std::cout << std::endl << "After array_init( data, 5, 3.0 ):" << std::endl; std::cout << "data = " << data << std::endl; std::cout << "data.capacity_ = " << data.capacity_ << std::endl; // PROBLEM 2: // // The user can change the capacity without changing the // dynamically allocated array. // // For example, the following statement would claim that // there are 1000 entries even though only five were // allocated: // // data.capacity_ = 1000; // // Functions trusting capacity_ could then access memory // beyond the allocated array. We will not actually make // this change because accessing that memory would result // in undefined behavior. // PROBLEM 3: // // The user can overwrite the pointer: // // data.array_ = nullptr; // // If we did this now, the address of the dynamically // allocated array would be lost: // // data.array_ ----X----> [3][3][3][3][3] // | // +-------------> nullptr // // The allocated memory would still exist, but we would // no longer have its address and therefore could not // deallocate it. This is a memory leak. // // Again, we will not actually execute that statement // because we do not want this demonstration to // deliberately leak memory. // PROBLEM 4: // // The user must remember to explicitly clean up the // dynamically allocated array. // // If we simply returned from main() now without calling // // array_clean_up( data ); // // the dynamically allocated array would not be // deallocated by our Array class. // // The programmer therefore has to remember a particular // sequence of operations: // // Array data{}; // array_init( data, ... ); // // // use data // // array_clean_up( data ); // // The class itself does not enforce this sequence. // Correctly clean up the array for this demonstration. array_clean_up( data ); std::cout << std::endl << "After array_clean_up( data ):" << std::endl; std::cout << "data.capacity_ = " << data.capacity_ << std::endl; std::cout << "data.array_ = " << static_cast( data.array_ ) << std::endl; // These problems arise because the user is responsible // for maintaining the internal state of the object. // // We would prefer the class itself to control: // // 1. how the object is initialized, // 2. how its data is accessed and modified, and // 3. how its resources are cleaned up. // // These lead to constructors, member functions, private // member variables, and destructors. return 0; }