The C++ Language C++ Features
Total Page:16
File Type:pdf, Size:1020Kb
The C++ Language C++ Features Classes Bjarne Stroupstrup, the language’s creator, explains User-defined types The C++ Language C++ was designed to provide Simula’s facilities for Operator overloading 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 Function Overloading 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 T> class Stack { Macro-preprocessor-like way of providing polymorphism. T s[SIZE]; // T is a type argument Stack<char> 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<double*> dps; // Creates version for double* Standard Template Library has templates for strings, lists, s[sp++] = v; double d; 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 Multiple Inheritance 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 virtual function 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<int> v; Implemented with trees—O(log n) for ( vector<int>::iterator i = v.begin(); vector<int> v; Set of objects i != v.end() ; i++ ) { v.push back(3); // vector can behave as a stack int entry = *i; set<int, less<int> > s; v.push back(2); } s.insert(5); int j = v[0]; // operator[] defined for vector set<int, less<int> >::iterator i = s.find(3); · · · " " v.begin() v.end() Map: Associative array map<int, char*> 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 •