CS 242 History

C++ ‹C++ is an object-oriented extension of C ‹C was designed by Dennis Ritchie at Bell Labs • used to write Unix • based on BCPL John Mitchell ‹C++ designed by Bjarne Stroustrup at Bell Labs • His original interest at Bell was research on simulation • Early extensions to C are based primarily on • Called “C with classes” in early 1980’s • Popularity increased in late 1980’s and early 1990’s • Features were added incrementally Classes, templates, exceptions, , type tests...

Design Goals How successful?

‹Provide object-oriented features in C-based ‹Given the design goals and constraints, language, without compromising efficiency • this is a very well-designed language • Backwards compatibility with C ‹Many users -- tremendous popular success • Better static type checking ‹However, very complicated design • Data abstraction • Many specific properties with complex behavior • Objects and classes • Difficult to predict from basic principles • Prefer efficiency of compiled code where possible • Most serious users chose subset of language ‹Important principle – Full language is complex and unpredictable • If you do not use a feature, your compiled code • Many implementation-dependent properties should be as efficient as if the language did not • Language for adventure game fans include the feature. (compare to Smalltalk)

Significant constraints Overview of C++

‹C has specific machine model ‹Additions and changes not related to objects • Access to underlying architecture • type bool ‹No garbage collection • pass-by-reference • Consistent with goal of efficiency • user-defined overloading • Need to manage object memory explicitly • function templates ‹Local variables stored in activation records • … • Objects treated as generalization of structs, so some objects may be allocated on stack • Stack/heap difference is visible to programmer

1 C++ Object System Some good decisions

‹Object-oriented features ‹Public, private, protected levels of visibility • Classes • Public: visible everywhere • Objects, with dynamic lookup of virtual functions • Protected: within class and subclass declarations • Inheritance • Private: visible only in class where declared – Single and multiple inheritance ‹Friend functions and classes – Public and private base classes • Careful attention to visibility and data abstraction • – Tied to inheritance mechanism ‹Allow inheritance without subtyping • Encapsulation • Better control of subtyping than without private base classes

Some problem areas Sample class: one-dimen. points

‹ Casts class Pt { • Sometimes no-op, sometimes not (esp multiple inher) public: ‹ Lack of garbage collection Pt(int xv); • Memory management is error prone Overloaded constructor – Constructors, destructors are helpful though Pt(Pt* pv); ‹ Objects allocated on stack int getX(); Public read access to private data • Better efficiency, interaction with exceptions virtual void move(int dx); Virtual function • BUT assignment works badly, possible dangling ptrs protected: ‹ Overloading void setX(int xv); Protected write access • Too many code selection mechanisms private: ‹ Multiple inheritance int x; Private member data • Efforts at efficiency lead to complicated behavior };

Virtual functions Sample derived class

‹Member functions are either class ColorPt: public Pt { Public base class gives supertype • Virtual, if explicitly declared or inherited as virtual public: ColorPt(int xv,int cv); • Non-virtual otherwise ColorPt(Pt* pv,int cv); Overloaded constructor ‹Virtual functions ColorPt(ColorPt* cp); • Accessed by indirection through ptr in object int getColor(); Non-virtual function • May be redefined in derived (sub) classes virtual void move(int dx); Virtual functions ‹Non-virtual functions virtual void darken(int tint); • Are called in the usual way. Just ordinary functions. protected: • Cannot redefine in derived classes (except overloading) void setColor(int cv); Protected write access private: ‹Pay overhead only if you use virtual functions int color; Private member data };

2 Run-time representation Compare to Smalltalk

Point object Point vtable Code for move Point object Point class Template Method dictionary vptr x newX:Y: x 3 2 y ...... 3 move ColorPoint object ColorPoint vtable Code for move ColorPoint class Template vptr ColorPoint object Method dictionary x 5 x c blue Code for darken 4 y newX:Y:C: 5 color color move red Data at same offset Function pointers at same offset

Why is C++ lookup simpler? Calls to virtual functions

‹Smalltalk has no static type system ‹One member function may call another • Code p message:pars could refer to any object class A { • Need to find method using pointer from object public: virtual int f (int x); • Different classes will put methods at different place in virtual int g(int y); method dictionary }; ‹C++ type gives compiler some superclass int A::f(int x) { … g(i) …;} • Offset of data, fctn ptr same in subclass and superclass int A::g(int y) { … f(j) …;} • Offset of data and function ptr known at compile time ‹How does body of f call the right g? • Code p->move(x) compiles to equivalent of • If g is redefined in derived class B, then inherited f (*(p->vptr[1]))(p,x) if move is first function in vtable must call B::g

data passed to member function; see next slide

“This” pointer (analogous to self in Smalltalk) Non-virtual functions

‹Code is compiled so that member function takes ‹How is code for non-virtual function found? “object itself” as first argument ‹Same way as ordinary “non-member” functions: Code int A::f(int x) { … g(i) …;} • Compiler generates function code and assigns address compiled as int A::f(A *this, int x) { … this->g(i) …;} • Address of code is placed in symbol table • At call site, address is taken from symbol table and ‹“this” pointer may be used in member function placed in compiled code • Can be used to return pointer to object itself, pass • But some special scoping rules for classes pointer to object itself to another function, ... ‹Overloading • Remember: overloading is resolved at compile time • This is different from run-time lookup of virtual function

3 Scope rules in C++ Virtual vs Overloaded Functions

‹Scope qualifiers class parent { public: • binary :: operator, ->, and . void printclass() {printf("p ");}; virtual void printvirtual() {printf("p ");}; }; • class::member, ptr->member, object.member class child : public parent { public: ‹A name outside a function or class, void printclass() {printf("c ");}; • not prefixed by unary :: and not qualified refers to virtual void printvirtual() {printf("c ");}; }; global object, function, enumerator or type. main() { ‹A name after X::, ptr-> or obj. parent p; child c; parent *q; • where we assume ptr is pointer to class X and obj is p.printclass(); p.printvirtual(); c.printclass(); c.printvirtual(); an object of class X q = &p; q->printclass(); q->printvirtual(); • refers to a member of class X or a base class of X q = &c; q->printclass(); q->printvirtual(); } Output: p p c c p p p c

Subtyping Independent classes not subtypes

‹Subtyping in principle class Point { class ColorPoint { public: • A <: B if every A object can be used without type public: int getX(); error whenever a B object is required int getX(); void move(int); void move(int); • Example: protected: ... int getColor(); Point: int getX(); void darken(int); Public members private: ... void move(int); protected: ... }; ColorPoint: int getX(); private: ... int getColor(); }; Public members void move(int); void darken(int tint); ‹C++ does not treat ColorPoint <: Point as written ‹C++: A <: B if class A has public base class B • Need public inheritance ColorPoint : public Pt • This is weaker than necessary Why? • Why??

Why C++ design? Function subtyping

‹Client code depends only on public ‹Subtyping principle • In principle, if ColorPoint interface contains Point • A <: B if an A expression can be safely used in any interface, then any client could use ColorPoint in context where a B expression is required place of point ‹Subtyping for function results • However -- offset in virtual function table may differ • If A <: B, then C → A <: C → B • Lose implementation efficiency (like Smalltalk) ‹Subtyping for function arguments ‹Without link to inheritance • If A <: B, then B → C <: A → C • subtyping leads to loss of implementation efficiency ‹Terminology ‹Also encapsulation issue: • Covariance: A <: B implies F(A) <: F(B) • Subtyping based on inheritance is preserved under • Contravariance: A <: B implies F(B) <: F(A) modifications to base class …

4 Examples Subtyping with functions

‹If circle <: shape, then class Point { class ColorPoint: public Point { public: public: Inherited, but repeated int getX(); int getX(); here for clarity circle → shape virtual Point *move(int); int getColor(); protected: ... ColorPoint * move(int); void darken(int); private: ... circle → circle shape → shape protected: ... }; private: ... }; shape → circle ‹In principle: can have ColorPoint <: Point ‹In practice: some compilers allow, others have not C++ compilers recognize limited forms of function subtyping This is covariant case; contravariance is another story

Details, details Subtyping and Object L,R-Values

‹This is legal ‹If class B : public A { … } class Point { … Then virtual Point * move(int); • B r-value <: A r-value … } – If x = a is OK, then x = b is OK class ColorPoint: public Point { … provided A’s operator = is public virtual ColorPoint * move(int); – If f(a) is OK, then f(b) is OK … } provided A’s copy constructor is public ‹But not legal if *’s are removed • B l-value <: A l-value class Point { … virtual Point move(int); … } • B* <: A* on last slide because cannot assign to return value class ColorPoint: public Point { …virtual ColorPoint move(int);… } • B** <: A**

Related to subtyping distinctions for object L-values and object R-values Generally, X <: Y Æ X* <: Y* is unsound. (Non-pointer return type is treated like an L-value for some reason)

Review Abstract Classes

‹Why C++ requires inheritance for subtyping ‹Abstract class: • Need virtual function table to look the same • A class without complete implementation • This includes private and protected members • Declare by =0 (what a great syntax!) • Subtyping w/o inheritance weakens data abstraction • Useful because it can have derived classes (This is my post facto explanation; I don’t know what designers think.) Since subtyping follows inheritance in C++, use abstract classes to build subtype hierarchies. ‹ Possible confusion regarding inlining • Establishes layout of virtual function table (vtable) • Cannot generally inline virtual functions • Inlining is possible for nonvirtual functions ‹Example – These are available in C++ – Not in Smalltalk since every lookup is through class • Geometry classes in appendix of reader Inlining is very significant for efficiency; enables further optimization. – Shape is abstract supertype of circle, rectangle, ...

5 Multiple Inheritance Problem: Name Clashes

class A { public: Shape ReferenceCounted void virtual f() { … } }; same name class B { in 2 base Rectangle classes public: void virtual f() { … } RefCounted }; Rectangle class C : public A, public B { … }; … C* p; Inherit independent functionality from independent classes p->f(); // error

Possible solutions to name clash Repair to previous example

‹Three general approaches ‹Rewrite class C to call A::f explicitly • Implicit resolution class C : public A, public B { – Language resolves name conflicts with arbitrary rule public: • Explicit resolution void virtual f( ) { – Programmer must explicitly resolve name conflicts A::f( ); // Call A::f(), not B::f(); • Disallow name clashes } – Programs are not allowed to contain name clashes

‹No solution is always best ‹Reasonable solution ‹C++ uses explicit resolution • This eliminates ambiguity • Preserves dependence on A – Changes to A::f will change C::f

A B vtable for Multiple Inheritance Object and classes C class A { class C: public A, public B { C object C-as-A vtbl public: public: & C::f 0 int x; int z; pa, pc vptr δ A object virtual void f(); virtual void f(); A data C-as-B vtbl }; }; pb vptr B object & B::g 0 class B { B data & C::f δ public: C *pc = new C; C data int y; B *pb = pc; virtual void g(); A *pa = pc; ‹ Offset δ in vtbl is used in call to pb->f, since C::f may virtual void f(); Three pointers to same object, refer to A data that is above the pointer pb }; but different static types. ‹ Call to pc->g can proceed through C-as-B vtbl

6 Multiple Inheritance “Diamond” Diamond inheritance in C++ D

A B ‹Standard base classes Window (D) C • D members appear twice in C ‹Virtual base classes Text Window (A) Graphics Window (B) class A : public virtual D { … } A part • Avoid duplication of base B part Text, Graphics class members Window (C) • Require additional pointers so C part that D part of A, B parts of object can be shared D part ‹Is interface or implementation inherited twice? ‹What if definitions conflict? ‹C++ multiple inheritance is complicated in part because of desire to maintain efficient lookup

C++ Summary

‹Objects • Created by classes • Contain member data and pointer to class ‹Classes: virtual function table ‹Inheritance • Public and private base classes, multiple inheritance ‹Subtyping: Occurs with public base classes only ‹Encapsulation • member can be declared public, private, protected • object initialization partly enforced

7