Generic Programming with C++
Generic Programming Generic programming a style of programming supported by C++ Similar to OOP trying to make it simple to reuse code, and abstracting general concepts Different from OOP in OOP, the emphasis is on data aspect, in Generic Programming, the emphasis is the algorithmic aspect Before we learn about generic programming, we'll need some C++ basics...
A typical C++ class (in three parts) The interface file: // in stack.h class Stack { public: // a constant static const int MAX_ELEMENTS = 16; // constructor Stack(); // destructor virtual ~Stack(); // dynamically bound functions virtual void push( int x ); virtual int pop(); // inlined function int getsize() { return size; protected: int size; int elements[max_elements]; ; Java programmers: don't forget the sneaky semi-colon at the end of the class defintion!
The implementation file... // in stack.cc #include "stack.h" Stack::Stack() : size(0) { for ( int i = 0; i < MAX_ELEMENTS; ++i ) { elements[i] = 0; Stack::~Stack() { // nothing to do void Stack::push( int x ) { // stack is full if ( size >= MAX_ELEMENTS - 1 ) return; // otherwise it's safe to push elements[size++] = x; int Stack::pop() { // stack is empty if ( size <= 0 ) return 0; // an exception would be better... int result = elements[0]; size--; for ( int i = 0; i < size; ++i ) { elements[i] = elements[i+1]; return result;
Using the Stack class in a program... // in main.cc #include <iostream> #include "stack.h" int main( int argc, char* argv[] ) { Stack* pstack = new Stack(); pstack->push( 1 ); pstack->push( 2 ); pstack->push( 3 ); int n; while ( n = pstack->pop() ) { cout << n << " " << endl; return 0; // compile with g++ main.cc stack.cc
Memory Management In Java, this task is done mostly in the background, is not the typical programmer's role This is done using an automatic garbage collector, which takes care of deallocating unused objects In C++, memory management is explicitly under the control of the programmer
Different Memory Models in Java only primitive types and references can be stack resident objects (including arrays) are always allocated on the heap in C++ objects can be allocated on the stack or the heap stack resident values are created automatically upon entering/exiting a method heap resident values are created using the new operator
Example // in Java void example() { int i; int a[] = new int[10]; obj ao = new obj(); // in C++ void example() { int i; int a[10]; obj ao; in Java only the primitive integer i is allocated space on the stack, while the array a and the object ao are created as heap resident values in C++ all three values reside on the stack
The stack The stack is an orderly internal data structure managed by a run-time system, which keeps track of: active function calls parameter values return address of caller local variables Objects exist until they go out of scope sometimes called automatic variables never return a pointer to a variable on the stack! Allocating memory on the stack is usually much faster than on the heap, though there is usually less space available.
Objects on the stack The size of stack resident values must be known at compile time no problem for primitive types and pointers array sizes must be known at compile time what about objects?
#include <iostream> The slicing problem class A { public: A(): dataone(2) { virtual void whoami() { cout << "class A" << endl; private: int dataone; ; class B: public A { public: B(): datatwo(4) { virtual void whoami() { cout << "class B" << endl; private: int datatwo; ; int main( int argc, char* argv[] ) { A a; B b; a.whoami(); b.whoami(); a = b; a.whoami(); // not who you think! return 0; when b is assigned to a, the compiler slices off the parts from B, leaving only an A type object.
The Heap (or free store) heap resident values are created with the new or new[] operator, which dynamically allocates memory C programmers: don't mix new and malloc, they are not equivalent! on failure, new will return a null pointer, or throw a bad_alloc exception access to these values is done through pointers (which will often be on the stack) C++ - the pointer declaration is explicitly stated, and has a significance (pointer arithmetic)
The delete operator there is no delete operator in Java, because the garbage collector deallocates unused objects automatically in C++ this task is left for the programmer, who does it using the delete operator on a pointer variable there are actually two delete operators: delete p: deletes the object pointed to by p delete[] p: deletes the array pointed to by p, and each object in the array pointed to by p if you used p = new Class[size] to allocate the objects, you must use delete[] p
Memory problems: using a value before it is initialized unpredictable results! failing to release memory no longer in use memory leak, can result in heap exhaustion! using a value after it has been deleted unpredictable results! invoking delete more than once on the same pointer depends on the heap manager, could corrupt the heap!
Destructors executed when an object is deleted or goes out of scope the name of the destructor is identical to the name of the class, prefixed with ~ e.g. ~Shape() can only be one destructor per class, no parameters or return type destructors for base classes must be virtual
default constructor no parameters Special Constructors needed to create arrays of objects copy constructor one parameter, a const reference to an instance of the same class e.g. Shape( const& Shape ) similar to cloning in Java (same deep vs. shallow copy issues to resolve) need a copy constructor for any class which has pointer or reference members also note: if your class has pointer or reference members, you should implement the assignment operator: e.g. operator=(const Shape& rhs);
Notes about memory management strive for symmetry between new and delete calls if you allocate memory in a constructor, deallocate that memory in the destructor try to hide dynamic memory inside classes if the object ends up on the stack, destructor will automatically ensure proper deallocation on the heap, objects help with by grouping complex collections of heap allocations together. Then you only have to remember to delete one object, rather than many. class HoldMyArray { public: int* const parray; HoldMyArray( int size ) { parray = new int[size]; ~HoldMyArray() { delete[] parray;
virtual functions a dynamically bound member function the virtual keyword is needed only in the base class declaration of the function remember, dynamic binding is needed for polymorphism. Pure virtual functions must be redefined by the derived class, and are resolved dynamically (at run-time) - dynamic binding Non-virtual member functions are resolved statically, i.e. the member function is selected statically (at compiletime) based on the type of the pointer (or reference) to the object.
Pointers Pointer a value that represents the address of an object Operators used: If x is an object, then &x is the address of x (here the & is an address operator, not a definition of reference types) If p is the address of an object, then *p is the object itself (the * is a dereference operator) If p is a pointer to a class (or a structure) p->a denotes we want to access a specific field (a) in this class If p contains the address of x, we say that p is a pointer that points to x Pointers have types int *p; //p is a pointer to int, or *p has type int
Arrays and Pointer Arithmetic C++ - a close association between a pointer and an array An array contains a sequence of one or more objects of the same type, it's size must be known at compile time The name of an array = a pointer to the initial element of the array A pointer is a random access iterator Pointer arithmetic moving along the elements of the array
Cast operations There are several cast operations that provide finegrained control over casting C style casts (using Java syntax) are not type safe! dynamic_cast<t>(v) uses RTTI to perform the cast of v to type T at run-time static_cast<t>(v) converts the value of the expression v to the type T at compile time.
References An alternative way to name an existing object Differences between pointers and references: a reference can never be null a reference cannot be changed (to point to a different object) dereferencing a reference is automatic int i = 9; int &j = i; // j is an alias, a reference for i j++; // i is now 10 i +=3; // i is now 13
Pass by Reference Parameters most common use of references in C++ allows you to avoid copying large objects, which would be very slow... class BigObject { int lotsofmemory[1000000]; // approximately 4MB void badfunc( BigObject o ) {... void frugalfunc( BigObject& o ) {... int main( int argc, char* argv[] ) { BigObject b = new BigObject(); // 4MB from the heap badfunc( b ); // 4MB more on the stack! frugalfunc( b ); // just the original 4MB on the heap
Pass by Reference, example // C++ void passtest( int& i ) { i++; i = 7; int main () { int j = 5; passtest( j ); cout << j << endl; return 0; // Java static void passtest (box i) { i.value++; i = new box(7); public static void main (String [ ] args) { box j = new box(5); passtest(j); System.out.println( J is + j.value); in C++, using pass by value, j would be 5 the same is true also for Java with primitive values in C++, using pass by reference, j is 7 in Java, using ordinary objects, j will be 6 in Java an object parameter passed to a method is really an object reference that is passed by value!