The C++ Language C++ Features

Classes Bjarne Stroupstrup, the language’s creator, explains User-defined types The C++ Language C++ was designed to provide ’s facilities for program organization together with C’s efficiency and COMS W4995-02 Attach different meaning to expressions such as a + b flexibility for systems programming. References Prof. Stephen A. Edwards Pass-by-reference function arguments Fall 2002 Columbia University Virtual Functions Department of Computer Science Dispatched depending on type at run time Templates Macro-like polymorphism for containers (e.g., arrays) Exceptions More elegant error handling

Implementing Classes Operator Overloading Complex Number Type

class Complex { Simple without virtual functions. For manipulating user-defined double re, im; “numeric” types C++ Equivalent C public: class Stack { struct Stack { complex(double); // used, e.g., in c1 + 2.3 complex c1(1, 5.3), c2(5); // Create objects char s[SIZE]; char s[SIZE]; complex(double, double); int sp; int sp; complex c3 = c1 + c2; // + means complex plus public: }; c3 = c3 + 2.3; // 2.3 promoted to a complex number // Here, & means pass-by-reference: reduces copying Stack(); complex& operator+=(const complex&); void push(char); void St_Stack(Stack*); }; char pop(); void St_push(Stack*,char); }; char St_pop(Stack*);

References Const

Designed to avoid copying in overloaded operators Overloaded operators a particular Access control over variables, Especially efficient when code is inlined. case of function/method overloading arguments, and objects. A mechanism for calling functions pass-by-reference General: select specific method/operator based on name, const double pi = 3.14159265; // Compile-time constant C only has pass-by-value: fakable with explicit pointer use number, and type of arguments. int foo(const char* a) { // Constant argument void bad swap(int x, int y) { Return type not part of overloading *a = 'a'; // Illegal: a is const int tmp = x; x = y; y = tmp; void foo(int); } } void foo(int, int); // OK void foo(char *); // OK class bar { // “object not modified” int foo(char *); void swap(int &x, int &y) { // BAD int get field() const { return field; } int tmp = x; x = y; y = tmp; }; } Templates Template Stack Class Using a Template template class Stack { Macro-preprocessor-like way of providing polymorphism. T s[SIZE]; // T is a type argument Stack cs; // Creates code specialized for char Polymorphism: Using the same code for different types int sp; cs.push('a'); public: char c = cs.pop(); Mostly intended for containiner classes (vectors of Stack() { sp = 0; } integers, doubles, etc.) void push(T v) { if (sp == SIZE) error("overflow"); Stack dps; // Creates version for double* Standard Template Library has templates for strings, lists, s[sp++] = v; double ; vectors, hash tables, trees, etc. } dps.push(&d); T pop() { if (sp == () error("underflow"); return s[--sp]; } };

Implementing Inheritance Virtual Functions Virtual Functions

class Shape { Simple: Add new fields to end of the object The Trick: Add a “virtual table” pointer to each object. virtual void draw(); // Invoked by object’s class struct A { Fields in base class always at same offset in derived class }; // not its compile-time type. A’s Vtbl B’s Vtbl class Line : public Shape { int x; Consequence: Derived classes can never remove fields void draw(); virtual void Foo(); A::Foo B::Foo C++ Equivalent C }; virtual void Bar(); A::Bar A::Bar class Arc : public Shape { class Shape { struct Shape { }; void draw(); a1 B::Baz double x, y; double x, y; }; struct B : A { vptr }; }; int y; Shape *s[10]; virtual void Foo(); x b1 s[0] = new Line; class Box : Shape { struct Box { virtual void Baz(); vptr s[1] = new Arc; a2 double h, w; double x, y; }; s[0]->draw(); // Invoke Line::draw() vptr x }; double h, w; s[1]->draw(); // Invoke Arc::draw() x y }; A a1, a2; B b1;

Virtual Functions Virtual Functions struct A { struct A { Rocket Science, int x; int x; and nearly as dangerous B’s Vtbl B’s Vtbl virtual void Foo(); virtual void Foo(); Inherit from two or more classes virtual void Bar() B::Foo virtual void Bar(); B::Foo { do something(); } A::Bar }; A::Bar class Window { ... }; }; B::Baz struct B : A { B::Baz class Border { ... }; struct B : A { int y; int y; virtual void Foo() class BWindow : public Window, virtual void Foo(); *a { something else(); } *a public Border { virtual void Baz(); vptr virtual void Baz(); vptr . }; x }; x . A *a = new B; A *a = new B; }; y y a->Bar(); a->Foo(); Multiple Inheritance Ambiguities Resolving Ambiguities Explicitly Duplicate Base Classes class Window { class Window { void draw(); }; A class may be inherited more than once void draw(); class Drawable { ... }; }; class Border { void draw(); }; class Window : public Drawable { ... }; class Border : public Drawable { ... }; class Border { class BWindow : public Window, class BWindow : public Window, public void draw(); // OK public Border { Border { ... }; }; void draw() { Window::draw(); } }; class BWindow : public Window, BWindow gets two copies of the Drawable base class. public Border { }; BWindow bw; bw.draw(); // OK BWindow bw; bw.draw(); // Compile-time error: ambiguous

Virtual Base Classes Implementing Multiple Inheritance Implementation using Offsets

struct A { int x; virtual void f(); } Virtual base classes are inherited at most once A expects a pointer to its object struct B { int y; virtual void f(); class Drawable { ... }; struct A { int x; virtual void f(); } virtual void g(); } class Window : public virtual Drawable { struct B { int y; virtual void f(); } struct C : A, B { int z; void f(); } ... }; struct C : A, B { int z; void f(); } B *b = new C; class Border : public virtual Drawable { b->f(); // Call C::f() ... }; B *obj = new C; “this” expected by C::f()→ x class BWindow : public Window, public b->f(); // Calls C::f() B* obj→ y C’s Virtual Tbl Border { ... }; this→ vptr z &C::f 0 adjust from offset x b→ vptr B in C’s V. Tbl BWindow gets two copies of the Drawable base class “obj” is, by definition, a pointer to a B, not a C. Pointer y &C::f −2 must be adjusted depending on the actual type of the z &B::g 0 object. At least two ways to do this.

Implementation using Thunks Offsets vs. Thunks Exceptions struct A { int x; virtual void f(); } A high-level replacement struct B { int y; virtual void f(); Offsets Thunks for C’s setjmp/longjmp. virtual void g(); } struct Except { }; struct C : A, B { int z; void f(); } Offsets to virtual tables Helper functions B *b = new C; Can be implemented in C Needs “extra” semantics void baz() { throw Except; } b->f(); // Call C::f() All virtual functions cost more Only multiply-inherited functions cost void bar() { baz(); } Tricky Very Tricky void void foo() { C vtbl C::f in B(void *this) try { this→ vptr &C::f x { bar(); } catch (Except e) { b→ vptr this = this - 2; B in C’s vtbl y printf("oops"); &C::f in B goto C::f; z } &B::g } } One Way to Implement Exceptions Implementing Exceptions Cleverly Standard Template Library try { push(Ex, Handler); Real question is the nearest handler for a given PC. I/O Facilities

throw Ex; throw(Ex); Lines Action iostream, fstream pop(); 1 void foo() { 1–2 Reraise Garbage-collected String class goto Exit; 2 3. look in table } catch (Ex e) { Handler: 3 try { 3–5 H1 Containers foo(); foo(); 4 bar(); 5 } catch (Ex1 e) { H1: a(); } 6–9 Reraise } Exit: 6 vector, list, queue, stack, map, set 7 } 2. H2 doesn’t handle Ex1, reraise 10–12 H2 Numerical push() adds a handler to a stack 8 void bar() { 9 1. look in table 13–14 Reraise complex, valarray pop() removes a handler 10 try { 11 throw Ex1(); General algorithms throw() finds first matching handler 12 } catch (Ex2 e) { H2: b(); } 13 search, sort 14 } Problem: imposes overhead even with no exceptions

C++ I/O C++ I/O C++ String Class

C’s printing facility is clever but not type safe. Easily extended to print user-defined types Provides automatic garbage collection, usually by char *s; int d; double g; ostream & reference counting. printf("%s %d %g", s, d, g); operator <<(ostream &o, MyType &m) { o << "An Object of MyType"; string s1, s2; return o; s1 = "Hello"; Hard for compiler to typecheck argument types against } s2 = "There"; format string. s1 += " goodbye"; C++ overloads the << and >> operators. This is type safe. Input overloads the >> operator s1 = ""; // Frees memory holding “Hello goodbye” cout << 's' << ' ' << d << ' ' << g; int read integer; cin >> read integer;

C++ STL Containers Iterators Associative Containers

Vector: dynamically growing and shrinking array of Mechanism for stepping through containers Keys must be totally ordered elements. vector v; Implemented with trees—O(log n) for ( vector::iterator i = v.begin(); vector v; Set of objects i != v.end() ; i++ ) { v.push back(3); // vector can behave as a stack int entry = *i; set > s; v.push back(2); } s.insert(5); int j = v[0]; // operator[] defined for vector set >::iterator i = s.find(3); · · · ↑ ↑ v.begin() v.end() Map: Associative array map m; m[3] = "example"; C++ In Embedded Systems C++ Features With No Impact Inexpensive C++ Features

Dangers of using C++: Classes Default arguments

No or bad compiler for your particular processor • Fancy way to describe functions and structs • Compiler adds code at call site to set default Increased code size • Equivalent to writing object-oriented C code arguments Slower program execution Single inheritance • Long argument lists costly in C and C++ anyway • More compact way to write larger structures Constructors and destructors

Much harder language to compile Function name overloading • Function call overhead when an object comes into Unoptimized C++ code can be larger & slower than • Completely resolved at compile time (normal case) equivalent C • Namespaces Extra code inserted when object comes into scope (inlined case) • Completely resolved at compile time

Medium-cost Features High-cost Features High-cost Features

Virtual functions Multiple inheritance Exceptions • Extra level of indirection for each virtual function call • Makes objects much larger (multiple virtual pointers) • Typical implementation: • Each object contains an extra pointer • Virtual tables larger, more complicated • When exception is thrown, look up stack until handler References is found and destroy automatic objects on the way • Often implemented with pointers • Calling virtual functions even slower • Mere presence of exceptions does not slow program • Extra level of indirection in accessing data Templates • • Often requires extra tables or code to direct clean-up Can disappear with inline functions • Compiler generates separate code for each copy Inline functions • Throwing and exception often very slow • Can greatly increase code sizes • Can greatly increase code size for large functions • No performance penalty • Usually speeds execution

High-cost Features The bottom line

Much of the standard template library C still generates better code

• Uses templates: often generates lots of code Easy to generate larger C++ executables

• Very dynamic data structures have high Harder to generate slower C++ executables memory-management overhead Exceptions most worrisome feature

• Easy to inadvertently copy large data structures • Consumes space without you asking

• GCC compiler has a flag to enable/disable exception support -fexceptions and -fno-exceptions