Wednesday 8 August 2012

Character Utilities

Character Utilities

char ch;

isalpha(ch)
Is alphabetic a..z A..Z
isupper(ch)
Is upper case
islower(ch)
Is lower case
isdigit(ch)
Is in the range 0..9
isxdigit(ch)
Is 0..9 or a..f or A..F
isspace(ch)
Is white space character (space/newline/tab)
ispunct(ch)
Is punctuation or symbolic
isalnum(ch)
Is alphanumeric (alphavetic or number)
isprint(ch)
Is printable on the screen (and space)
isgraph(ch)
If the character is printable (not space)
iscntrl(ch)
Is a control character (not printable)
isascii(ch)
Is in the range 0..127
iscsym(ch)
Is a valid character for a C identifier
toupper(ch)
Converts character to upper case
tolower(ch)
Converts character to lower case
toascii(ch)
Converts character to ascii (masks off top bit)

Character Utilities

Character Utilities

char ch;

isalpha(ch)
Is alphabetic a..z A..Z
isupper(ch)
Is upper case
islower(ch)
Is lower case
isdigit(ch)
Is in the range 0..9
isxdigit(ch)
Is 0..9 or a..f or A..F
isspace(ch)
Is white space character (space/newline/tab)
ispunct(ch)
Is punctuation or symbolic
isalnum(ch)
Is alphanumeric (alphavetic or number)
isprint(ch)
Is printable on the screen (and space)
isgraph(ch)
If the character is printable (not space)
iscntrl(ch)
Is a control character (not printable)
isascii(ch)
Is in the range 0..127
iscsym(ch)
Is a valid character for a C identifier
toupper(ch)
Converts character to upper case
tolower(ch)
Converts character to lower case
toascii(ch)
Converts character to ascii (masks off top bit)

Character Utilities

Character Utilities

char ch;

isalpha(ch)
Is alphabetic a..z A..Z
isupper(ch)
Is upper case
islower(ch)
Is lower case
isdigit(ch)
Is in the range 0..9
isxdigit(ch)
Is 0..9 or a..f or A..F
isspace(ch)
Is white space character (space/newline/tab)
ispunct(ch)
Is punctuation or symbolic
isalnum(ch)
Is alphanumeric (alphavetic or number)
isprint(ch)
Is printable on the screen (and space)
isgraph(ch)
If the character is printable (not space)
iscntrl(ch)
Is a control character (not printable)
isascii(ch)
Is in the range 0..127
iscsym(ch)
Is a valid character for a C identifier
toupper(ch)
Converts character to upper case
tolower(ch)
Converts character to lower case
toascii(ch)
Converts character to ascii (masks off top bit)

Preprocessor Directives

Preprocessor Directives

#include  include file for linking
#define   define a preprocessor symbol/macro
#undef     un-define a previously defnined symbol
#if            test for conditional compilation
#ifdef      
#ifndef
#else
#endif
#line           debug tool
#error        debug tool
 

Thursday 26 July 2012

Statements

Statements

A program consists of a collection of functions (one of which must be int main() {...}) and type and object declarations. A function may contain declarations and statements. Statements have the following forms, where s is a statement, and t is a true/false expression.
s; // Expression or declaration
; // Empty statement
{s; s;} // A block of 0 or more statements is a statement
if (t) s; // If t is true then s
if (t) s; else s; // else is optional
while (t) s; // Loop 0 or more times
for (s1; t; s2) s; // s1; while (t) {s; s2;}
break; // Jump from while, for, do, switch
return x; // Return x to calling function
try {throw x;} // Throw exception, abort if not caught, x has any type
catch (T y) {s;} // if x has type T then y=x, jump to s
catch (...) {s;} // else jump here (optional)
do s; while (t); // (uncommon) s; while (t) s;
continue; // (uncommon) Start next loop of while, for, do
switch (i) { // (uncommon) Test int expression i to const C
case C: s; break; // if (i==C) go here
default: s; // optional, else go here
}
label: goto label; // (rare) Jump to label within a function

A statement may be a declaration or an expression. Objects and types declared in a block are local to that block. (Functions cannot be defined locally). It is normal (but not required) to show statements on separate lines and to indent statements enclosed in a block. If braces are optional, we indent anyway. For instance,
{ // start of block
int a[10], i=0, j; // declaration
a[i+2]=3; // expression
} // end of block, a, i, and j are destroyed
declares the array of int a with elements a[0] through a[9] (whose values are initially undefined), i with initial value 0, and j with an undefined initial value. These names can only be used in scope, which is from the declaration to the closing brace.

The for loop is normally used for iteration. For instance, the following both exit the loop with i set to the index of the first element of a such that a[i] is 0, or to 10 if not found.
for (i=0; i<10; i=i+1) { i=0;
if (a[i]==0) { while (i<10) {
break; if (a[i]==0)
} break;
} i=i+1;
}
The braces in the for loop are optional because they each enclose a single statement. In the while loop, the outer braces are required because they enclose 2 statements. All statements are optional: for (;;) loops forever. The first statement in a for loop may declare a variable local to the loop. for (int i=0; i<10; i=i+1)

It is only possible to break from the innermost loop of a nested loop. continue in a for loop skips the rest of the block but executes the iteration (s2) and test before starting the next loop.

return x; causes the current function to return to the caller, evaluating to x. It is required except in functions returning void, in which case return; returns without a value. The value returned by main() has no effect on program behavior and is normally discarded. However it is available as the $status in a UNIX csh script or ERRORLEVEL in a Windows .BAT file.
int sum(int x, int y) { // Function definition
return x+y;
}
int main() {
int a=sum(1,2); // a=3;
return 0; // By convention, nonzero indicates an error
}

A test of several alternatives usually has the form if (t) s; else if (t) s; else if (t) s; ... else s;. A switch statement is an optimization for the special case where an int expression is tested against a small range of constant values. The following are equivalent:
switch (i) { if (i==1)
case 1: j=1; break; j=1;
case 2: // fall thru else if (i==2 || i==3) // || means "or else"
case 3: j=23; break; j=23;
default: j=0; else
} j=0;

throw x jumps to the first catch statement of the most recently executed try block where the parameter declaration matches the type of x, or a type that x can be converted to, or is .... At most one catch block is executed. If no matching catch block is found, the program aborts (Unexpected exception). throw; with no expression in a catch block throws the exception just caught. Exceptions are generally used when it is inconvenient to detect and handle an error in the same place.
void f() {
throw 3;
}

int main() {
try {
f();
}
catch(int i) { // Execute this block with i = 3
throw; // throw 3 (not caught, so program aborts)
}
catch(...) { // Catch any other type
}
}

Creating Libraries (namespaces)

Creating Libraries (namespaces)



Libraries usually come in the form of a header and an object (.o) file. To use them, #include "header.h" and link the .o file using g++. If the .o was compiled in C rather than C++, then indicate this with extern "C" {} to turn off name mangling. C++ encodes or "mangles" overloaded function names to allow them to be linked, but C does not since it doesn't allow overloading. extern "C" { // Turn off name mangling
#include "header.h" // Written in C
}
When writing your own library, use a unique namespace name to prevent conflicts with other libraries. A namespace may span multiple files. Types, objects, and functions declared in a namespace N must be prefixed with N:: when used outside the namespace, or there must be a using namespace N; in the current scope.
Also, to guard against possible multiple inclusions of the header file, #define some symbol and test for it with #ifndef ... #endif on the first and last lines. Don't have a using namespace std;, since the user may not want std visible.
#ifndef MYLIB_H // mylib.h, or use #if !defined(MYLIB_H)
#define MYLIB_H
#include <string>
// No using statement
namespace mylib {
class B {
public:
std::string f(); // No code
}
}
#endif

// mylib.cpp, becomes mylib.o
#include <string>
#include "mylib.h"
using namespace std; // OK
namespace mylib {
string B::f() {return "hi";}
}
#define could be used to create constants through text substitution, but it is better to use const to allow type checking. #define X Y has the effect of replacing symbol X with arbitrary text Y before compiling, equivalent to the g++ option -DX=Y. Each compiler usually defines a different set of symbols, which can be tested with #if, #ifdef, #ifndef, #elsif, #else, and #endif. #ifdef unix // Defined by most UNIX compilers
// ...
#else
// ...
#endif
Preprocessor statements are one line (no semicolon). They perform text substitutions in the source code prior to compiling. #include <header> // Standard header
#include "header.h" // Include header file from current directory
#define X Y // Replace X with Y in source code
#define f(a,b) a##b // Replace f(1,2) with 12
#define X \ // Continue a # statement on next line
#ifdef X // True if X is #defined
#ifndef X // False if X is #defined
#if !defined(X) // Same
#else // Optional after #if...
#endif // Required

Polymorphism

Polymorphism



Polymorphism is the technique of defining a common interface for a hierarchy of classes. To support this, a derived object is allowed wherever a base object is expected. For example, String s="Hello";
Vector<char> v=s; // Discards derived part of s to convert
Vector<char>* p=&s; // p points to base part of s
try {throw s;} catch(Vector<char> x) {} // Caught with x set to base part of s
s=Vector<char>(5); // Error, can't convert base to derived

// Allow output of Vector<char> using normal notation
ostream& operator << (ostream& out, const Vector<char>& v) {
copy(v.begin(), v.end(), ostream_iterator<char>(out, "")); // Print v to out
return out; // To allow (cout << a) << b;
}
cout << s; // OK, v refers to base part of s
ofstream f("file.txt");
f << s; // OK, ofstream is derived from ostream

A derived class may redefine inherited member functions, overriding any function with the same name, parameters, return type, and const-ness (and hiding other functions with the same name, thus the overriding function should not be overloaded). The function call is resolved at compile time. This is incorrect in case of a base pointer or reference to a derived object. To allow run time resolution, the base member function should be declared virtual. Since the default destructor is not virtual, a virtual destructor should be added to the base class. If empty, no copy constructor or assignment operator is required. Constructors and = are never virtual.
class Shape {
public:
virtual void draw() const;
virtual ~Shape() {}
};
class Circle: public Shape {
public:
void draw() const; // Must use same parameters, return type, and const
};

Shape s; s.draw(); // Shape::draw()
Circle c; c.draw(); // Circle::draw()
Shape& r=c; r.draw(); // Circle::draw() if virtual
Shape* p=&c; p->draw(); // Circle::draw() if virtual
p=new Circle; p->draw(); // Circle::draw() if virtual
delete p; // Circle::~Circle() if virtual

An abstract base class defines an interface for one or more derived classes, which are allowed to instantiate objects. Abstractness can be enforced by using protected (not private) constructors or using pure virtual member functions, which must be overridden in the derived class or else that class is abstract too. A pure virtual member function is declared =0; and has no code defined.
class Shape {
protected:
Shape(); // Optional, but default would be public
public:
virtual void draw() const = 0; // Pure virtual, no definition
virtual ~Shape() {}
};
// Circle as before

Shape s; // Error, protected constructor, no Shape::draw()
Circle c; // OK
Shape& r=c; r.draw(); // OK, Circle::draw()
Shape* p=new Circle(); // OK
Run time type identification
C++ provides for run time type identification, although this usually indicates a poor design. dynamic_cast<T>(x) checks at run time whether a base pointer or reference is to a derived object, and if so, does a conversion. The base class must have at least one virtual function to use run time type checking. #include <typeinfo> // For typeid()
typeid(*p)==typeid(T) // true if p points to a T
dynamic_cast<T*>(p) // Convert base pointer to derived T* or 0.
dynamic_cast<T&>(r) // Convert base reference to derived T& or throw bad_cast()
For example, class B {public: virtual void f(){}};
class D: public B {public: int x;} d; // Bad design, public member in D but not B
B* p=&d; p->x; // Error, no B::x
D* q=p; q->x; // Error, can't convert B* to D*
q=(D*)p; q->x; // OK, but reinterpret_cast, no run time check
q=dynamic_cast<D*>(p); if (q) q->x; // OK