Eiffel and C++ Are Statically Typed, Class- Based Object-Oriented Languages
Total Page:16
File Type:pdf, Size:1020Kb
EEiiffeffell aanndd CC++++ A comparison between two Object-Oriented languages Tom Van Cutsem CCllaasssseess aanndd OObbjjeecctsts Both Eiffel and C++ are statically typed, class- based object-oriented languages. Both regard class definitions as type definitions. Both make a clear distinction between classes and objects. Eiffel is a pure object-oriented language, C++ is impure (multi-paradigm). CCllaasssseess aanndd OObbjjeecctsts CC++++ State encapsulated in ‘data members’. Behaviour encapsulated in ‘memberfunctions’. Manual memory Management Four so called ‘special member functions’: the constructor, the destructor, the copy constructor and the assignment operator. CCllaasssseess aanndd OObbjjeecctsts EEiiffeffell Automatic Garbage Collection Eiffel classes consist of features: class HELLO create make feature make is -- Print a simple message. do io.put_string ("Hello World") io.put_new_line end end -- class HELLO CCllaasssseess aanndd OObbjjeecctsts EEiiffeffell Two viewpoints on features. From the viewpoint of the defining class, a feature is an attribute, a function or a procedure. From the viewpoint of the client class, a feature is a command or a query. Client Supplier Procedure Command Function Feature Query Attribute CCllaasssseess aanndd OObbjjeecctsts EEiiffeffell The client class does not see the difference between attribute access and a parameterless function call: the Uniform Access Principle myBalance := myAccount.balance implemented as an attribute class ACCOUNT feature balance: INTEGER end CCllaasssseess aanndd OObbjjeecctsts EEiiffeffell The client class does not see the difference between attribute access and a parameterless function call: the Uniform Access Principle myBalance := myAccount.balance implemented as a function class ACCOUNT feature -- an instance variable thebalance: INTEGER feature -- access the balance balance is do balance := thebalance end CCllaasssseess aanndd OObbjjeecctsts In C++, classes without ancestors become the root of a new hierarchy. Eiffel has a root class, ANY, which exports features like cloning, comparison, … Eiffel also defines a ‘least class’, NONE, with its sole instance Void. ANY … … … NONE InInfoforrmmaatitioonn HHiiddiinngg C++ defines a plethora of keywords to restrict access to members (public,private, protected, friend, …) Eiffel uses ‘export clauses’ to denote which classes (and subclasses) get to see certain features. Attributes can never be assigned to from outside the class, even when ‘public’. class ACCOUNT feature { NONE } -- “Private” balance: INTEGER feature -- “Public” deposit (sum: INTEGER) is do … end feature { ACCOUNT } -- “Protected” connect (bankcomputer: COMPUTER) is do … end feature { COMPUTER } -- COMPUTER is a ”friend” validate (password: STRING) is do … end end InInhheerriitatannccee MMooddeell CC++++ Both Eiffel and C++ allow multiple inheritance. C++ allows ‘private inheritance’ to support ‘subclassing for construction’. Sharing in repeated inheritance is accomplished by ‘virtual inheritance’. Name clashes are solved through explicit qualification of the scope using the scope- resolution (‘::’) operator. InInhheerriitatannccee MMooddeell EEiiffeffell class ASSISTANT inherit STUDENT TEACHER feature … feature adaption clauses are provided to change inherited features. The redefinition clause specifies which methods are overridden. class SAVINGS_ACCOUNT inherit ACCOUNT redefine deposit end feature -- new implementation deposit (sum : INTEGER) is do … end InInhheerriitatannccee MMooddeell EEiiffeffell The renaming clause specifies new names for inherited methods. This is also used to avoid name clashes. class ARRAYED_LIST inherit LIST ARRAY rename count as capacity, item as array_item end feature … The undefine clause uneffects features which is usefull when a class inherits two implementations for conceptually the same method. InInhheerriitatannccee MMooddeell EEiiffeffell Repeated inheritance is solved on a per-feature basis, rather than on a per-class basis. By renaming a repeated feature, a duplicate is made. If nothing is renamed, then the repeated features are folded into one. InInhheerriitatannccee MMooddeell EEiiffeffell class ASSISTANT inherit TEACHER rename computer_account as faculty_account select faculty_account end STUDENT rename computer_account as student_account end end The Select Clause is used to solve ambiguous calls, for example: p : PERSON create { ASSISTANT } p.make(“John Doe”) … p.computer_account … MMiixxiinnss class Person { string name; public: virtual void display() const { cout << name; } }; template<class T> class Doctor_mixin: public virtual T { public: virtual void display() const { cout << "Dr. "; T::display(); } }; template<class T> class Professor_mixin: public virtual T { public: virtual void display() const { cout << "Prof. "; T::display(); } }; class ProfessorDoctor: public virtual Professor_mixin<Doctor_mixin<Person> > {}; class DoctorProfessor: public virtual Doctor_mixin<Professor_mixin<Person> > {}; MMiixxiinnss Axiom (shared) f() = 0 Implementation Interface f() { … } g() { f(); } Mixin MMiixxiinn EExxaammppllee Axiom class Sequence { virtual void push_front(T elt) = 0; … }; Implementation Interface class Array: public virtual Sequence { … }; class Stack: public virtual Sequence { class List: public virtual Sequence { … }; virtual void push(T elt) { push_front(elt); } }; class Queue: public virtual Sequence { virtual void enqueue(T elt) { push_front(elt); } }; Mixin class ArrayStack: public virtual Array, public virtual Stack { }; class ListQueue: public virtual List, public virtual Queue { }; TTyyppee MMooddeell Both Eiffel and C++ allow polymorphic assignments based on subclassing (subclassing is interpreted as subtyping). Safe downcasting in C++ through a dynamic_cast: Derived* derived = dynamic_cast<Derived*>(base); if (derived != 0) … In Eiffel, use an ‘assignment attempt’: subclassObj ?= superclassObj if (subclassObj /= Void) then … GGeenneerriiccss C++ introduces Template functions or classes. Unconstrained: the constraints on the parameters are implicit in the body of the function or class. Eiffel introduces Generic classes with ‘formal generic parameters’. Introduces constrained genericity, for example: class SORTABLE_ARRAY [G COMPARABLE] feature … end DDeessiiggnn BByy CCoonntrtraacctt EEiiffeffell Client classes must conform to a ‘contract’ before being able to use services provided by a supplier. Supplier classes must conform to that ‘contract’ by ensuring they provide the correct services. Supported in Eiffel through pre- and postconditions and class invariants. Preconditions are inherited by ‘OR-ing’ them together, postconditions by ‘AND-ing’. DDeessiiggnn BByy CCoonntrtraacctt EEiiffeffell class ACCOUNT feature balance: INTEGER deposit_count: INTEGER -- number of deposits made since opening all_deposits: DEPOSIT_LIST -- list of deposits since account's opening feature deposit (sum: INTEGER) is precondition require non_negative: sum >= 0 do … postcondition ensure one_more_deposit: deposit_count = old deposit_count + 1 updated: balance = old balance + sum Class end invariant invariant consistent_balance: (all_deposits /= Void) implies (balance = all_deposits.total) zero_if_no_deposits: (all_deposits = Void) implies (balance = 0) end -- class ACCOUNT VVaarriiaannccee RRuulleess CC++++ C++ is agnostic (or non-variant) with respect to method arguments. Allows covariant semantics on return types, which is type safe: class Parent { public: virtual Parent* clone() { … return abase; } }; class Child: public Parent { public: virtual Child* clone() { … return aderived; } }; Parent* aparent = new Child; Parent* result = aparent->clone(); VVaarriiaannccee RRuulleess EEiiffeffell Eiffel uses covariant semantics for both return types and arguments. Allowing covariant semantics on arguments can compromise static typing: class PLOT class PLOT_3D feature inherit PLOT redefine add end add(covar:DATASAMPLE) is … feature add(covar:DATASAMPLE_3D) is … plot : PLOT plot3D : PLOT_3D sample : DATASAMPLE plot := plot3D plot.add(sample) VVaarriiaannccee RRuulleess EEiiffeffell Eiffel uses implicit covariance in its generic types: polymorphic assignments between A[X] and A[Y] are possible if X and Y are related through inheritance. This is similar to Java arrays, and also causes type-safety problems: girlsOnly : VECTOR [PERSON] aBoy : PERSON create {VECTOR[GIRL]} girlsOnly -- allowed with covariance create {BOY} aBoy -- permitted by principle of substitution girlsOnly.append(aBoy) -- girlsOnly now holds a Boy AAnncchhoorreedd TTyyppeess Eiffel introduces ‘anchored types’ to support working with covariance. Variables declared as: ‘x: like obj’ will be assigned the static type of obj. Anchored types are usually used with Current in a method declaration: when the method is inherited, its type signature changes acluastso AmCCOaUtNiTcally. class BUSINESS_ACCOUNT feature inherit ACCOUNT owner : PERSON feature set_owner(new: like owner ) is owner : BUSINESS_PERSON … … end end … end MMeeththoodd LLooookkuupp Eiffel always uses dynamic binding (static binding when appropriate). C++ employs a default policy of static binding. C++ binding policy Method is non-virtual Method is virtual Automatic Variables Static Binding Static Binding References & Pointers Static Binding Dynamic Binding MMeeththoodd LLooookkuupp CC++++ Implementation via a ‘virtual function table’ which is shared at run-time by all objects of the same class. Compiler uses lexical addressing through offsets