530 likes | 556 Views
Advanced Program Design with C++. Part 2: Data types. Data types. Simple data types Pointers Type checking Type coercion. Data types . Highly similar to Java data types Basic types are not classes (like Java)
E N D
Advanced Program Design with C++ Part 2: Data types Joey Paquet, 2007-2019
Data types Simple data types Pointers Type checking Type coercion Joey Paquet, 2007-2019
Data types • Highly similar to Java data types • Basic types are not classes (like Java) • Pit trap: different compilers will have different ranges for most basic data types • Some programs potentially will behave differently across different platforms • Hence, lack of portability of C++ programs • User-defined data types using struct(as in C), as well as class(object-oriented programming) • Both are allowed in the same program • In fact, they are almost equivalent, but struct was kept for backward compatibility • A struct can have data members, methods, constructors, destructors, etc • One difference is that a struct sets its members as public by default Joey Paquet, 2007-2019
Data types: simple types size, range and precision Joey Paquet, 2007-2019
Data types: simple types size, range and precision Joey Paquet, 2007-2019
variable declarations Joey Paquet, 2007-2019
Variable declaration • A variable can be declared for any type valid in the current scope. int x; double y; myClass mc; • Multiple variables of the same type can be declared on the same declaration: intx,y,z; • Any declared name can be referred to thereafter in the scope in which it is declared. Joey Paquet, 2007-2019
Variable declaration • Declarations can include an optional initialization, which can use different syntactical forms: Type a1 {v}; Type a2 = {v}; Type a3 = v; Type a4(v); • All of these are widely used and apparently equivalent. • However: • Some are restricted to use in certain situations. • Only the first one is universally usable, and is actually safer, as it implicitly does some checking of the value passed versus the specified type. int a1 = 1.5; //allowed using truncation int a1 {1.5}; //not allowed, as truncation would happen Joey Paquet, 2007-2019
Data types: type checking and type coercion • C++ uses a manifest typing strategy • Variables and values are assigned types explicitly in the source code • Values can only be assigned to variables declared as having the same type • However, C++ allows type coercion, i.e. implicitly or explicitly changing the type of variables or values • This loophole, among other things, makes C++ a weakly typed language • Type mismatches • General Rule: Cannot place value of one type into variable of another type intvar = 2.99; // 2 is assigned to var! • Only the integer part "fits", so that’s all that goes • Called "implicit type casting" or "automatic type conversion" • When using pointers or classes, much more problematic! Joey Paquet, 2007-2019
literals Joey Paquet, 2007-2019
Data types: literals • Literals • 2, 5.75, ‘Z’, "Hello World“ • Considered "constants": can’t change in program • All literals have an inherent type that can be determined during lexical analysis • Like many other languages, C++ uses escape sequences for string literals: Joey Paquet, 2007-2019
pointers Joey Paquet, 2007-2019
Data types: pointer variables • Variables contain a specific value, e.g., an integer. • A pointer variable contains the memory address of a portion of memory that in turn contains a specific value. • For any type T, T* is the type “pointer to T”, i.e. a variable of type T* can hold the address of an object of type T. inti = 99; int* p = &i; cout << *p << endl; • Two operators on pointers: • Dereferencing operator: *, e.g. *p refers to the object pointed to by the pointer. • Address operator: &, e.g. &irefers to the address of the first memory cell holding a value. 99 i (int) &i p (*int) Joey Paquet, 2007-2019
Data types: pointer variables • Consider: int *p1, *p2, v1, v2; • p1 and p2 are now uninitialized pointers (often called “wild pointers”). • They point to a memory cell whose address corresponds to the value that was already in their cell before allocation. • Pointer assignment: p1 = &v1; • Sets pointer variable p1 to "point to" variable v1 • "p1 equals address of v1" • Or "p1 points to v1“ Joey Paquet, 2007-2019
Data types: pointer variables • Value assignments v1 = 0; *p1 = 42; • p1 and v1 refer to same memory cell • Changing either the value pointed to by p1 or the value of v1 also changes the other variable’s value. Joey Paquet, 2007-2019
Data types: pointer variables • Pointer assignment vs value assignment: int v1 = 42; int v2 = 9; int *p2 = &v2; int *p1 = &v1; • Pointer assignment: p2 = p1; • Assigns one pointer to another • "Make p2 point to where p1 points“ • Value assignment: *p2 = *p1; • Assigns "value pointed to" by p1, to "valuepointed to" by p2 Joey Paquet, 2007-2019
Data types: pointer variables • Dynamic variables • Require the use of pointers • Allocated with new operator, deallocated with the delete operator • Need to be allocated and destroyed explicitly while program runs • C++ does not have a garbage collector • Local variables • Declared within a function definition • Not dynamic • Allocated on the stack when code block is entered (e.g. function call) • Destroyed when code block is exited (e.g. function call completes) • Often called "automatic" variables • Allocation and deallocation controlled by the runtime system as functions are called • Uses a function call stack mechanism to automatically manage memory allocation/deallocation • If pointers are declared in a function • The pointer is managed as a local variable • The value pointed to is dynamically allocated/deallocated Joey Paquet, 2007-2019
Dynamic/automatic memory allocation: example int global = 99; int f2(){ int f2x = 3; return (global/f2x); } int f1(int p){ // calling this function // creates a memory leak int* f1x = new int(10); return (p + *f1x + f2()); } int main(){ int x,y,z; y = 1; z = 2; for (int i = 1; i <= 10; i++){ x = f1(y); } } Joey Paquet, 2007-2019
Data types: pointer variables • The operator new creates dynamically allocated values that can then be pointed to by pointer variables. • The value created is a nameless pointer value. • Allocated on the heap (also known as freestore) through the runtime system’s interaction with the operating system. • All dynamically allocated variables need to be carefully managed by the programmer. • C++ does not have garbage collection. • Dynamically allocated variables need to be allocated and deallocated manually • Similar to C’s mallocandfree Joey Paquet, 2007-2019
Data types: pointer variables int *p1, *p2; p1 = new int; *p1 = 42; p2 = p1; Joey Paquet, 2007-2019
Data types: pointer variables *p2 = 53; p1 = new int; *p1 = 88; Joey Paquet, 2007-2019
Data types: pointer variables • If the type used as parameter is of class type: • Constructor is called for new object • Can invoke different constructor with initializer arguments: MyClass *myPtr;myPtr = new MyClass(32.0, 17); • Can still initialize non-class types: int *n; n = new int(17);//Initializes *n to 17 //That does NOT mean int is a class Joey Paquet, 2007-2019
Data types: pointer variables • Pointers are full-fledged types • Can be used just like other types • Can be function parameters • Can be returned from functions • Example: int* findOtherPointer(int* p); • This function declaration: • Has "pointer to an int" parameter • Returns "pointer to an int" Joey Paquet, 2007-2019
Data types: pointer variables • Potential problem if freestore runs out of memory • Older compilers: • Test if null returned by call to new:int *p;p = new int;if (p == NULL){ cout << "Error: Insufficient memory.\n"; exit(1);} • Most contemporary C++ compilers (C++98 and after) : • newthrows exception bad_alloc try { int * myarray= new int[1000]; } catch (bad_alloc&) { cout << "Error allocating memory." << endl; } Joey Paquet, 2007-2019
Data types: pointer variables • To deallocate dynamic memory, use the delete operator • When value no longer needed • Returns memory to freestore • Example:int *p;p = new int(5); //allocate memory… //Some processing…delete p; //deallocate memoryp = NULL; //prevents dangling pointer errors • Deallocates dynamic memory "pointed to by pointer p“ • p is then a “dangling pointer” that still points to its previously allocated value. • If not deleted before the variable goes out of scope, memory is not freed, which creates a memory leak. • Plus, dereferencing a dangling pointer leads to unpredictable results, ranging from getting a seemingly random value to a program crash. • Managing dangling pointers and deallocating dynamically allocated memory is a very important aspect of proper C++ programming. Joey Paquet, 2007-2019
pointer arithmetic Joey Paquet, 2007-2019
Pointer arithmetic • Can perform arithmetic operations on pointers • Used to navigate arrays (covered later) • Example:int *d; d = new int[10]; • d refers to: address of new int[10] • d + 1refers to: address of new int[10] + 1*sizeof(int) • d + 2refers to: address of new int[10] + 2*sizeof(int) • d[i] == *(&d[0]+i) == *(d+i) Joey Paquet, 2007-2019
references Joey Paquet, 2007-2019
Reference • Pointers are very powerful, as they allow: • A variable to refer a value held by another variable. • A variable to refer to different values held by different variables in time. • Pass information around without having to copy it. • However, due to their power, pointers bring additional complexities: • Must use a special syntax (*, &, ->) • Possibility of dangling pointers, wild pointers, null pointers. • Pointers are unsafe. • References are pointer variables that eliminate some of the disadvantages of pointers, at the cost of eliminating some of their power. • Pointer arithmetic cannot be applied to a reference. • Any operation applied to a reference is actually applied onto the variable it refers to, including assignment. • Hence, references must be initialized upon declaration and cannot be changed afterwards. • Furthermore, given a reference int& r {v1}, &r returns a pointer to the object referred to by r. Thus, we cannot even have a pointer to a reference. Joey Paquet, 2007-2019
smart pointers Joey Paquet, 2007-2019
Smart pointers • Pointers are a very good example of a powerful C++ feature that is dangerous to use. • Very powerful and lean tool to use. • But leads to: • Memory leaks: if a pointer is declared in a function and not deleted, the value that is pointed to is not freed. • Dangling pointers: if a pointer is deleted, the pointer still points to the freed memory block. • Solution: use smart pointers • Reduce bugs • Retain similar efficiency and syntax • Control memory management by methods • auto_ptr(now deprecated), unique_ptr, shared_ptr, weak_ptr • Defined in the <memory> library Joey Paquet, 2007-2019
Smart pointers • Smart pointers are conceptually the same as pointers. • Implemented as a template class: • Class that contains a pointer of a variable type. • Implements the *, ->, = operators, constructor and destructor. • Classes, templates and explicit constructors will be explained later. template <class T> class auto_ptr { T* ptr; public: explicit auto_ptr(T* p = 0) : ptr(p) {} ~auto_ptr() {delete ptr;} T& operator*() {return *ptr;} T* operator->() {return ptr;} }; Joey Paquet, 2007-2019
Smart pointers Therefore instead of writing… The programmer writes… void foo(){ MyClass* p(new MyClass); p->DoSomething(); delete p; } void foo(){ auto_ptr<MyClass> p(new MyClass); p->DoSomething(); } Joey Paquet, 2007-2019
Smart pointers • Here is an sample of code which illustrates the situation of a dangling pointer: • Problem: p and q are both pointing at the same address space, which is problematic. MyClass* p(new MyClass); MyClass* q = p; delete p; p->DoSomething(); // Watch out! p is now dangling! p = NULL; // p is no longer dangling q->DoSomething(); // Ouch! q is still dangling! Joey Paquet, 2007-2019
Smart pointers • For auto_ptr, this is solved by setting its pointer to NULL when it is copied: • This implementation prevents a memory space from being pointed to by two different auto_ptr. • But maybe we wanted that possibility. Using smart pointers brings its limitations. template <class T> auto_ptr<T>& auto_ptr<T>::operator=(auto_ptr<T>& rhs){ if (this != &rhs) { delete ptr; ptr = rhs.ptr; rhs.ptr = NULL; } return *this; } Joey Paquet, 2007-2019
parameter passing Joey Paquet, 2007-2019
Parameter passing • Parameters can be passed to a function: • By value • A copy of the value is made, then the copied value is passed to the function. • For objects, the copy constructor is called by the runtime system to make the copy. • Thus, the value in the function is local to the function. • Changing it in the function does not change the value passed from the calling function. • By pointer • A copy of the pointer is made, then passed to the function. • Thus, both functions are pointing to the same value; no copy of the value pointed to is made. • Changing the value in the called function will change the value in the calling function. • By reference • Conceptually same as pass by pointer, except that the called function cannot change where the received pointer is pointing. • Drawback: cannot pass NULL , as a reference cannot be NULL • Advantage: can accept unnamed values resulting from the evaluation of expressions as parameters: void f(const T& t); called as f(T(a, b, c)), or f(a+b) • Call by constant reference is very often used to save memory consumption to pass parameters that are not to be changed locally. Joey Paquet, 2007-2019
Paremeter passing: simple example #include <iostream> using std::cout; using std::endl; intmain() { intn = 100; cout<< "= = = = = = = = = = = = = = = = =" << endl; cout << "integer n: " << n << endl; cout<< "= = = = = = = = = = = = = = = = =" << endl; pass_by_value(n); pass_by_reference(n); pass_by_pointer(&n); return 0; } integer n: 100 address of n in main: 0x7ffeee9f14c8 = = = = = = = = = = = = = = = = = pass by value value of n: 100 address of n: 0x7ffeee9f145c = = = = = = = = = = = = = = = = = pass by reference value of n: 100 address of n: 0x7ffeee9f14c8 = = = = = = = = = = = = = = = = = pass by pointer value of n: 100 address of n: 0x7ffeee9f14c8 void pass_by_value(intn) { cout<< "= = = = = = = = = = = = = = = = =" << endl; cout<< "pass by value" << endl; cout<< "value of n: " << n << endl; cout<< "address of n: " << &n << endl; } void pass_by_reference(int&n) { cout<< "= = = = = = = = = = = = = = = = =" << endl; cout<< "pass by reference" << endl; cout<< "value of n: " << n << endl; cout<< "address of n: " << &n << endl; } void pass_by_pointer(int*n) { cout<< "= = = = = = = = = = = = = = = = =" << endl; cout<< "pass by pointer" << endl; cout<< "value of n: " << *n << endl; cout<< "address of n: " << n << endl; } Joey Paquet, 2007-2019
type casting Joey Paquet, 2007-2019
Data types: explicit type casting • C++ provides operators for explicit type coercion, or type casting static_cast<double>(intVar) • Explicitly "casts" intVarto double type doubleVar = static_cast<double>(intVar1/intVar2); • Casting forces double-precision division to take place among two integer variables. • Equivalent in meaning to the following C syntax, even though the C++ cast operation is checked at compile time and is thus less prone to runtime errors doubleVar = (double)intVar1/intVar2; Joey Paquet, 2007-2019
Data types: explicit type casting • Different kinds of explicit type casting operations: • static_cast<Type>(expression) • General-purpose type casting • const_cast<Type>(expression) • Cast-out “constantness” • dynamic_cast<Type>(expression) • Runtime-checked conversion of pointers and references within a single class hierarchy. Used for downcasting from a superclass to a subclass • reinterpret_cast<Type>(expression) • Implementation-dependent casting, performs a binary copy and assigns the new type to the resulting binary copied value. Highly unsafe and error-prone. Joey Paquet, 2007-2019
Data types: upcasting and downcasting • When dealing with classes and subclasses, one can declare objects of a supertype and manipulate them as one of its subclasses • Problem: subclass members are undefined in superclass GeometricObject ---------------------- area perimeter void displayGeometricObject(GeometricObject& g) { cout << "The radius is " << g.getRadius() << endl; cout << "The diameter is " << g.getDiameter() << endl; cout << "The width is " << g.getWidth() << endl; cout << "The height is " << g.getHeight() << endl; cout << "The area is " << g.getArea() << endl; cout << "The perimeter is " << g.getPerimeter() << endl; } Circle ------------ radius diameter Rectangle ------------- width height Joey Paquet, 2007-2019
Data types: upcasting and downcasting • May want to use static_cast: • This successfully compiles, but will fail at runtime if the object passed was originally of a type that does not contain the members referred to in the code. • static_cast makes a static (compile-time) type cast, but correct runtime behavior cannot be verified. void displayGeometricObject(GeometricObject& g) { GeometricObject* p = &g; cout << "The radius is " << static_cast<Circle*>(p)->getRadius() << endl; cout << "The diameter is " << static_cast<Circle*>(p)->getDiameter() << endl; cout << "The width is " << static_cast<Rectangle*>(p)->getWidth() << endl; cout << "The height is " << static_cast<Rectangle*>(p)->getHeight() << endl; cout << "The area is " << g.getArea() << endl; cout << "The perimeter is " << g.getPerimeter() << endl; } Joey Paquet, 2007-2019
Data types: upcasting and downcasting • Use dynamic_cast to downcast into a subclass • dynamic_cast works on pointers • Does runtime checking to verify that the cast is successful • Also deals with polymorphic types and virtual methods at runtime Joey Paquet, 2007-2019
strings Joey Paquet, 2007-2019
Strings • C++ provides following two types of string representations: • C-style character strings • string class type introduced with Standard C++ • Many libraries would define their own string type • You will encounter many different ways of declaring/manipulating strings Joey Paquet, 2007-2019
C-style character strings • With C-style character strings, strings are arrays of characters terminated by a null character (‘\0’) char greeting[6] = {'H', 'e', 'l', 'l', 'o', '\0'}; char greeting[] = "Hello"; • <cstring> provides many string manipulation functions • strcpy(s1,s2): copy string s2 into string s1 • strcat(s1,s2): concatenate string s2 onto the end of string s1 • strlen(s1): returns the length of string s1 • strcmp(s1,s2): returns lexical distance between s1 and s2 • strchr(s1,ch): returns a pointer to the first occurrence of character ch in s1 • strstr(s1,s2): returns a pointer to the first occurrence of string s2 in string s1 Joey Paquet, 2007-2019
Strings: example of C-style strings #include <iostream> #include <cstring> using namespace std; int main (){ char str1[10] = "Hello"; char str2[10] = "World"; char str3[10]; int len ; // copy str1 into str3 strcpy(str3, str1); cout << "strcpy(str3, str1) : " << str3 << endl; // concatenates str1 and str2 strcat(str1, str2); cout << "strcat(str1, str2): " << str1 << endl; // total lenghth of str1 after concatenation len = strlen(str1); cout << "strlen(str1) : " << len << endl; return 0; } Joey Paquet, 2007-2019
Standard C++ string class • The <string> standard C++ library provides a string class • Provides all the operations mentioned above, using a more natural object-oriented style. Joey Paquet, 2007-2019
Standard C++ string class Joey Paquet, 2007-2019