Proper Inheritance John Lakos Tuesday, May 10, 2016
1
Copyright Notice © 2016 Bloomberg L.P. Permission is granted to copy, distribute, and display this material, and to make derivative works and commercial use of it. The information in this material is provided "AS IS", without warranty of any kind. Neither Bloomberg nor any employee guarantees the correctness or completeness of such information. Bloomberg, its employees, and its affiliated entities and persons shall not be liable, directly or indirectly, in any way, for any inaccuracies, errors or omissions in such information. Nothing herein should be interpreted as stating the opinions, policies, recommendations, or positions of Bloomberg.
2
Abstract All essential behavior of our software must be documented, and yet there are important advantages, with respect to development, verification and testing, performance, and stability, for leaving the behavior for some combinations of inputs and initial conditions undefined. What is and is not defined behavior should therefore be readily discernible from the contract, especially when creating contracts that must span classes related by inheritance.
In this two-part talk, we begin by reviewing components, interfaces and contracts in general, and the significance of narrow versus wide contracts. In the second part, we go on to explore three kinds of inheritance: (1) Interface Inheritance resulting from pure-virtual functions, (2) Structural Inheritance resulting from non-virtual functions, and (3) Implementation Inheritance resulting from non-pure virtual functions. Proper contracts involving each of these distinct forms have different criteria that must be addressed. The three kinds of inheritance are compared, and their relative utility is explained. What's more, several common uses of inheritance that are provably improper are summarily debunked. 3
What’s The Problem?
4
What’s The Problem? Large-Scale C++ Software Design is Multi-Dimensional:
5
What’s The Problem? Large-Scale C++ Software Design is Multi-Dimensional: • It involves many subtle logical and physical aspects.
6
What’s The Problem? Large-Scale C++ Software Design is Multi-Dimensional: • It involves many subtle logical and physical aspects. • It requires an ability to isolate and modularize logical functionality within discrete, fine-grain physical components.
7
What’s The Problem? Large-Scale C++ Software Design is Multi-Dimensional: • It involves many subtle logical and physical aspects. • It requires an ability to isolate and modularize logical functionality within discrete, fine-grain physical components. • It requires the designer to delineate logical behavior precisely, while managing the physical dependencies on other subordinate components.
8
What’s The Problem? Large-Scale C++ Software Design is Multi-Dimensional: • It involves many subtle logical and physical aspects. • It requires an ability to isolate and modularize logical functionality within discrete, fine-grain physical components. • It requires the designer to delineate logical behavior precisely, while managing the physical dependencies on other subordinate components. • It requires attention to numerous logical and physical rules that govern sound software design. 9
Purpose of this Talk
10
Purpose of this Talk Review:
11
Purpose of this Talk Review: 1. Components – Our fundamental unit of logical and physical design
12
Purpose of this Talk Review: 1. Components – Our fundamental unit of logical and physical design
2. Interfaces and contracts
(in general)
13
Purpose of this Talk Review: 1. Components – Our fundamental unit of logical and physical design
2. Interfaces and contracts (in general) 3. Narrow versus Wide contracts (in particular)
14
Purpose of this Talk Review: 1. Components – Our fundamental unit of logical and physical design
2. Interfaces and contracts (in general) 3. Narrow versus Wide contracts (in particular)
4. Explore these basic ideas in the context inheritance. 15
Outline 1. Components (review) Modularity, Logical/Physical Dependencies, & Level numbers
2. Interfaces and Contracts (review) Syntax versus Semantics & Essential Behavior
3. Narrow versus Wide Contracts (review) The Significance of Undefined Behavior
4. Proper Inheritance Is-A for Interface, Structural, & Implementation Inheritance
16
Outline 1. Components (review) Modularity, Logical/Physical Dependencies, & Level numbers
2. Interfaces and Contracts (review) Syntax versus Semantics & Essential Behavior
3. Narrow versus Wide Contracts (review) The Significance of Undefined Behavior
4. Proper Inheritance Is-A for Interface, Structural, & Implementation Inheritance
17
1. Components (review)
Logical versus Physical Design What distinguishes Logical from Physical Design?
Logical physical
18
1. Components (review)
Logical versus Physical Design What distinguishes Logical from Physical Design?
Logical physical
Logical: Classes and Functions 19
1. Components (review)
Logical versus Physical Design What distinguishes Logical from Physical Design?
Logical physical
Logical: Classes and Functions Physical: Files and Libraries 20
1. Components (review)
Logical versus Physical Design Logical content aggregated into a Physical hierarchy of components c a
b
21
1. Components (review)
Component: Uniform Physical Structure A Component Is Physical // component.t.cpp #include
// ... int main(...) { //...
// component.h
// component.cpp #include
// ...
// ...
//-- END OF FILE --
//-- END OF FILE --
} //-- END OF FILE --
component.t.cpp
component.h component
component.cpp 22
1. Components (review)
Component: Uniform Physical Structure Implementation // component.t.cpp #include
// ... int main(...) { //...
// component.h
// component.cpp #include
// ...
// ...
//-- END OF FILE --
//-- END OF FILE --
} //-- END OF FILE --
component.t.cpp
component.h component
component.cpp 23
1. Components (review)
Component: Uniform Physical Structure Header // component.t.cpp #include
// ... int main(...) { //...
// component.h
// component.cpp #include
// ...
// ...
//-- END OF FILE --
//-- END OF FILE --
} //-- END OF FILE --
component.t.cpp
component.h component
component.cpp 24
1. Components (review)
Component: Uniform Physical Structure Test Driver // component.t.cpp #include
// ... int main(...) { //...
// component.h
// component.cpp #include
// ...
// ...
//-- END OF FILE --
//-- END OF FILE --
} //-- END OF FILE --
component.t.cpp
component.h component
component.cpp 25
1. Components (review)
Component: Uniform Physical Structure The Fundamental Unit of Design // component.t.cpp #include
// ... int main(...) { //...
// component.h
// component.cpp #include
// ...
// ...
//-- END OF FILE --
//-- END OF FILE --
} //-- END OF FILE --
component.t.cpp
component.h component
component.cpp 26
1. Components (review)
Component: Not Just a .h/.cpp Pair
my::Widget
my_widget 27
1. Components (review)
Component: Not Just a .h/.cpp Pair
28
1. Components (review)
Component: Not Just a .h/.cpp Pair 1.
The .cpp file includes its .h file as the first substantive line of code.
29
1. Components (review)
Component: Not Just a .h/.cpp Pair The .cpp file includes its .h file as the first substantive line of code. 2. All logical constructs having external linkage defined in a .cpp file are declared in the corresponding .h file. 3. All constructs having external or dual bindage declared in a .h file (if defined at all) are defined within the component. 4. A component’s functionality is accessed via a #include of its header, and never via a forward (extern) declaration. 1.
30
1. Components (review)
Component: Not Just a .h/.cpp Pair The .cpp file includes its .h file as the first substantive line of code. 2. All logical constructs having external linkage defined in a .cpp file are declared in the corresponding .h file. 3. All constructs having external or dual bindage declared in a .h file (if defined at all) are defined within the component. 4. A component’s functionality is accessed via a #include of its header, and never via a forward (extern) declaration. 1.
We could easily spend 20 minutes on this slide alone!
31
Avoid Global Namespace Pollution
1. Components Achieve (review)
Enable Efficient Extraction of Physical Dependencies
Component: Not Just a .h/.cpp Pair Logical/Physical Modularity
The .cpp file includes its .h file as the first substantive line of code. 2. All logical constructs having external linkage defined in a .cpp file are declared in the corresponding .h file. 3. All constructs having external or dual bindage declared in a .h file (if defined at all) are defined within the component. 4. A component’s functionality is accessed via a #include of its header, and never via a forward (extern) declaration. 1.
32
Avoid Global Namespace Pollution
1. Components Achieve (review)
Enable Efficient Extraction of Physical Dependencies
Component: Not Just a .h/.cpp Pair Logical/Physical Modularity
The .cpp file includes its .h file as the first substantive line of code. 2. All logical constructs having external linkage For much defined in a .cpp file aremore declaredsee in the corresponding .h file. 3. All constructs having external or dual bindage declared in a .h file (if defined at all) are defined within the component. 4. A component’s functionality is accessed via a #include of its header, and never via a forward (extern) declaration. 1.
ADVANCED LEVELIZATION TECHNIQUES
33
1. Components (review)
Logical Relationships Polygon
PointList
PointList_Link Underscore Implies Component-Local Class
Point
Shape
34
1. Components (review)
Logical Relationships Polygon
PointList
PointList_Link
Point
Shape
Is-A
35
1. Components (review)
Logical Relationships Polygon
PointList
PointList_Link
Point
Shape
Is-A
36
1. Components (review)
Logical Relationships Polygon
PointList
PointList_Link
Point
Shape
Uses-in-the-Interface Is-A
37
1. Components (review)
Logical Relationships Polygon
PointList
PointList_Link
Point
Shape
Uses-in-the-Interface Is-A
38
1. Components (review)
Logical Relationships Polygon
PointList
PointList_Link
Point
Shape
Uses-in-the-Interface Is-A
39
1. Components (review)
Logical Relationships Polygon
PointList
PointList_Link
Point
Shape
Uses-in-the-Interface Uses-in-the-Implementation
Is-A
40
1. Components (review)
Logical Relationships Polygon
PointList
PointList_Link
Point
Shape
Uses-in-the-Interface Uses-in-the-Implementation
Is-A
41
1. Components (review)
Logical Relationships Polygon
PointList
PointList_Link
Point
Shape
Uses-in-the-Interface Uses-in-the-Implementation
Is-A
42
1. Components (review)
Logical Relationships Polygon
PointList
PointList_Link
Point
Uses-in-the-Interface Uses-in-the-Implementation
Shape
Uses in name only 43 Is-A
1. Components (review)
Logical Relationships Polygon
PointList
PointList_Link
Point
Uses-in-the-Interface Uses-in-the-Implementation
Shape
Uses in name only 44 Is-A
1. Components (review)
Implied Dependency Polygon
PointList
PointList_Link
Point
Uses-in-the-Interface Uses-in-the-Implementation
Shape
Uses in name only 45 Is-A
1. Components (review)
Implied Dependency Polygon
PointList
PointList_Link
Point
Uses-in-the-Interface Uses-in-the-Implementation
Shape Depends-On Uses in name only 46 Is-A
1. Components (review)
Implied Dependency Polygon
PointList
PointList_Link
Point
Uses-in-the-Interface Uses-in-the-Implementation
Shape Depends-On Uses in name only 47 Is-A
1. Components (review)
Implied Dependency Polygon
PointList
PointList_Link
Point
Uses-in-the-Interface Uses-in-the-Implementation
Shape Depends-On Uses in name only 48 Is-A
1. Components (review)
Implied Dependency Polygon
PointList
PointList_Link
Point
Uses-in-the-Interface Uses-in-the-Implementation
Shape Depends-On Uses in name only 49 Is-A
1. Components (review)
Implied Dependency Polygon
PointList
PointList_Link
Point
Uses-in-the-Interface Uses-in-the-Implementation
Shape Depends-On Uses in name only 50 Is-A
1. Components (review)
Level Numbers Polygon
PointList
PointList_Link
Point
Uses-in-the-Interface Uses-in-the-Implementation
Shape Depends-On Uses in name only 51 Is-A
1. Components (review)
Level Numbers Polygon
PointList
PointList_Link
1
Point
Uses-in-the-Interface Uses-in-the-Implementation
Shape Depends-On Uses in name only 52 Is-A
1. Components (review)
Level Numbers Polygon
PointList
PointList_Link
1
Point
Uses-in-the-Interface Uses-in-the-Implementation
Shape
1
Depends-On Uses in name only 53 Is-A
1. Components (review)
Level Numbers 2
Polygon
PointList
PointList_Link
1
Point
Uses-in-the-Interface Uses-in-the-Implementation
Shape
1
Depends-On Uses in name only 54 Is-A
1. Components (review)
Level Numbers 2
3
Polygon
PointList
PointList_Link
1
Point
Uses-in-the-Interface Uses-in-the-Implementation
Shape
1
Depends-On Uses in name only 55 Is-A
1. Components (review)
Essential Physical Design Rules
56
1. Components (review)
Essential Physical Design Rules
There are two:
57
1. Components (review)
Essential Physical Design Rules
There are two:
1.No Cyclic Physical Dependencies! 58
1. Components (review)
Essential Physical Design Rules
There are two:
1.No Cyclic Physical Dependencies! 2.No Long-Distance Friendships!
59
1. Components (review)
End of Section
Questions?
1. Components (review)
What Questions are we Answering? • What distinguishes Logical and Physical Design? • What is the first of the (four) fundamental properties of a .h /.cpp pair that make it a component? • Which of these fundamental properties helps us extract physical dependencies efficiently? Extra credit: Why? How? • What are the (four) logical-relationship annotations? • Which logical relationship does not imply a physical one? • How do we infer physical relationships (Depends-On) from logical ones? • What do we mean by the term level number? • What are the (two) quintessential physical design rules? 61
Outline 1. Components (review) Modularity, Logical/Physical Dependencies, & Level numbers
2. Interfaces and Contracts (review) Syntax versus Semantics & Essential Behavior
3. Narrow versus Wide Contracts (review) The Significance of Undefined Behavior
4. Proper Inheritance Is-A for Interface, Structural, & Implementation Inheritance
62
Outline 1. Components (review) Modularity, Logical/Physical Dependencies, & Level numbers
2. Interfaces and Contracts (review) Syntax versus Semantics & Essential Behavior
3. Narrow versus Wide Contracts (review) The Significance of Undefined Behavior
4. Proper Inheritance Is-A for Interface, Structural, & Implementation Inheritance
63
2. Interfaces and Contracts (review)
Interfaces and Contracts What do we mean by Interface versus Contract for
• A Function? • A Class? • A Component? 64
2. Interfaces and Contracts (review)
Interfaces and Contracts
Function std::ostream& print(std::ostream& stream, int level = 0, int spacesPerLevel = 4) const;
65
2. Interfaces and Contracts (review)
Interfaces and Contracts
Function std::ostream& print(std::ostream& stream, int level = 0, int spacesPerLevel = 4) const;
Types Used In the Interface
66
2. Interfaces and Contracts (review)
Interfaces and Contracts
Function std::ostream& print(std::ostream& stream, int level = 0, int spacesPerLevel = 4) const; // Format this object to the specified output 'stream' at the (absolute // value of) the optionally specified indentation 'level' and return a // reference to 'stream'. If 'level' is specified, optionally specify // 'spacesPerLevel', the number of spaces per indentation level for // this and all of its nested objects. If 'level' is negative, // suppress indentation of the first line. If 'spacesPerLevel' is // negative, format the entire output on one line, suppressing all but // the initial indentation (as governed by 'level'). If 'stream' is // not valid on entry, this operation has no effect. 67
2. Interfaces and Contracts (review)
Interfaces and Contracts
Class
class Date { // This class implements a value-semantic type representing // a valid date in history between the dates 0001/01/01 and // 9999/12/31 inclusive. //… public: Date(int year, int month, int day); // Create a valid date from the specified ‘year’, ‘month’, and // ‘day’. The behavior is undefined unless ‘year’/’month’/’day’ // represents a valid date in the range [0001/01/01 .. 9999/12/31].
Date(const Date& original); // Create a date having the value of the specified ‘original’ date. // … };
68
2. Interfaces and Contracts (review)
Interfaces and Contracts
Class
class Date { // This class implements a value-semantic type representing // a valid date in history between the dates 0001/01/01 and // 9999/12/31 inclusive. //… public: Date(int year, int month, int day); // Create a valid date from the specified ‘year’, ‘month’, and // ‘day’. The behavior is undefined unless ‘year’/’month’/’day’ Public // represents a valid date in the range [0001/01/01 .. 9999/12/31].
Date(const Date& original); Interface // Create a date having the value of the specified ‘original’ date. // … };
69
2. Interfaces and Contracts (review)
Interfaces and Contracts
Class
class Date { // This class implements a value-semantic type representing // a valid date in history between the dates 0001/01/01 and // 9999/12/31 inclusive. //… public: Date(int year, int month, int day); // Create a valid date from the specified ‘year’, ‘month’, and // ‘day’. The behavior is undefined unless ‘year’/’month’/’day’ // represents a valid date in the range [0001/01/01 .. 9999/12/31].
Date(const Date& original); // Create a date having the value of the specified ‘original’ date. // … };
70
2. Interfaces and Contracts (review)
Interfaces and Contracts
Class
class Date { // This class implements a value-semantic type representing // a valid date in history between the dates 0001/01/01 and // 9999/12/31 inclusive. //… public: Date(int year, int month, int day); // Create a valid date from the specified ‘year’, ‘month’, and // ‘day’. The behavior is undefined unless ‘year’/’month’/’day’ // represents a valid date in the range [0001/01/01 .. 9999/12/31].
Date(const Date& original); // Create a date having the value of the specified ‘original’ date. // … };
71
2. Interfaces and Contracts (review)
Interfaces and Contracts
Class
class Date { // This class implements a value-semantic type representing // a valid date in history between the dates 0001/01/01 and // 9999/12/31 inclusive. //… public: Date(int year, int month, int day); // Create a valid date from the specified ‘year’, ‘month’, and // ‘day’. The behavior is undefined unless ‘year’/’month’/’day’ // represents a valid date in the range [0001/01/01 .. 9999/12/31].
Date(const Date& original); // Create a date having the value of the specified ‘original’ date. // … };
72
2. Interfaces and Contracts (review)
Interfaces and Contracts
Class
class Date { // This class implements a value-semantic type representing // a valid date in history between the dates 0001/01/01 and // 9999/12/31 inclusive. //… public: Date(int year, int month, int day); // Create a valid date from the specified ‘year’, ‘month’, and // ‘day’. The behavior is undefined unless ‘year’/’month’/’day’ // represents a valid date in the range [0001/01/01 .. 9999/12/31].
Date(const Date& original); // Create a date having the value of the specified ‘original’ date. // … };
73
2. Interfaces and Contracts (review)
Interfaces and Contracts
Component
class Date { // … public: // … }; bool operator==(const Date& lhs, const Date& rhs); // Return ‘true’ if the specified ‘lhs’ and ‘rhs’ dates have the same // value, ‘and’ false otherwise. Two ‘Date’ objects have the same // value if their respective ‘year’, ‘month’, and ‘day’ attributes // have the same value. bool operator!=(const Date& lhs, const Date& rhs); // Return ‘true’ if the specified ‘lhs’ and ‘rhs’ dates no not have the // same value and false otherwise. Two ‘Date’ objects do not have // the same value if any of their respective ‘year’, ‘month’, and ‘day’ // attributes do not have the same value. std::ostream& operator<<(std::ostream& stream, const Date& date); // Format the value of the specified ‘date’ object to the specified // output ‘stream’ as ‘yyyy/mm/dd’. 74
2. Interfaces and Contracts (review)
Interfaces and Contracts
Component
class Date { // … public: // … }; “Public” bool operator==(const Date& lhs, const Date& rhs); // Return ‘true’ if the specified ‘lhs’ and ‘rhs’ dates have the same // value, ‘and’ false otherwise. Two ‘Date’ objects have the same // value if their respective ‘year’, ‘month’, and ‘day’ attributes // have the same value.
Interface
bool operator!=(const Date& lhs, const Date& rhs); // Return ‘true’ if the specified ‘lhs’ and ‘rhs’ dates no not have the // same value and false otherwise. Two ‘Date’ objects do not have // the same value if any of their respective ‘year’, ‘month’, and ‘day’ // attributes do not have the same value. std::ostream& operator<<(std::ostream& stream, const Date& date); // Format the value of the specified ‘date’ object to the specified // output ‘stream’ as ‘yyyy/mm/dd’. 75
2. Interfaces and Contracts (review)
Interfaces and Contracts
Component
class Date { // … public: // … }; bool operator==(const Date& lhs, const Date& rhs); // Return ‘true’ if the specified ‘lhs’ and ‘rhs’ dates have the same // value, ‘and’ false otherwise. Two ‘Date’ objects have the same // value if their respective ‘year’, ‘month’, and ‘day’ attributes // have the same value. bool operator!=(const Date& lhs, const Date& rhs); // Return ‘true’ if the specified ‘lhs’ and ‘rhs’ dates no not have the // same value and false otherwise. Two ‘Date’ objects do not have // the same value if any of their respective ‘year’, ‘month’, and ‘day’ // attributes do not have the same value. std::ostream& operator<<(std::ostream& stream, const Date& date); // Format the value of the specified ‘date’ object to the specified // output ‘stream’ as ‘yyyy/mm/dd’. 76
2. Interfaces and Contracts (review)
Interfaces and Contracts
Component
class Date { // … public: // … }; bool operator==(const Date& lhs, const Date& rhs); // Return ‘true’ if the specified ‘lhs’ and ‘rhs’ dates have the same // value, and ‘false’ otherwise. Two ‘Date’ objects have the same // value if their respective ‘year’, ‘month’, and ‘day’ attributes // have the same value. bool operator!=(const Date& lhs, const Date& rhs); // Return ‘true’ if the specified ‘lhs’ and ‘rhs’ dates no not have the // same value and false otherwise. Two ‘Date’ objects do not have // the same value if any of their respective ‘year’, ‘month’, and ‘day’ // attributes do not have the same value. std::ostream& operator<<(std::ostream& stream, const Date& date); // Format the value of the specified ‘date’ object to the specified // output ‘stream’ as ‘yyyy/mm/dd’. 77
2. Interfaces and Contracts (review)
Interfaces and Contracts
Component
class Date { // … public: // … }; bool operator==(const Date& lhs, const Date& rhs); // Return ‘true’ if the specified ‘lhs’ and ‘rhs’ dates have the same // value, and ‘false’ otherwise. Two ‘Date’ objects have the same // value if their respective ‘year’, ‘month’, and ‘day’ attributes // have the same value. bool operator!=(const Date& lhs, const Date& rhs); // Return ‘true’ if the specified ‘lhs’ and ‘rhs’ dates do not have the // same value, and ‘false’ otherwise. Two ‘Date’ objects do not have // the same value if any of their respective ‘year’, ‘month’, and ‘day’ // attributes do not have the same value. std::ostream& operator<<(std::ostream& stream, const Date& date); // Format the value of the specified ‘date’ object to the specified // output ‘stream’ as ‘yyyy/mm/dd’. 78
2. Interfaces and Contracts (review)
Interfaces and Contracts
Component
class Date { // … public: // … }; bool operator==(const Date& lhs, const Date& rhs); // Return ‘true’ if the specified ‘lhs’ and ‘rhs’ dates have the same // value, and ‘false’ otherwise. Two ‘Date’ objects have the same // value if their respective ‘year’, ‘month’, and ‘day’ attributes // have the same value. bool operator!=(const Date& lhs, const Date& rhs); // Return ‘true’ if the specified ‘lhs’ and ‘rhs’ dates do not have the // same value, and ‘false’ otherwise. Two ‘Date’ objects do not have // the same value if any of their respective ‘year’, ‘month’, and ‘day’ // attributes do not have the same value. std::ostream& operator<<(std::ostream& stream, const Date& date); // Format the value of the specified ‘date’ object to the specified // output ‘stream’ as ‘yyyy/mm/dd’, and return a reference to ‘stream’. 79
2. Interfaces and Contracts (review)
Preconditions and Postconditions
80
2. Interfaces and Contracts (review)
Preconditions and Postconditions
Function
81
2. Interfaces and Contracts (review)
Preconditions and Postconditions
Function double sqrt(double value); // Return the square root of the specified ‘value’. // The behavior is undefined unless ‘0 <= value’.
82
2. Interfaces and Contracts (review)
Preconditions and Postconditions
Function double sqrt(double value); // Return the square root of the specified ‘value’. // The behavior is undefined unless ‘0 <= value’.
83
2. Interfaces and Contracts (review)
Preconditions and Postconditions
Function double sqrt(double value); // Return the square root of the specified ‘value’. // The behavior is undefined unless ‘0 <= value’.
Precondition 84
2. Interfaces and Contracts (review)
Preconditions and Postconditions
Function double sqrt(double value); // Return the square root of the specified ‘value’. // The behavior is undefined unless ‘0 <= value’.
Precondition For a Stateless Function: Restriction on syntactically legal inputs. 85
2. Interfaces and Contracts (review)
Preconditions and Postconditions
Function double sqrt(double value); // Return the square root of the specified ‘value’. // The behavior is undefined unless ‘0 <= value’.
86
2. Interfaces and Contracts (review)
Preconditions and Postconditions
Function double sqrt(double value); // Return the square root of the specified ‘value’. // The behavior is undefined unless ‘0 <= value’.
Postcondition 87
2. Interfaces and Contracts (review)
Preconditions and Postconditions
Function double sqrt(double value); // Return the square root of the specified ‘value’. // The behavior is undefined unless ‘0 <= value’.
Postcondition For a Stateless Function: What it “returns.”
88
2. Interfaces and Contracts (review)
Preconditions and Postconditions
Object Method
89
2. Interfaces and Contracts (review)
Preconditions and Postconditions
Object Method Preconditions: What must be true of both (object) state and method inputs; otherwise the behavior is undefined.
90
2. Interfaces and Contracts (review)
Preconditions and Postconditions
Object Method Preconditions: What must be true of both (object) state and method inputs; otherwise the behavior is undefined. Postconditions: What must happen as a function of (object) state and input if all Preconditions are satisfied. 91
2. Interfaces and Contracts (review)
Preconditions and Postconditions
Object Method Preconditions: What must be true of both (object) state and method inputs; otherwise the behavior is undefined. Postconditions: What must happen as a function of (object) state and input if all Preconditions are satisfied. 92
Note that Essentialand Behavior refers 2. Interfaces Contracts (review)to a superset of Postconditions that includes Preconditions and Postconditions behavioral guarantees, such as runtime complexity.
Object Method
Preconditions: What must be true of both (object) state and method inputs; otherwise the behavior is undefined. Postconditions: What must happen as a function of (object) state and method inputs if all preconditions are satisfied. Observation By
Kevlin Henny
93
2. Interfaces and Contracts (review)
Preconditions and Postconditions
Defined & Essential Behavior Not Undefined Behavior
Undefined Behavior 94
2. Interfaces and Contracts (review)
Preconditions and Postconditions
Defined & Essential Behavior Essential Behavior
Undefined Behavior 95
2. Interfaces and Contracts (review)
Preconditions and Postconditions
Defined & Essential Behavior Defined but not Essential Essential Behavior
Undefined Behavior 96
2. Interfaces and Contracts (review)
Preconditions and Postconditions
Defined & Essential Behavior Defined but not Essential
Unspecified and Implementation -dependent
Essential Behavior
Undefined Behavior 97
2. Interfaces and Contracts (review)
Preconditions and Postconditions
Defined & Essential Behavior std::ostream& print(std::ostream& stream, int level = 0, int spacesPerLevel = 4) const;
// Format this object to the specified output 'stream' at the (absolute // value of) the optionally specified indentation 'level' and return a // reference to 'stream'. If 'level' is specified, optionally specify // 'spacesPerLevel', the number of spaces per indentation level for // this and all of its nested objects. If 'level' is negative, // suppress indentation of the first line. If 'spacesPerLevel' is // negative, format the entire output on one line, suppressing all but // the initial indentation (as governed by 'level'). If 'stream' is // not valid on entry, this operation has no effect. 98
2. Interfaces and Contracts (review)
Preconditions and Postconditions
Defined & Essential Behavior std::ostream& print(std::ostream& stream, int level = 0, int spacesPerLevel = 4) const;
// Format this object to the specified output 'stream' at the (absolute // value of) the optionally specified indentation 'level' and return a // reference to 'stream'. If 'level' is specified, optionally specify // 'spacesPerLevel', the number of spaces per indentation level for // this and all of its nested objects. If 'level' is negative, // suppress indentation of the first line. If 'spacesPerLevel' is // negative, format the entire output on one line, suppressing all but // the initial indentation (as governed by 'level'). If 'stream' is // not valid on entry, this operation has no effect. 99
2. Interfaces and Contracts (review)
Preconditions and Postconditions
Defined & Essential Behavior std::ostream& print(std::ostream& stream, int level = 0, int spacesPerLevel = 4) const;
// Format this object to the specified output 'stream' at the (absolute // value of) the optionally specified indentation 'level' and return a // reference to 'stream'. If 'level' is specified, optionally specify // 'spacesPerLevel', the number of spaces per indentation level for // this and all of its nested objects. If 'level' is negative, // suppress indentation of the first line. If 'spacesPerLevel' is // negative, format the entire output on one line, suppressing all but // the initial indentation (as governed by 'level'). If 'stream' is // not valid on entry, this operation has no effect. 100
2. Interfaces and Contracts (review)
Preconditions and Postconditions
Defined & Essential Behavior std::ostream& print(std::ostream& stream, int level = 0, int spacesPerLevel = 4) const;
// Format this object to the specified output 'stream' at the (absolute // value of) the optionally specified indentation 'level' and return a // reference to 'stream'. If 'level' is specified, optionally specify // 'spacesPerLevel', the number of spaces per indentation level for // this and all of its nested objects. If 'level' is negative, // suppress indentation of the first line. If 'spacesPerLevel' is // negative, format the entire output on one line, suppressing all but // the initial indentation (as governed by 'level'). If 'stream' is // not valid on entry, this operation has no effect. 101
2. Interfaces and Contracts (review)
Preconditions and Postconditions
Defined & Essential Behavior std::ostream& print(std::ostream& stream, int level = 0, int spacesPerLevel = 4) const;
// Format this object to the specified output 'stream' at the (absolute // value of) the optionally specified indentation 'level' and return a // reference to 'stream'. If 'level' is specified, optionally specify // 'spacesPerLevel', the number of spaces per indentation level for // this and all of its nested objects. If 'level' is negative, // suppress indentation of the first line. If 'spacesPerLevel' is // negative, format the entire output on one line, suppressing all but // the initial indentation (as governed by 'level'). If 'stream' is // not valid on entry, this operation has no effect. 102
2. Interfaces and Contracts (review)
Any Preconditions and Postconditions Undefined Behavior?
Defined & Essential Behavior std::ostream& print(std::ostream& stream, int level = 0, int spacesPerLevel = 4) const;
// Format this object to the specified output 'stream' at the (absolute // value of) the optionally specified indentation 'level' and return a // reference to 'stream'. If 'level' is specified, optionally specify // 'spacesPerLevel', the number of spaces per indentation level for // this and all of its nested objects. If 'level' is negative, // suppress indentation of the first line. If 'spacesPerLevel' is // negative, format the entire output on one line, suppressing all but // the initial indentation (as governed by 'level'). If 'stream' is // not valid on entry, this operation has no effect. 103
2. Interfaces and Contracts (review)
Any Preconditions and Postconditions Non-Essential Behavior?
Defined & Essential Behavior std::ostream& print(std::ostream& stream, int level = 0, int spacesPerLevel = 4) const;
// Format this object to the specified output 'stream' at the (absolute // value of) the optionally specified indentation 'level' and return a // reference to 'stream'. If 'level' is specified, optionally specify // 'spacesPerLevel', the number of spaces per indentation level for // this and all of its nested objects. If 'level' is negative, // suppress indentation of the first line. If 'spacesPerLevel' is // negative, format the entire output on one line, suppressing all but // the initial indentation (as governed by 'level'). If 'stream' is // not valid on entry, this operation has no effect. 104
Hint
2. Interfaces and Contracts (review)
Any Preconditions and Postconditions Non-Essential Behavior?
Defined & Essential Behavior std::ostream& print(std::ostream& stream, int level = 0, int spacesPerLevel = 4) const;
// Format this object to the specified output 'stream' at the (absolute // value of) the optionally specified indentation 'level' and return a // reference to 'stream'. If 'level' is specified, optionally specify // 'spacesPerLevel', the number of spaces per indentation level for // this and all of its nested objects. If 'level' is negative, // suppress indentation of the first line. If 'spacesPerLevel' is // negative, format the entire output on one line, suppressing all but // the initial indentation (as governed by 'level'). If 'stream' is // not valid on entry, this operation has no effect. 105
2. Interfaces and Contracts (review)
Preconditions and Postconditions
Defined & Essential Behavior class Date { // This class implements a value-semantic type representing // a valid date in history between the dates 0001/01/01 and // 9999/12/31 inclusive. //…
public: Date(int year, int month, int day); // Create a valid date from the specified ‘year’, ‘month’, and // ‘day’. The behavior is undefined unless ‘year’/’month’/’day’ // represents a valid date in the range [0001/01/01 .. 9999/12/31].
};
Date(const Date& original); // Create a date having the value of the specified ‘original’ date. // … 106
2. Interfaces and Contracts (review)
Preconditions and Postconditions
Defined & Essential Behavior class Date { Any // This class implements a value-semantic type representing // a valid date in history between the dates 0001/01/01 and // 9999/12/31 inclusive. //…
Undefined Behavior?
public: Date(int year, int month, int day); // Create a valid date from the specified ‘year’, ‘month’, and // ‘day’. The behavior is undefined unless ‘year’/’month’/’day’ // represents a valid date in the range [0001/01/01 .. 9999/12/31].
};
Date(const Date& original); // Create a date having the value of the specified ‘original’ date. // … 107
2. Interfaces and Contracts (review)
Preconditions and Postconditions
Defined & Essential Behavior class Date { Any // This class implements a value-semantic type representing // a valid date in history between the dates 0001/01/01 and // 9999/12/31 inclusive. //…
Undefined Behavior?
public: Date(int year, int month, int day); // Create a valid date from the specified ‘year’, ‘month’, and // ‘day’. The behavior is undefined unless ‘year’/’month’/’day’ // represents a valid date in the range [0001/01/01 .. 9999/12/31].
};
Date(const Date& original); // Create a date having the value of the specified ‘original’ date. // … 108
2. Interfaces and Contracts (review)
Preconditions and Postconditions
Defined & Essential Behavior class Date { // This class implements a value-semantic type representing // a valid date in history between the dates 0001/01/01 and // 9999/12/31 inclusive. //…
public: Date(int year, int month, int day); // Create a valid date from the specified ‘year’, ‘month’, and // ‘day’. The behavior is undefined unless ‘year’/’month’/’day’ // represents a valid date in the range [0001/01/01 .. 9999/12/31].
};
Date(const Date& original); // Create a date having the value of the specified ‘original’ date. // … 109
2. Interfaces and Contracts (review)
Preconditions and Postconditions
Defined & Essential Behavior class Date { // This class implements a value-semantic type representing // a valid date in history between the dates 0001/01/01 and // 9999/12/31 inclusive. //…
Any public: Undefined Date(int year, int month, int day); // Create a valid date from the specified Behavior? ‘year’, ‘month’, and // ‘day’. The behavior is undefined unless ‘year’/’month’/’day’ // represents a valid date in the range [0001/01/01 .. 9999/12/31].
};
Date(const Date& original); // Create a date having the value of the specified ‘original’ date. // … 110
2. Interfaces and Contracts (review)
Preconditions and Postconditions
(Object) Invariants class Date { // This class implements a value-semantic type representing // a valid date in history between the dates 0001/01/01 and // 9999/12/31 inclusive. //…
public: Date(int year, int month, int day); // Create a valid date from the specified ‘year’, ‘month’, and // ‘day’. The behavior is undefined unless ‘year’/’month’/’day’ // represents a valid date in the range [0001/01/01 .. 9999/12/31].
};
Date(const Date& original); // Create a date having the value of the specified ‘original’ date. // … 111
2. Interfaces and Contracts (review)
Preconditions and Postconditions
(Object) Invariants class Date { // This class implements a value-semantic type representing // a valid date in history between the dates 0001/01/01 and // 9999/12/31 inclusive. //…
public: Date(int year, int month, int day); // Create a valid date from the specified ‘year’, ‘month’, and // ‘day’. The behavior is undefined unless ‘year’/’month’/’day’ // represents a valid date in the range [0001/01/01 .. 9999/12/31].
};
Date(const Date& original); // Create a date having the value of the specified ‘original’ date. // … 112
2. Interfaces and Contracts (review)
Preconditions and Postconditions
(Object) Invariants class Date { // This class implements a value-semantic type representing // a valid date in history between the dates 0001/01/01 and // 9999/12/31 inclusive. //…
public: Date(int year, int month, int day); // Create a valid date from the specified ‘year’, ‘month’, and // ‘day’. The behavior is undefined unless ‘year’/’month’/’day’ // represents a valid date in the range [0001/01/01 .. 9999/12/31].
};
Date(const Date& original); // Create a date having the value of the specified ‘original’ date. // … 113
2. Interfaces and Contracts (review)
Preconditions and Postconditions
(Object) Invariants class Date { // This class implements a value-semantic type representing // a valid date in history between the dates 0001/01/01 and // 9999/12/31 inclusive. //…
public: Date(int year, int month, int day); // Create a valid date from the specified ‘year’, ‘month’, and // ‘day’. The behavior is undefined unless ‘year’/’month’/’day’ // represents a valid date in the range [0001/01/01 .. 9999/12/31].
};
Date(const Date& original); // Create a date having the value of the specified ‘original’ date. // … 114
2. Interfaces and Contracts (review)
Preconditions and Postconditions
(Object) Invariants class Date { // This class implements a value-semantic type representing // a valid date in history between the dates 0001/01/01 and // 9999/12/31 inclusive. //…
public: Date(int year, int month, int day); // Create a valid date from the specified ‘year’, ‘month’, and // ‘day’. The behavior is undefined unless ‘year’/’month’/’day’ // represents a valid date in the range [0001/01/01 .. 9999/12/31].
};
Date(const Date& original); // Create a date having the value of the specified ‘original’ date. // … 115
2. Interfaces and Contracts (review)
Preconditions and Postconditions
(Object) Invariants class Date { // This class implements a value-semantic type representing // a valid date in history between the dates 0001/01/01 and // 9999/12/31 inclusive. //…
public: Date(int year, int month, int day); // Create a valid date from the specified ‘year’, ‘month’, and // ‘day’. The behavior is undefined unless ‘year’/’month’/’day’ // represents a valid date in the range [0001/01/01 .. 9999/12/31].
};
Date(const Date& original); // Create a date having the value of the specified ‘original’ date. // … 116
2. Interfaces and Contracts (review)
Design by Contract
117
2. Interfaces and Contracts (review)
Design by Contract
(DbC) “If you give me valid input*, I will behave as advertised; otherwise, all bets are off!” *including state 118
2. Interfaces and Contracts (review)
Design by Contract
Documentation There are five aspects: 1. What it does. 2. What it returns. 3. Essential Behavior. 4. Undefined Behavior. 5. Note that… 119
2. Interfaces and Contracts (review)
Design by Contract
Documentation There are five aspects: 1. What it does. 2. What it returns. 3. Essential Behavior. 4. Undefined Behavior. 5. Note that… 120
2. Interfaces and Contracts (review)
Design by Contract
Documentation There are five aspects: 1. What it does. 2. What it returns. 3. Essential Behavior. 4. Undefined Behavior. 5. Note that… 121
2. Interfaces and Contracts (review)
Design by Contract
Documentation There are five aspects: 1. What it does. 2. What it returns. 3. Essential Behavior. 4. Undefined Behavior. 5. Note that… 122
2. Interfaces and Contracts (review)
Design by Contract
Documentation There are five aspects: 1. What it does. 2. What it returns. 3. Essential Behavior. 4. Undefined Behavior. 5. Note that… 123
2. Interfaces and Contracts (review)
Design by Contract
Documentation There are five aspects: 1. What it does. 2. What it returns. 3. Essential Behavior. 4. Undefined Behavior. 5. Note that… 124
2. Interfaces and Contracts (review)
Design by Contract
Verification Invariants: Assert invariants in the destructor.
125
2. Interfaces and Contracts (review)
Design by Contract
Verification Preconditions: Invariants: Assert invariants in the destructor.
126
2. Interfaces and Contracts (review)
Design by Contract
Verification Preconditions: RTFM (Read the Manual). Invariants: Assert invariants in the destructor.
127
2. Interfaces and Contracts (review)
Design by Contract
Verification Preconditions: RTFM (Read the Manual). Assert (only in ‘debug’ or ‘safe’ mode). Invariants: Assert invariants in the destructor. 128
2. Interfaces and Contracts (review)
Design by Contract
Verification Preconditions: RTFM (Read the Manual). Assert (only in ‘debug’ or ‘safe’ mode). Postconditions: Invariants: Assert invariants in the destructor. 129
2. Interfaces and Contracts (review)
Design by Contract
Verification Preconditions: RTFM (Read the Manual). Assert (only in ‘debug’ or ‘safe’ mode). Postconditions: Component-level test drivers. Invariants: Assert invariants in the destructor. 130
2. Interfaces and Contracts (review)
Design by Contract
Verification Preconditions: RTFM (Read the Manual). Assert (only in ‘debug’ or ‘safe’ mode). Postconditions: Component-level test drivers. Invariants: Assert invariants in the destructor. 131
2. Interfaces and Contracts (review)
Design by Contract
Verification Preconditions: RTFM (Read the Manual). Assert (only in ‘debug’ or ‘safe’ mode). Postconditions: Component-level test drivers. Invariants: Assert invariants in the destructor. 132
2. Interfaces and Contracts (review)
Contracts and Exceptions Preconditions always Imply Postconditions:
133
2. Interfaces and Contracts (review)
Contracts and Exceptions Preconditions always Imply Postconditions: If a function cannot satisfy its contract (given valid preconditions) it
must not return normally.
134
2. Interfaces and Contracts (review)
Contracts and Exceptions Preconditions always Imply Postconditions: If a function cannot satisfy its contract (given valid preconditions) it
must not return normally.
abort() should be considered a viable alternative to throw in virtually all cases (if exceptions are disabled).
135
2. Interfaces and Contracts (review)
Contracts and Exceptions Preconditions always Imply Postconditions: If a function cannot satisfy its contract (given valid preconditions) it
must not return normally.
abort() should be considered a viable alternative to throw in virtually all cases (if exceptions are disabled). Good library components are exception agnostic (via RAII). 136
2. Interfaces and Contracts (review)
End of Section
Questions? 137
2. Interfaces and Contracts (review)
What Questions are we Answering? • What do we mean by Interface versus Contract for a function, a class, or a component? • What do we mean by preconditions, postconditions, and invariants? • What do we mean by essential & undefined behavior? • Must the code itself preserve invariants even if one or more preconditions of the contract are violated? • What is the idea behind Design-by-Contract (DbC)? • How do we document the contract for a function? • How can clients ensure that preconditions are satisfied? • How do we guarantee that postconditions are satisfied? • How can we test to make sure invariants are preserved? • What must be true if a client satisfies all preconditions? 138
Outline 1. Components (review) Modularity, Logical/Physical Dependencies, & Level numbers
2. Interfaces and Contracts (review) Syntax versus Semantics & Essential Behavior
3. Narrow versus Wide Contracts (review) The Significance of Undefined Behavior
4. Proper Inheritance Is-A for Interface, Structural, & Implementation Inheritance
139
Outline 1. Components (review) Modularity, Logical/Physical Dependencies, & Level numbers
2. Interfaces and Contracts (review) Syntax versus Semantics & Essential Behavior
3. Narrow versus Wide Contracts (review) The Significance of Undefined Behavior
4. Proper Inheritance Is-A for Interface, Structural, & Implementation Inheritance
140
3. Narrow versus Wide Contracts (review)
Narrow versus Wide Contracts
141
3. Narrow versus Wide Contracts (review)
Narrow versus Wide Contracts
Pejorative terms:
142
3. Narrow versus Wide Contracts (review)
Narrow versus Wide Contracts
Pejorative terms: •Fat Interface (4. Proper Inheritance)
143
3. Narrow versus Wide Contracts (review)
Narrow versus Wide Contracts
Pejorative terms: •Fat Interface (4. Proper Inheritance) •Large (Non-Primitive) Interface 144
3. Narrow versus Wide Contracts (review)
Narrow versus Wide Contracts
Pejorative terms: •Fat Interface (4. Proper Inheritance) •Large (Non-Primitive) Interface •Wide Contract
145
3. Narrow versus Wide Contracts (review)
Narrow versus Wide Contracts Narrow Contracts Imply Undefined Behavior:
146
3. Narrow versus Wide Contracts (review)
Narrow versus Wide Contracts Narrow Contracts Imply Undefined Behavior: What should happen with the following call?
int x = std::strlen(0);
147
3. Narrow versus Wide Contracts (review)
Narrow versus Wide Contracts Narrow Contracts Imply Undefined Behavior: What should happen with the following call?
int x = std::strlen(0);
How about it must return 0? 148
3. Narrow versus Wide Contracts (review)
Narrow versus Wide Contracts Narrow Contracts Imply Undefined Behavior:
int strlen(const char *s) { if (!s) return 0; Wide // … }
How about it must return 0? 149
3. Narrow versus Wide Contracts (review)
Narrow versus Wide Contracts Narrow Contracts Imply Undefined Behavior:
int strlen(const char *s) { if (!s) return 0; Wide // … Likely to mask a defect }
How about it must return 0? 150
3. Narrow versus Wide Contracts (review)
Narrow versus Wide Contracts Narrow Contracts Imply Undefined Behavior:
int strlen(const char *s) { if (!s) return 0; Wide // … Likely to mask a defect }
How about it must return 0? 151
3. Narrow versus Wide Contracts (review)
Narrow versus Wide Contracts Narrow Contracts Imply Undefined Behavior: What should happen with the following call?
int x = std::strlen(0);
152
3. Narrow versus Wide Contracts (review)
Narrow versus Wide Contracts Narrow Contracts Imply Undefined Behavior: What should happen with the following call?
int x = std::strlen(0);
153
3. Narrow versus Wide Contracts (review)
Narrow versus Wide Contracts Narrow Contracts Imply Undefined Behavior:
int strlen(const char *s) { assert(s); Narrow // … }
154
3. Narrow versus Wide Contracts (review)
Narrow versus Wide Contracts Narrow Contracts Imply Undefined Behavior:
int strlen(const char *s) { Narrow
// … }
155
3. Narrow versus Wide Contracts (review)
Narrow versus Wide Contracts Narrow Contracts Imply Undefined Behavior:
int strlen(const char *s) { Narrow
// … }
156
3. Narrow versus Wide Contracts (review)
Narrow versus Wide Contracts Narrow Contracts Imply Undefined Behavior: Should Date::setDate(int, int, int); Return a status?
157
3. Narrow versus Wide Contracts (review)
Narrow versus Wide Contracts Narrow Contracts Imply Undefined Behavior: Should Date::setDate(int, int, int); Return a status?
158
3. Narrow versus Wide Contracts (review)
Narrow versus Wide Contracts Narrow Contracts Imply Undefined Behavior: I “know” this date is valid (It’s my birthday)! date.setDate(3, 8, 59); Therefore, why should I bother to check status? date.setDate(1959, 3, 8); 159
3. Narrow versus Wide Contracts (review)
Narrow versus Wide Contracts Narrow Contracts Imply Undefined Behavior: I “know” this date is valid (It’s my birthday)! date.setDate(3, 8, 59); Therefore, why should I bother to check status? date.setDate(1959, 3, 8); 160
3. Narrow versus Wide Contracts (review)
Narrow versus Wide Contracts Narrow Contracts Imply Undefined Behavior: I “know” this date is valid (It’s my birthday)! date.setDate(3, 8, 59); Therefore, why should I bother to check status? date.setDate(1959, 3, 8); 161
3. Narrow versus Wide Contracts (review)
Narrow versus Wide Contracts Narrow Contracts Imply Undefined Behavior:
Returning status implies a wide contract.
162
3. Narrow versus Wide Contracts (review)
Narrow versus Wide Contracts Narrow Contracts Imply Undefined Behavior:
Returning status implies a wide contract. Wide contracts prevent defending against such errors in any build mode. 163
3. Narrow versus Wide Contracts (review)
Narrow versus Wide Contracts Narrow Contracts Imply Undefined Behavior: void Date::setDate(int y, int m, int d) { assert(isValid(y,m,d)); d_year = y; d_month = m; d_day = d; } 164
3. Narrow versus Wide Contracts (review)
Narrow versus Wide Contracts Narrow Contracts Imply Undefined Behavior: void Date::setDate(int y, int m, int d) { assert(isValid(y,m,d)); d_year = y; d_month = m; d_day = d; } 165
3. Narrow versus Wide Contracts (review)
Narrow versus Wide Contracts Narrow Contracts Imply Undefined Behavior: void Date::setDate(int y, int m, int d) { assert(isValid(y,m,d)); d_year = y; d_month = m; Narrow Contract: d_day = d; Checked Only In } “Debug Mode”
166
3. Narrow versus Wide Contracts (review)
Narrow versus Wide Contracts Narrow Contracts Imply Undefined Behavior: int Date::setDateIfValid(int y, int m, int d) { if (!isValid(y, m, d)) { return !0; } d_year = y; d_month = m; d_day = d; return 0; } 167
3. Narrow versus Wide Contracts (review)
Narrow versus Wide Contracts Narrow Contracts Imply Undefined Behavior: int Date::setDateIfValid(int y, int m, int d) { if (!isValid(y, m, d)) { return !0; } Wide Contract: d_year = y; d_month = m; Checked in d_day = d; Every Build Mode return 0; } 168
3. Narrow versus Wide Contracts (review)
Narrow versus Wide Contracts Narrow Contracts Imply Undefined Behavior: • What should happen when the behavior is undefined? TYPE& vector::operator[](int idx);
169
3. Narrow versus Wide Contracts (review)
Narrow versus Wide Contracts Narrow Contracts Imply Undefined Behavior: • What should happen when the behavior is undefined? TYPE& vector::operator[](int idx);
• Should what happens be part of the contract? TYPE& vector::at(int idx); 170
3. Narrow versus Wide Contracts (review)
Narrow versus Wide Contracts Narrow Contracts Imply Undefined Behavior: • What should happen when the behavior is undefined? It depends on the build mode. TYPE& vector::operator[](int idx);
• Should what happens be part of the contract? TYPE& vector::at(int idx); 171
3. Narrow versus Wide Contracts (review)
Narrow versus Wide Contracts Narrow Contracts Imply Undefined Behavior: • What should happen when the behavior is undefined? It depends on the build mode. TYPE& vector::operator[](int idx);
• Should what happens be part of the contract? If it is, then it’s essential behavior! TYPE& vector::at(int idx); 172
3. Narrow versus Wide Contracts (review)
Narrow versus Wide Contracts Narrow Contracts Imply Undefined Behavior: • What should happen when the behavior is undefined? It depends on the build mode. TYPE& vector::operator[](int idx);
• Should what happens be part of the contract? If it is, then it’s essential behavior! TYPE& vector::at(int idx); 173
3. Narrow versus Wide Contracts (review)
Narrow versus Wide Contracts Narrow Contracts Imply Undefined Behavior: • What should happen when the behavior is undefined? It depends on the build mode. TYPE& vector::operator[](int idx);
• Should what happens be part of the contract? If it is, then it’s essential behavior! TYPE& vector::at(int idx); 174
3. Narrow versus Wide Contracts (review)
Narrow versus Wide Contracts Narrow Contracts Imply Undefined Behavior: Should the behavior for void insert(int idx, const TYPE& value);
be defined when idx is greater than length() or less than zero?
175
3. Narrow versus Wide Contracts (review)
Narrow versus Wide Contracts Narrow Contracts Imply Undefined Behavior: Should the behavior for void insert(int idx, const TYPE& value);
be defined when idx is greater than length() or less than zero? If so, what should it be?
176
3. Narrow versus Wide Contracts (review)
Narrow versus Wide Contracts Narrow Contracts Imply Undefined Behavior: Should the behavior for void insert(int idx, const TYPE& value);
be defined when idx is greater than length() or less than zero? If so, what should it be? if (idx < 0) idx = 0; if (idx > length()) idx = length(); 177
3. Narrow versus Wide Contracts (review)
Narrow versus Wide Contracts Narrow Contracts Imply Undefined Behavior: Should the behavior for void insert(int idx, const TYPE& value);
be defined when idx is greater than length() or less than zero? If so, what should it be? if (idx < 0) idx = 0; if (idx > length()) idx = length(); idx = abs(idx) % (length() + 1); 178
3. Narrow versus Wide Contracts (review)
Narrow versus Wide Contracts Narrow Contracts Imply Undefined Behavior: Should the behavior for void insert(int idx, const TYPE& value);
be defined when idx is greater than length() or less than zero? If so, what should it be? if (idx < 0) idx = 0; if (idx > length()) idx = length(); idx = abs(idx) % (length() + 1); 179
3. Narrow versus Wide Contracts (review)
Narrow versus Wide Contracts Narrow Contracts Imply Undefined Behavior: Should the behavior for void insert(int idx, const TYPE& value);
be defined when idx is greater than length() or less than zero? If so, what should it be? if (idx < 0) idx = 0; if (idx > length()) idx = length(); idx = abs(idx) % (length() + 1); 180
3. Narrow versus Wide Contracts (review)
Narrow versus Wide Contracts Narrow Contracts Imply Undefined Behavior: Should the behavior for void insert(int idx, const TYPE& value);
be defined when idx is greater than length() or less than zero? If so, what should it be? if (idx < 0) idx = 0; if (idx > length()) idx = length(); idx = abs(idx) % (length() + 1); 181
3. Narrow versus Wide Contracts (review)
Narrow versus Wide Contracts Narrow Contracts Imply Undefined Behavior: Should the behavior for void insert(int idx, const TYPE& value);
be defined when idx is greater than length() or less than zero?
182
3. Narrow versus Wide Contracts (review)
Narrow versus Wide Contracts Narrow Contracts Imply Undefined Behavior: Should the behavior for void insert(int idx, const TYPE& value);
be defined when idx is greater than length() or less than zero? Answer: No!
183
3. Narrow versus Wide Contracts (review)
Narrow versus Wide Contracts Narrow Contracts Imply Undefined Behavior: Should the behavior for void insert(int idx, const TYPE& value);
be defined when idx is greater than length() or less than zero? Answer: No! assert(0 <= idx); assert(idx <= length()); 184
3. Narrow versus Wide Contracts (review)
Narrow versus Wide Contracts Narrow Contracts Imply Undefined Behavior: Should the behavior for void insert(int idx, const TYPE& value);
be defined when idx is greater than length() or less than zero? Answer: No! assert(0 <= idx); assert(idx <= length()); 185
3. Narrow versus Wide Contracts (review)
Appropriately Narrow Contracts
186
3. Narrow versus Wide Contracts (review)
Appropriately Narrow Contracts Narrow, but not too narrow.
187
3. Narrow versus Wide Contracts (review)
Appropriately Narrow Contracts Narrow, but not too narrow. Should the behavior for void replace(int index, const TYPE& value, int numElements); be defined when index is length() and numElements is zero? 188
3. Narrow versus Wide Contracts (review)
Appropriately Narrow Contracts ? E.g., length() is 5.
index 5 4 3 2 1 0 0 1
2
3
4
5
numElements
189
3. Narrow versus Wide Contracts (review)
Appropriately Narrow Contracts ? E.g., length() is 5.
index 5 4 3 2 1 0 0 1
2
3
4
5
numElements
void replace(int index, const TYPE& value, int numElements) { assert(0 <= index); assert(0 <= numElements); assert(index + numElements <= length()); // … }
190
3. Narrow versus Wide Contracts (review)
Appropriately Narrow Contracts X ?
index
E.g., length() is 5.
5 4 3 2 1 0 0 1
2
3
4
5
numElements
void replace(int index, const TYPE& value, int numElements) { assert(0 <= index); assert(0 <= numElements); assert(index + numElements <= length()); // … }
191
3. Narrow versus Wide Contracts (review)
Appropriately Narrow Contracts index 5
X ?
E.g., length() is 5.
Now a client 4 3 would have 2 to check for this 1 0 special case. 0 1 2 3 4 5 numElements void replace(int index, const TYPE& value, int numElements) { assert(0 <= index); assert(0 <= numElements); assert(index + numElements <= length()); // … }
192
3. Narrow versus Wide Contracts (review)
Appropriately Narrow Contracts index 5
X ?
E.g., length() is 5.
Now a client 4 3 would have 2 to check for this 1 0 special case. 0 1 2 3 4 5 numElements void replace(int index, const TYPE& value, int numElements) { assert(0 <= index); assert(0 <= numElements); assert(index + numElements <= length()); // … }
193
3. Narrow versus Wide Contracts (review)
Appropriately Narrow Contracts ? E.g., length() is 5.
index
Assuming no extra code is needed to handle it …
5 4 3 2 1 0 0 1
2
3
4
5
numElements
void replace(int index, const TYPE& value, int numElements) { assert(0 <= index); assert(0 <= numElements); assert(index + numElements <= length()); // … }
194
3. Narrow versus Wide Contracts (review)
Appropriately Narrow Contracts ! E.g., length() is 5.
index
Assuming no extra code is needed to handle it …
5 4 3 2 1 0 0 1
2
3
4
5
… it is naturally more efficient to allow it.
numElements
void replace(int index, const TYPE& value, int numElements) { assert(0 <= index); assert(0 <= numElements); assert(index + numElements <= length()); // … }
195
3. Narrow versus Wide Contracts (review)
End of Section
Questions? 196
3. Narrow versus Wide Contracts (review)
What Questions are we Answering? • What do we mean by a narrow versus a wide contract? – Should std::strlen(0) be required to do something reasonable? – Should Date::setDate(int, int, int) return a status?
• What should happen when the behavior is undefined? – Should what happens be part of the component-level contract?
• What about the behavior for these specific interfaces:
– Should operator[](int index) check to see if index is less than zero or greater than length()? • And what should happen if index is out of range?
– Should insert(int index, const TYPE& value) be defined when index is greater than length() or less than zero? – Should replace(int index, const TYPE& value, int numElements) be defined when index is length() and numElements is zero?
• What do we mean by Defensive Programming (DP)?
197
Outline 1. Components (review) Modularity, Logical/Physical Dependencies, & Level numbers
2. Interfaces and Contracts (review) Syntax versus Semantics & Essential Behavior
3. Narrow versus Wide Contracts (review) The Significance of Undefined Behavior
4. Proper Inheritance Is-A for Interface, Structural, & Implementation Inheritance
198
Outline 1. Components (review) Modularity, Logical/Physical Dependencies, & Level numbers
2. Interfaces and Contracts (review) Syntax versus Semantics & Essential Behavior
3. Narrow versus Wide Contracts (review) The Significance of Undefined Behavior
4. Proper Inheritance Is-A for Interface, Structural, & Implementation Inheritance
199
4. Proper Inheritance
Three Kinds of Inheritance
200
4. Proper Inheritance
Three Kinds of Inheritance There are three kinds of inheritance because there are three kinds of member functions:
201
4. Proper Inheritance
Three Kinds of Inheritance There are three kinds of inheritance because there are three kinds of member functions:
Public, Protected, Private? 202
4. Proper Inheritance
Three Kinds of Inheritance There are three kinds of inheritance because there are three kinds of member functions:
Public, Protected, Private? 203
4. Proper Inheritance
Three Kinds of Inheritance There are three kinds of inheritance because there are three kinds of member functions:
204
4. Proper Inheritance
Three Kinds of Inheritance There are three kinds of inheritance because there are three kinds of member functions:
• Interface Inheritance: – Pure Virtual Functions
205
4. Proper Inheritance
Three Kinds of Inheritance There are three kinds of inheritance because there are three kinds of member functions:
• Interface Inheritance: – Pure Virtual Functions
• Structural Inheritance: – Non-Virtual Functions
206
4. Proper Inheritance
Three Kinds of Inheritance There are three kinds of inheritance because there are three kinds of member functions:
• Interface Inheritance: – Pure Virtual Functions
• Structural Inheritance: – Non-Virtual Functions
• Implementation Inheritance: –Non-Pure Virtual Functions 207
4. Proper Inheritance
Interface Inheritance class TcpChannel : public Channel {
/* … */ public:
TcpChannel
// … (creators) virtual int read(char *buffer, int numBytes) {…} virtual int write(const char *buffer, int numBytes) {…} };
class Channel { public:
Channel
virtual ~Channel() { } virtual int read(char *buffer, int numBytes) = 0; virtual int write(const char *buffer, int numBytes) = 0; }; 208
4. Proper Inheritance
Structural Inheritance Pixel
class Pixel : public Point { public: enum Color { RED, GREEN, BLUE }; private: Color d_color; public: // … (creators) void setColor(Color color) { /* … */ } Color color ( ) const { /* … */ } };
Point
class Point { int d_x; int d_y; public: // … (creators) void setX(int x) { /* … */ } void setY(int y) { /* … */ } int x() const { /* … */ } int y() const { /* … */ } };
209
4. Proper Inheritance
Implementation Inheritance CompositeWidget
Widget
class CompositeWidget : public Widget { // … public: // … (creators) virtual const char *widgetCategory() const { return "COMP"; } virtual int numChildren() const { /* … */ } // … };
class Widget { Point d_origin; // … public: // … (creators) virtual bool isNameable() const { return false; } virtual const char *instanceName() const { return 0; } virtual bool hasLocation() const { return true; } virtual Point origin() const { return d_origin; } virtual const char *widgetCategory() const { return "LEAF"; } virtual int numChildren const { return 0; } // … };
210
4. Proper Inheritance
What Is Proper Inheritance? 2 Derived
Is-A
Implements Extends Is-Substitutable-For 1 Base
211
4. Proper Inheritance
What Is Proper Inheritance? • The “IsA” Relationship? – What does it mean?
212
4. Proper Inheritance
What Is Proper Inheritance? • The “IsA” Relationship? – What does it mean?
• Weaker Preconditions? • Stronger Postconditions? • Same Invariants?
213
4. Proper Inheritance
What Is Proper Inheritance? • The “IsA” Relationship? – What does it mean?
• • • •
Weaker Preconditions? Stronger Postconditions? Same Invariants? Providing a Proper Superset of Behavior?
214
4. Proper Inheritance
What Is Proper Inheritance? • The “IsA” Relationship? – What does it mean?
• • • • •
Weaker Preconditions? Stronger Postconditions? Same Invariants? Providing a Proper Superset of Behavior? Substitutability? – Of what? – What criteria?
215
4. Proper Inheritance
What Is Proper Inheritance? 2 Derived
Is-A
Implements Extends Is-Substitutable-For 1 Base
216
4. Proper Inheritance
What Is Proper Inheritance? 2 Derived
Is-A
Implements Extends Is-Substitutable-For 1 Base
The Is-A Relation: The implementation of a derived class must satisfy (simultaneously) its own contract, as well as that of “each” base class. 217
4. Proper Inheritance
What Is Proper Inheritance? What about the following general property: For inheritance to be proper, any operation that can be invoked on a derived-class object via a base-class pointer (or reference) must behave identically if we replace that baseclass pointer (or reference) with a corresponding derived-class one.
Note that this is how virtual functions behave! 218
4. Proper Inheritance
What Is Proper Inheritance? What about the following general property: For inheritance to be proper, any operation that can be invoked on a derived-class object via a base-class pointer (or reference) must behave identically if we replace that baseclass pointer (or reference) with a corresponding derived-class one.
Note that this is how virtual functions behave! 219
4. Proper Inheritance
What Is Proper Inheritance? What about the following general property: For inheritance to be proper, any operation that can be invoked on a derived-class object via a base-class pointer (or reference) must behave identically if we replace that baseclass pointer (or reference) with a corresponding derived-class one.
Note that this is how virtual functions behave! 220
4. Proper Inheritance
What Is Proper Inheritance?
Derived f(int, double, const char *)
Base f(int, double, const char *) 221
4. Proper Inheritance
What Is Proper Inheritance?
Derived f(int, double, const char *)
Base f(int, double, const char *) 222
4. Proper Inheritance
What Is Proper Inheritance?
Derived f(int, double, const char *)
Base f(int, double, const char *) 223
4. Proper Inheritance
What Is Proper Inheritance?
Derived f(int, double, const char *)
Base f(int, double, const char *) 224
4. Proper Inheritance
What Is Proper Inheritance? Derived *dp = new Derived(); Base *bp = dp;
// bp = 0x002140
Derived f(int, double, const char *)
Base f(int, double, const char *) 225
4. Proper Inheritance
What Is Proper Inheritance? Derived *dp = new Derived();
// dp = 0x002140
Base
// bp = 0x002140
*bp = dp;
Derived f(int, double, const char *)
Base f(int, double, const char *)
0x002130: 0x002138: 0x002140: 0x002148: 0x002150: 0x002158: 0x002160: 0x002168: 0x002170: 226
4. Proper Inheritance
What Is Proper Inheritance? Derived *dp = new Derived();
// dp = 0x002140
Base
// bp = 0x002140
*bp = dp;
Derived f(int, double, const char *)
Base f(int, double, const char *)
0x002130: 0x002138: 0x002140: 0x002148: 0x002150: 0x002158: 0x002160: 0x002168: 0x002170: 227
4. Proper Inheritance
What Is Proper Inheritance? Derived *dp = new Derived();
// dp = 0x002140
Base
// bp = 0x002140
*bp = dp;
Derived f(int, double, const char *)
Base f(int, double, const char *)
0x002130: 0x002138: 0x002140: 0x002148: 0x002150: 0x002158: 0x002160: 0x002168: 0x002170:
Object of type
Derived 228
4. Proper Inheritance
What Is Proper Inheritance? Derived *dp = new Derived();
// dp = 0x002140
Base
// bp = 0x002140
*bp = dp;
Derived f(int, double, const char *)
Base f(int, double, const char *)
0x002130: 0x002138: 0x002140: 0x002148: 0x002150: 0x002158: 0x002160: 0x002168: 0x002170:
Object of type
Derived 229
4. Proper Inheritance
What Is Proper Inheritance? Derived *dp = new Derived();
// dp = 0x002140
Base
// bp = 0x002140
*bp = dp;
Derived f(int, double, const char *)
Base f(int, double, const char *)
0x002130: 0x002138: 0x002140: 0x002148: 0x002150: 0x002158: 0x002160: 0x002168: 0x002170:
Object of type
Derived 230
4. Proper Inheritance
What Is Proper Inheritance? Derived *dp = new Derived();
// dp = 0x002140
Base
// bp = 0x002140
*bp = dp;
Derived f(int, double, const char *)
Base f(int, double, const char *)
0x002130: 0x002138: 0x002140: 0x002148: 0x002150: 0x002158: 0x002160: 0x002168: 0x002170:
Object of type
Derived 231
4. Proper Inheritance
What Is Proper Inheritance? Derived *dp = new Derived();
// dp = 0x002140
Base
// bp = 0x002140
*bp = dp;
Derived f(int, double, const char *)
Base f(int, double, const char *)
0x002130: 0x002138: 0x002140: 0x002148: 0x002150: 0x002158: 0x002160: 0x002168: 0x002170:
Object of type
Derived 232
4. Proper Inheritance
What Is Proper Inheritance? Derived *dp = new Derived();
// dp = 0x002140
Base
// bp = 0x002140
*bp = dp;
Derived f(int, double, const char *)
Base f(int, double, const char *)
0x002130: 0x002138: 0x002140: 0x002148: 0x002150: 0x002158: 0x002160: 0x002168: 0x002170:
Object of type
Derived 233
4. Proper Inheritance
What Is Proper Inheritance? Derived *dp = new Derived();
// dp = 0x002140
Base
// bp = 0x002140
*bp = dp;
bp->f(1, 2.0, “three”);
Derived f(int, double, const char *)
Base f(int, double, const char *)
0x002130: 0x002138: 0x002140: 0x002148: 0x002150: 0x002158: 0x002160: 0x002168: 0x002170:
Object of type
Derived 234
4. Proper Inheritance
What Is Proper Inheritance? Derived *dp = new Derived();
// dp = 0x002140
Base
// bp = 0x002140
*bp = dp;
bp->f(1, 2.0, “three”); dp->f(1, 2.0, “three”);
Derived f(int, double, const char *)
Base f(int, double, const char *)
0x002130: 0x002138: 0x002140: 0x002148: 0x002150: 0x002158: 0x002160: 0x002168: 0x002170:
Object of type
Derived 235
4. Proper Inheritance
What Is Proper Inheritance? Derived *dp = new Derived();
// dp = 0x002140
Base
// bp = 0x002140
*bp = dp;
bp->f(1, 2.0, “three”); dp->f(1, 2.0, “three”);
Derived f(int, double, const char *)
Base f(int, double, const char *)
Identical Behavior 0x002130: 0x002138: 0x002140: 0x002148: 0x002150: 0x002158: 0x002160: 0x002168: 0x002170:
Object of type
Derived 236
4. Proper Inheritance
What Is Proper Inheritance? What about the following general property: For inheritance to be proper, any operation that can be invoked on a derived-class object via a base-class pointer (or reference) must behave identically if we replace that baseclass pointer (or reference) with a corresponding derived-class one.
Note that this is how virtual functions behave! 237
4. Proper Inheritance
What Is Proper Inheritance? What about the following general property: For inheritance to be proper, any operation that can be invoked on a derived-class object via a base-class pointer (or reference) must behave identically if we replace that baseclass pointer (or reference) with a corresponding derived-class one.
Note that this is how virtual functions behave! 238
4. Proper Inheritance
What Is Proper Inheritance? Derived::f(int x); Derived::g(int x); Derived::h();
Derived
Base Base::f(int x); Base::g(int x);
// Defined for all x. // Defined for all x. // Note: not accessible from Base class.
g(int) f(int) h()
Derived Interface and Contract
g(int) f(int) // Defined for all x. // Defined only for 0 <= x.
Base Interface and Contract 239
4. Proper Inheritance
What Is Proper Inheritance? Derived::f(int x); Derived::g(int x); Derived::h();
Derived
Base Base::f(int x); Base::g(int x);
// Defined for all x. // Defined for all x. // Note: not accessible from Base class.
g(int) f(int) h()
Derived Interface and Contract
g(int) f(int) // Defined for all x. // Defined only for 0 <= x.
Base Interface and Contract 240
4. Proper Inheritance
What Is Proper Inheritance? Derived::f(int x); Derived::g(int x); Derived::h();
Derived
Base Base::f(int x); Base::g(int x);
// Defined for all x. // Defined for all x. // Note: not accessible from Base class.
g(int) f(int) h()
Derived Interface and Contract
g(int) f(int) // Defined for all x. // Defined only for 0 <= x.
Base Interface and Contract 241
4. Proper Inheritance
What Is Proper Inheritance? Derived::f(int x); Derived::g(int x); Derived::h();
Derived
Base Base::f(int x); Base::g(int x);
// Defined for all x. // Defined for all x. // Note: not accessible from Base class.
g(int) f(int) h()
Derived Interface and Contract
g(int) f(int) // Defined for all x. // Defined only for 0 <= x.
Base Interface and Contract 242
4. Proper Inheritance
Pure Interface Inheritance Constructors
D::f
Implementation
Derived
B::f
Interface
Base
For each function D::f in the derived class overriding a virtual one B::f in the base class, the (documented) preconditions of D::f must be no stronger than those for B::f, and the postconditions no weaker. 243
4. Proper Inheritance
Pure Interface Inheritance Constructors
D::f
Implementation
Derived Implements the Interface B::f
Interface
Base
For each function D::f in the derived class overriding a virtual one B::f in the base class, the (documented) preconditions of D::f must be no stronger than those for B::f, and the postconditions no weaker. 244
4. Proper Inheritance
Pure Interface Inheritance Constructors
D::f
Implementation
Derived Implements the Interface B::f
Interface
Base
For each function D::f in the derived class overriding a virtual one B::f in the base class, the (documented) preconditions of D::f must be no stronger than those for B::f, and the postconditions no weaker. 245
4. Proper Inheritance
Pure Interface Inheritance Constructors
D::f
Implementation
Derived Implements the Interface B::f
Interface
Base
For each function D::f in the derived class overriding a virtual one B::f in the base class, the (documented) are typically the same as preconditions of D::f must be no stronger than those for B::f, and the postconditions no weaker. 246
4. Proper Inheritance
Pure Interface Inheritance
B::f
Channel
247
4. Proper Inheritance
Pure Interface Inheritance
B::f
Channel
virtual int write(const char *buffer, int numBytes) = 0; // Write the specified 'numBytes' from the specified // 'buffer'. Return 0 on success, and a non-zero value // otherwise. The behavior is undefined unless // '0 <= numBytes <= 32767'.
248
4. Proper Inheritance
Pure Interface Inheritance D::f
TcpChannel
B::f
Channel
virtual int write(const char *buffer, int numBytes) = 0; // Write the specified 'numBytes' from the specified // 'buffer'. Return 0 on success, and a non-zero value // otherwise. The behavior is undefined unless // '0 <= numBytes <= 32767'.
249
4. Proper Inheritance
Pure Interface Inheritance D::f
TcpChannel
B::f
Channel
virtual int write(const char *buffer, int numBytes); // Write to this TCP/IP channel the specified // 'numBytes' from the specified 'buffer'. Return 0 on // success, and a non-zero value otherwise. The // behavior is undefined unless '0 == numBytes % 4'.
virtual int write(const char *buffer, int numBytes) = 0; // Write the specified 'numBytes' from the specified // 'buffer'. Return 0 on success, and a non-zero value // otherwise. The behavior is undefined unless // '0 <= numBytes <= 32767'.
250
4. Proper Inheritance
Pure Interface Inheritance D::f
TcpChannel
B::f
Channel
virtual int write(const char *buffer, int numBytes); // Write to this TCP/IP channel the specified // 'numBytes' from the specified 'buffer'. Return 0 on // success, 1 if '0 != numBytes % 4', and a negative // value otherwise.
virtual int write(const char *buffer, int numBytes) = 0; // Write the specified 'numBytes' from the specified // 'buffer'. Return 0 on success, and a non-zero value // otherwise. The behavior is undefined unless // '0 <= numBytes <= 32767'.
251
4. Proper Inheritance
Pure Interface Inheritance D::f
TcpChannel
B::f
Channel
virtual int write(const char *buffer, int numBytes); // Write to this TCP/IP channel the specified // 'numBytes' from the specified 'buffer'. Return 0 on // success, and a non-zero value otherwise. Note that // this functionality is not yet implemented on Windows; // on that platform, this function always returns -1.
virtual int write(const char *buffer, int numBytes) = 0; // Write the specified 'numBytes' from the specified // 'buffer'. Return 0 on success, and a non-zero value // otherwise. The behavior is undefined unless // '0 <= numBytes <= 32767'.
252
4. Proper Inheritance
What Is a Proper Subtype/Subclass?
253
4. Proper Inheritance
What Is a Proper Subtype/Subclass? “A type hierarchy is composed of subtypes and supertypes. The intuitive idea of a subtype is one whose objects provide all the behavior of objects of another type (the supertype) plus something extra.” – Barbara Liskov .
(OOPSLA ’87)
254
4. Proper Inheritance
What Is a Proper Subtype/Subclass? “A type hierarchy is composed of subtypes and supertypes. The intuitive idea of a subtype is one whose objects provide all the behavior of objects of another type (the supertype) plus something extra.” – Barbara Liskov .
(OOPSLA ’87)
Can
create it. TcpChannel
Channel
Interface Inheritance
255
4. Proper Inheritance
What Is a Proper Subtype/Subclass? “A type hierarchy is composed of subtypes and supertypes. The intuitive idea of a subtype is one whose objects provide all the behavior of objects of another type (the supertype) plus something extra.” – Barbara Liskov .
(OOPSLA ’87)
Can
create it.
Can do something
more with it.
TcpChannel
Pixel
Channel
Point
Interface
Structural
Inheritance
Inheritance
256
4. Proper Inheritance
What Is a Proper Subtype/Subclass? “A type hierarchy is composed of subtypes and supertypes. The intuitive idea of a subtype is one whose objects provide all the behavior of objects of another type (the supertype) plus something extra.” – Barbara Liskov .
(OOPSLA ’87)
Can
create it.
Can do something
Can create something
more with it.
else with it. CompositeWidget
TcpChannel
Pixel
Widget
Channel
Point
Interface
Structural
Implementation
Inheritance
Inheritance
Inheritance
257
4. Proper Inheritance
What Is Liskov Substitution?
258
4. Proper Inheritance
What Is Liskov Substitution? What exactly is the Liskov Substitution Principle (LSP)?
259
4. Proper Inheritance
What Is Liskov Substitution? What exactly is the Liskov Substitution Principle (LSP)? • What motivated LSP in the first place?
260
4. Proper Inheritance
What Is Liskov Substitution? What exactly is the Liskov Substitution Principle (LSP)? • What motivated LSP in the first place? • (How?) Does LSP relate to inheritance in C++?
261
4. Proper Inheritance
What Is Liskov Substitution? What exactly is the Liskov Substitution Principle (LSP)? • What motivated LSP in the first place? • (How?) Does LSP relate to inheritance in C++? • After Liskov substitution is applied, can (observable) behavior be (subtly) different?
262
4. Proper Inheritance
What Is Liskov Substitution? What exactly is the Liskov Substitution Principle (LSP)? • What motivated LSP in the first place? • (How?) Does LSP relate to inheritance in C++? • After Liskov substitution is applied, can (observable) behavior be (subtly) different? • Does LSP apply to all three kinds of inheritance?
263
4. Proper Inheritance
What Is Liskov Substitution? What exactly is the Liskov Substitution Principle (LSP)? • What motivated LSP in the first place? • (How?) Does LSP relate to inheritance in C++? • After Liskov substitution is applied, can (observable) behavior be (subtly) different? • Does LSP apply to all three kinds of inheritance? • Does LSP have any other practical applications? 264
4. Proper Inheritance
What Is Liskov Substitution? What exactly is the Liskov Substitution Principle (LSP)? • What motivated LSP in the first place? • (How?) Does LSP relate to inheritance in C++? • After Liskov substitution is applied, can (observable) behavior be (subtly) different? • Does LSP apply to all three kinds of inheritance? • Does LSP have any other practical applications? • Let’s have a look… 265
4. Proper Inheritance
What Is Liskov Substitution? “If for each object o1 of type S there is an object o2 of type T such that for all programs P defined in terms of T, the behavior of P is unchanged when o1 is substituted for o2, then S is a subtype of T.” – Barbara Liskov (OOPSLA ’87)
266
4. Proper Inheritance
What Is Liskov Substitution? “If for each object o1 of type S there is an object o2 of type T such that for all programs P defined in terms of T, the behavior of P is unchanged when o1 is substituted for o2, then S is a subtype of T.” – Barbara Liskov (OOPSLA ’87)
267
4. Proper Inheritance
What Is Liskov Substitution? “If for each object o1 of type S there is an object o2 of type T such that for all programs P defined in terms of T, the behavior of P is unchanged when o1 is substituted for o2, then S is a subtype of T.” – Barbara Liskov (OOPSLA ’87)
main() { Fool f0(false); Fool f1(true); // … p(f1, f0, …); } 268
4. Proper Inheritance
What Is Liskov Substitution? “If for each object o1 of type S there is an object o2 of type T such that for all programs P defined in terms of T, the behavior of P is unchanged when o1 is substituted for o2, then S is a subtype of T.” – Barbara Liskov (OOPSLA ’87) class Fool : public Bool { public: }; Fool(int x) : Bool(!x) { }
main() { Fool f0(false); Fool f1(true); // … p(f1, f0, …); } 269
4. Proper Inheritance
What Is Liskov Substitution? “If for each object o1 of type S there is an object o2 of type T such that for all programs P defined in terms of T, the behavior of P is unchanged when o1 is substituted for o2, then S is a subtype of T.” – Barbara Liskov (OOPSLA ’87) class Fool : public Bool { public: }; Fool(int x) : Bool(!x) { }
main() { Bool b0(false); Bool b1(true); // … p(b0, b1, …); }
main() { Fool f0(false); Fool f1(true); // … p(f1, f0, …); } 270
4. Proper Inheritance
What Is Liskov Substitution? “If for each object o1 of type S there is an object o2 of type T such that for all programs P defined in terms of T, the behavior of P is unchanged when o1 is substituted for o2, then S is a subtype of T.” – Barbara Liskov (OOPSLA ’87) class Bool { bool d_v; public: Bool(int x) : d_v(x) { } operator bool() const {return d_v;} };
main() { Bool b0(false); Bool b1(true); // … p(b0, b1, …); }
class Fool : public Bool { public: }; Fool(int x) : Bool(!x) { }
main() { Fool f0(false); Fool f1(true); // … p(f1, f0, …); } 271
4. Proper Inheritance
What Is Liskov Substitution? “If for each object o1 of type S there is an object o2 of type T such that for all programs P defined in terms of T, the behavior of P is unchanged when o1 is substituted for o2, then S is a subtype of T.” – Barbara Liskov (OOPSLA ’87) class Bool { bool d_v; public: Bool(int x) : d_v(x) { } operator bool() const {return d_v;} };
class Fool : public Bool { public: }; Fool(int x) : Bool(!x) { }
void p(const Bool& x, const Bool& y, …) { /* … */ } main() { Bool b0(false); Bool b1(true); // … p(b0, b1, …); }
main() { Fool f0(false); Fool f1(true); // … p(f1, f0, …); } 272
4. Proper Inheritance
What Is Liskov Substitution? “If for each object o1 of type S there is an object o2 of type T such that for all programs P defined in terms of T, the behavior of P is unchanged when o1 is substituted for o2, then S is a subtype of T.” – Barbara Liskov (OOPSLA ’87) class Bool { bool d_v; public: Bool(int x) : d_v(x) { } operator bool() const {return d_v;} };
class Fool : public Bool { public: }; Fool(int x) : Bool(!x) { }
void p(const Bool& x, const Bool& y, …) { /* … */ } main() { Bool b0(false); Bool b1(true); // … p(b0, b1, …); }
Note order is different!
main() { Fool f0(false); Fool f1(true); // … p(f1, f0, …); } 273
4. Proper Inheritance
What Is Liskov Substitution? “If for each object o1 of type S there is an object o2 of type T such that for all programs P defined in terms of T, the behavior of P is unchanged when o1 is substituted for o2, then S is a subtype of T.” – Barbara Liskov (OOPSLA ’87) class Bool { bool d_v; public: Bool(int x) : d_v(x) { } operator bool() const {return d_v;} };
class Fool : public Bool { public: }; Fool(int x) : Bool(!x) { }
void p(const Bool& x, const Bool& y, …) { /* … */ } main() { Bool b0(false); Bool b1(true); // … p(b0, b1, …); }
main() { Fool f0(false); Fool f1(true); // … p(f1, f0, …); } 274
4. Proper Inheritance
What Is Liskov Substitution? “If for each object o1 of type S there is an object o2 of type T such that for all programs P defined in terms of T, the behavior of P is unchanged when o1 is substituted for o2, then S is a subtype of T.” – Barbara Liskov (OOPSLA ’87) class Bool { bool d_v; public: Bool(int x) : d_v(x) { } operator bool() const {return d_v;} };
class Fool : public Bool { public: }; Fool(int x) : Bool(!x) { }
void p(const Bool& x, const Bool& y, …) { /* … */ } main() { Bool b0(false); Bool b1(true); // … p(b0, b1, …); }
Note order is different!
main() { Fool f0(false); Fool f1(true); // … p(f1, f0, …); } 275
4. Proper Inheritance
What Is Liskov Substitution? “If for each object o1 of type S there is an object o2 of type T such that for all programs P defined in terms of T, the behavior of P is unchanged when o1 is substituted for o2, then S is a subtype of T.” – Barbara Liskov (OOPSLA ’87)
276
4. Proper Inheritance
What Is Liskov Substitution? “If for each object o1 of type S there is an object o2 of type T such that for all programs P defined in terms of T, the behavior of P is unchanged when o1 is substituted for o2, then S is a subtype of T.” – Barbara Liskov (OOPSLA ’87)
277
4. Proper Inheritance
What Is Liskov Substitution? “If for each object o1 of type S there is an object o2 of type T such that for all programs P defined in terms of T, the behavior of P is unchanged when o1 is substituted for o2, then S is a subtype of T.” – Barbara Liskov (OOPSLA ’87) If, for each “derived-class” object o1 of type S, there exists a “base-class” object o2 of type T such that, for all programs P defined in terms of type T, the behavior of P is unchanged when the “derived-class” object o1 is substituted for the “base-class” object o2, then S is a subtype of T.
278
4. Proper Inheritance
What Is Liskov Substitution? “If for each object o1 of type S there is an object o2 of type T such that for all programs P defined in terms of T, the behavior of P is unchanged when o1 is substituted for o2, then S is a subtype of T.” – Barbara Liskov (OOPSLA ’87) If, for each “derived-class” object o1 of type S, there exists a “base-class” object o2 of type T such that, for all programs P defined in terms of type T, the behavior of P is unchanged when the “derived-class” object o1 is substituted for the “base-class” object o2, then S is a subtype of T. If, for each “derived-class” object d of type D, there exists a “base-class” object b of type B such that, for all programs P defined in terms of type B, the behavior of P is unchanged when the “derived-class” object d is substituted for the “base-class” object b, then D is a subtype of B.
279
4. Proper Inheritance
What Is Liskov Substitution? “If for each object o1 of type S there is an object o2 of type T such that for all programs P defined in terms of T, the behavior of P is unchanged when o1 is substituted for o2, then S is a subtype of T.” – Barbara Liskov (OOPSLA ’87) If, for each “derived-class” object o1 of type S, there exists a “base-class” object o2 of type T such that, for all programs P defined in terms of type T, the behavior of P is unchanged when the “derived-class” object o1 is substituted for the “base-class” object o2, then S is a subtype of T. If, for each “derived-class” object d of type D, there exists a “base-class” object b of type B such that, for all programs P defined in terms of type B, the behavior of P is unchanged when the “derived-class” object d is substituted for the “base-class” object b, then D is a subtype of B. If, for each object d of type D, there exists an object b of type B such that, for all programs P defined in terms of B, the behavior of P is unchanged when d is substituted for b, then D is a subtype of B. 280
4. Proper Inheritance
What Is Liskov Substitution? “If for each object o1 of type S there is an object o2 of type T such that for all programs P defined in terms of T, the behavior of P is unchanged when o1 is substituted for o2, then S is a subtype of T.” – Barbara Liskov (OOPSLA ’87) If, for each “derived-class” object o1 of type S, there exists a “base-class” object o2 of type T such that, for all programs P defined in terms of type T, the behavior of P is unchanged when the “derived-class” object o1 is substituted for the “base-class” object o2, then S is a subtype of T. If, for each “derived-class” object d of type D, there exists a “base-class” object b of type B such that, for all programs P defined in terms of type B, the behavior of P is unchanged when the “derived-class” object d is substituted for the “base-class” object b, then D is a subtype of B. If, for each object d of type D, there exists an object b of type B such that, for all programs P defined in terms of B, the behavior of P is unchanged when d is substituted for b, then D is a subtype of B. 281
4. Proper Inheritance
What Is Liskov Substitution? If, for each object d of type D, there exists an object b of type B such that, for all programs P defined in terms of B, the behavior of P is unchanged when d is substituted for b, then D is a subtype of B. class Bool { bool d_v; public: Bool(int x) : d_v(x) { } operator bool() const {return d_v;} };
class Fool : public Bool { public: }; Fool(int x) : Bool(!x) { }
void p(const Bool& x, const Bool& y, …) { /* … */ } main() { Bool b0(false); Bool b1(true); // … p(b0, b1, …); }
main() { Fool f0(false); Fool f1(true); // … p(f1, f0, …); } 282
4. Proper Inheritance
What Is Liskov Substitution? If, for each object d of type D, there exists an object b of type B such that, for all programs P defined in terms of B, the behavior of P is unchanged when d is substituted for b, then D is a subtype of B. class Bool { bool d_v; public: Bool(int x) : d_v(x) { } operator bool() const {return d_v;} };
class Fool : public Bool { public: }; Fool(int x) : Bool(!x) { }
void p(const Bool& x, const Bool& y, …) { /* … */ } main() { Bool b0(false); Bool b1(true); // … p(b0, b1, …); }
main() { Fool f0(false); Fool f1(true); // … p(f1, f0, …); } 283
4. Proper Inheritance
What Is Liskov Substitution? If, for each object d of type D, there exists an object b of type B such that, for all programs P defined in terms of B, the behavior of P is unchanged when d is substituted for b, then D is a subtype of B. class Bool { bool d_v; public: Bool(int x) : d_v(x) { } operator bool() const {return d_v;} };
class Fool : public Bool { public: }; Fool(int x) : Bool(!x) { }
void p(const Bool& x, const Bool& y, …) { /* … */ } main() { Bool b0(false); Bool b1(true); // … p(b0, b1, …); }
main() { Fool f0(false); Fool f1(true); // … p(f1, f0, …); } 284
4. Proper Inheritance
What Is Proper Inheritance? Recall the following general property: For inheritance to be proper, any operation that can be invoked on a derived-class object via a base-class pointer (or reference) must behave identically if we replace that baseclass pointer (or reference) with a corresponding derived-class one.
Note that this is how virtual functions behave! 285
4. Proper Inheritance
What Is Proper Inheritance? void example(Derived *pDerived) { Base *pBase = pDerived; #ifdef USE_DERIVED_CLASS_INTERFACE pDerived->someMethod(/* … */);
Derived
int result = someFunction(*pDerive); #else pBase->someMethod(/* … */);
Base
int result = someFunction(*pBase);
#endif 286
4. Proper Inheritance
Pure Structural Inheritance #ifdef USE_BASE_CLASS_INTERFACE typedef Point Type; #else typedef Pixel Type; #endif void anyProgram(Type *p);
Pixel
Point
void main() { Pixel pixel(1, 2, Pixel::BLUE); anyProgram(&pixel); } using std::cout; // (We do this only using std::endl; // in test drivers.) 287
4. Proper Inheritance
Pure Structural Inheritance void anyProgram(Type *p) { cout << p->x() << endl; }
Pixel
Point
int Point::x() const { return d_x; } 288
4. Proper Inheritance
Pure Structural Inheritance void anyProgram(Type *p) { p->setY(10); }
Pixel
Point
void Point::setY(int y) { d_y = y; } 289
4. Proper Inheritance
Pure Structural Inheritance class Pixel : public Point { // … Color d_color; // … };
Pixel
void anyProgram(Type *p) { cout << p->color() << endl; }
Pixel::Color Pixel::color() const { return d_color; }
Point
290
4. Proper Inheritance
Pure Structural Inheritance void anyProgram(Type *p) { p->setY(10); }
Pixel
void Pixel::setY(int y) { cout << "Pixel::setY(int y)" << endl; d_y = y; }
Point
void Point::setY(int y) { d_y = y; } 291
4. Proper Inheritance
Pure Structural Inheritance void anyProgram(Type *p) { p->setY(10); }
Pixel
void Pixel::setY(int y) { cout << "Pixel::setY(int y)" << endl; d_y = y; }
Point
void Point::setY(int y) { d_y = y; }
!
292
4. Proper Inheritance
Pure Structural Inheritance class Pixel : public Point { // … static int s_numSetY; public: // … };
void anyProgram(Type *p) { p->setY(10); }
Pixel
void Pixel::setY(int y) { ++s_numSetY; // Pixel class data d_y = y; }
Point
void Point::setY(int y) { d_y = y; } 293
4. Proper Inheritance
Pure Structural Inheritance class Pixel : public Point { // … static int s_numSetY; public: // … };
Pixel
Point
void anyProgram(Type *p) { p->setY(10); cout << p->numSetY() << endl; }
void Pixel::setY(int y) { ++s_numSetY; // Pixel class data d_y = y; } int Pixel::numSetY() { return s_numSetY; } void Point::setY(int y) { d_y = y; } 294
4. Proper Inheritance
Pure Structural Inheritance void anyProgram(Type *p) { p->setY(10); }
Pixel
Point
void Pixel::setY(int y) // Set the y-coordinate of this object to the absolute value of the // specified 'y'. The behavior is undefined unless 'INT_MIN < y'. { d_y = y < 0 ? -y : y; } void Point::setY(int y) // Set the y-coordinate of this object to the specified 'y'. // The behavior is undefined unless '0 <= y'. { d_y = y; }
295
4. Proper Inheritance
Pure Structural Inheritance class Pixel : public Point { // … Color d_color; // … };
void anyProgram(Type *p) { if (sizeof p->self() > sizeof(Point)) cout << "It's not a Point!" << endl; }
Pixel
const Pixel& Pixel::self() const { return *this; }
Point
const Point& Point::self() const { return *this; } 296
4. Proper Inheritance
Pure Structural Inheritance class Pixel : public Point { // … Color d_color; // … };
void anyProgram(Type *p) { if (sizeof p->self() > sizeof(Point)) ? cout << "It's not a Point!" << endl; }
Pixel
const Pixel& Pixel::self() const { return *this; }
Point
const Point& Point::self() const { return *this; } 297
4. Proper Inheritance
Pure Structural Inheritance class Pixel : public Point { // … Color d_color; // … };
void anyProgram(Type *p) { if (sizeof p->self() > 8) // sizeof(Point) cout << "It's not a Point!" << endl; }
Pixel
const Pixel& Pixel::self() const { return *this; }
Point
const Point& Point::self() const { return *this; } 298
4. Proper Inheritance
Pure Structural Inheritance class Pixel : public Point { // … Color d_color; // … };
void anyProgram(Type *p) { if (sizeof p->self() > 8) // sizeof(Point) cout << "It's not a Point!" << endl; }
Pixel
const Pixel& Pixel::self() const { return *this; }
Point
const Point& Point::self() const { return *this; } 299
4. Proper Inheritance
Pure Structural Inheritance class Pixel : public Point { // … Color d_color; // … };
void anyProgram(Type *p) { if (sizeof p->self() > 8) // sizeof(Point) cout << "It's not a point!" << endl; }
Pixel
const Pixel& Pixel::self() const { return *this; }
Point
const Point& Point::self() const { return *this; } 300
4. Proper Inheritance
Pure Structural Inheritance class Pixel : public Point { // … Color d_color; // … };
void anyProgram(Type *p) { if (sizeof p->self() > sizeof(Point)) cout << "It's not a Point!" << endl; }
Pixel
const Pixel& Pixel::self() const { return *this; }
Point
const Point& Point::self() const { return *this; } 301
4. Proper Inheritance
Pure Structural Inheritance class Pixel : public Point { // … Color d_color; // … };
void anyProgram(Type *p) { if (sizeof p->self() > sizeof(Point)) cout << "It's not a Point!" << endl; }
Pixel
const Pixel& Pixel::self() const { return *this; }
Point
const Point& Point::self() const { return *this; }
!
302
4. Proper Inheritance
Pure Structural Inheritance class Pixel : public Point { // … Color d_color; // … };
void anyProgram(Type *p) { if (sizeof *p > sizeof(Point)) cout << "It's not a Point!" << endl; }
Pixel
Point
303
4. Proper Inheritance
Pure Structural Inheritance class Pixel : public Point { // … Color d_color; // … };
!
void anyProgram(Type *p) { if (sizeof *p > sizeof(Point)) cout << "It's not a Point!" << endl; }
Pixel
Point
304
4. Proper Inheritance
Pure Structural Inheritance class Pixel : public Point { // … Color d_color; // … };
!
void anyProgram(Type *p) { if (sizeof *p > sizeof(Point)) cout << "It's not a Point!" << endl; }
Pixel
Point
305
4. Proper Inheritance
Pure Structural Inheritance class Pixel : public Point { // … Color d_color; // … };
!
Pixel
Point
void anyProgram(Type *p) { if (sizeof *p > sizeof(Point)) cout << "It's not a Point!" << endl; }
Proper Structural Inheritance extends functionality, but does not extend the object’s footprint.
306
4. Proper Inheritance
Pure Structural Inheritance class Pixel : public Point { // … Color d_color; // … };
!
Pixel
void anyProgram(Type *p) { double alignmentHack; // Don’t do it! char buffer[sizeof(Point)]; (Type *)&buffer = *p; } ?!?!
buffer
Point buffer
The same “size” issue applies to arrays of objects! 307
4. Proper Inheritance
Implementation Inheritance CompositeWidget
Implementation hierarchies are highly problematic! Incorporating implementation with interface inheritance:
Widget
– Makes software brittle, inflexible, and hard to maintain. – Exposes public clients to physical (compileand link-time) dependencies on the shared implementation. – Adds nothing that cannot be done with pure interface inheritance and layering.
Its only value is as a syntactic expedient! 308
4. Proper Inheritance
Using Interface Inheritance Effectively Point
Shape
Polygon
Square
Rectangle
MyRectangle
309
4. Proper Inheritance
Using Interface Inheritance Effectively Point
x; y
Shape origin; numVertices; operator[](int index)
Polygon
origin
Square
Rectangle
origin; width
origin; width; length
MyRectangle
310
4. Proper Inheritance
Using Interface Inheritance Effectively Point
x; y
Shape origin; numVertices; operator[](int index)
origin
Polygon
Square
A Rectangle Is-A Square with a length attribute? origin; width
? Rectangle
origin; width; length
MyRectangle
311
4. Proper Inheritance
Using Interface Inheritance Effectively Point
x; y
Shape origin; numVertices; operator[](int index)
origin
Polygon
Square
A Rectangle Is-A Square with a length attribute? origin; width
? Rectangle
MyRectangle
origin; width; length
Rectangle does not respect Square Invariant
312
4. Proper Inheritance
Using Interface Inheritance Effectively Point
x; y
Shape origin; numVertices; operator[](int index)
origin
Polygon
Square
A Rectangle Is-A Square with a length attribute? origin; width
? Rectangle
origin; width; length
MyRectangle
313
4. Proper Inheritance
Using Interface Inheritance Effectively Point
x; y
Shape origin; numVertices; operator[](int index)
Polygon
origin
Square
Rectangle
origin; width
origin; width; length
MyRectangle
314
4. Proper Inheritance
Using Interface Inheritance Effectively Point
x; y
Shape origin; numVertices; operator[](int index)
Polygon
origin
Square
Rectangle
origin; width
origin; width; length
MyRectangle
315
4. Proper Inheritance
Using Interface Inheritance Effectively Point
x; y
Shape origin; numVertices; operator[](int index)
Polygon
origin
Square
Rectangle
origin; width
origin; width; length
MyRectangle
316
4. Proper Inheritance
Using Interface Inheritance Effectively Point
x; y
Shape origin; numVertices; operator[](int index)
Polygon
origin
Square
Rectangle
origin; width
origin; width; length
MyRectangle
317
4. Proper Inheritance
Using Interface Inheritance Effectively Square
Rectangle
318
4. Proper Inheritance
Using Interface Inheritance Effectively Square
Rectangle
void stretchBy1(Rectangle *r) { int wid = r->width(); int len = r->length(); r->setLength(len + 1); assert(wid == r->width()); assert(len + 1 == r->length()); } 319
4. Proper Inheritance
Using Interface Inheritance Effectively Square
Rectangle
void stretchBy1(Rectangle *r) { int wid = r->width(); int len = r->length(); r->setLength(len + 1); assert(wid == r->width()); assert(len + 1 == r->length()); } 320
4. Proper Inheritance
Using Interface Inheritance Effectively Square
Rectangle
void stretchBy1(Rectangle *r) { int wid = r->width(); int len = r->length(); r->setLength(len + 1); assert(wid == r->width()); assert(len + 1 == r->length()); } 321
4. Proper Inheritance
Using Interface Inheritance Effectively Square
Rectangle
void stretchBy1(Rectangle *r) { int wid = r->width(); int len = r->length(); r->setLength(len + 1); assert(wid == r->width()); assert(len + 1 == r->length()); } 322
4. Proper Inheritance
Using Interface Inheritance Effectively Square
Rectangle
void stretchBy1(Rectangle *r) { int wid = r->width(); int len = r->length(); r->setLength(len + 1); assert(wid == r->width()); assert(len + 1 == r->length()); } 323
4. Proper Inheritance
Using Interface Inheritance Effectively Square
Rectangle
void stretchBy1(Rectangle *r) { int wid = r->width(); int len = r->length(); r->setLength(len + 1); assert(wid == r->width()); assert(len + 1 == r->length()); } 324
4. Proper Inheritance
Using Interface Inheritance Effectively Square
Rectangle
void stretchBy1(Rectangle *r) { int wid = r->width(); int len = r->length(); r->setLength(len + 1); assert(wid == r->width()); assert(len + 1 == r->length()); } 325
4. Proper Inheritance
Using Interface Inheritance Effectively Square
Rectangle
Either Assert or No Longer Square
void stretchBy1(Rectangle *r) { int wid = r->width(); int len = r->length(); r->setLength(len + 1); assert(wid == r->width()); assert(len + 1 == r->length()); } 326
4. Proper Inheritance
Using Interface Inheritance Effectively Square
Rectangle
Either Assert or No Longer Square
void stretchBy1(Rectangle *r) { int wid = r->width(); int len = r->length(); r->setLength(len + 1); assert(wid == r->width()); assert(len + 1 == r->length()); } 327
4. Proper Inheritance
Using Interface Inheritance Effectively Point
x; y
Shape origin; numVertices; operator[](int index)
Polygon
origin
Square
Rectangle
origin; width
origin; width; length
MyRectangle
328
4. Proper Inheritance
Using Interface Inheritance Effectively Point
x; y
Shape origin; numVertices; operator[](int index)
Polygon
origin
Square
Rectangle
origin; width
origin; width; length
MyRectangle
329
4. Proper Inheritance
Using Interface Inheritance Effectively Point
x; y
Shape origin; numVertices; operator[](int index)
Polygon
origin
Square
Rectangle
origin; width
origin; width; length
MyRectangle
330
4. Proper Inheritance
Using Interface Inheritance Effectively Point
x; y
Shape origin; numVertices; operator[](int index)
Polygon
origin
Square
Rectangle
origin; width
origin; width; length
MyRectangle
331
4. Proper Inheritance
Using Interface Inheritance Effectively Point
x; y
Shape origin; numVertices; operator[](int index)
Polygon
origin
Square
Rectangle
origin; width
origin; width; length
MyRectangle
332
4. Proper Inheritance
Using Interface Inheritance Effectively Point
x; y
Shape origin; numVertices; operator[](int index)
Polygon
origin
Square
Rectangle
origin; width
origin; width; length
MyRectangle
333
4. Proper Inheritance
Using Interface Inheritance Effectively TheirPolygon()
YourSquare()
MyRectangle()
(Concrete)
(Concrete)
(Concrete)
TheirPolygon
YourSquare
MyRectangle
void setSide(int side)
(Modifiable)
Square ConstSquare void setWidth(int width) void setLength(int length)
int side() const (Modifiable)
Rectangle void appendVertex(const Point& v) void removeVertex(int i) (Modifiable)
ConstRectangle int width() const int length() const
Polygon ConstPolygon void setOrigin(const Point& v);
Point vertex(int i) const int numVertices() const
(Modifiable)
Shape ConstShape origin() const; 334
4. Proper Inheritance
Using Interface Inheritance Effectively TheirPolygon()
YourSquare()
MyRectangle()
(Concrete)
(Concrete)
(Concrete)
TheirPolygon
YourSquare
MyRectangle
void setSide(int side)
(Modifiable)
Square ConstSquare void setWidth(int width) void setLength(int length)
int side() const (Modifiable)
Rectangle void appendVertex(const Point& v) void removeVertex(int i) (Modifiable)
ConstRectangle int width() const int length() const
Polygon ConstPolygon void setOrigin(const Point& v);
Point vertex(int i) const int numVertices() const
(Modifiable)
Shape ConstShape origin() const; 335
4. Proper Inheritance
Using Interface Inheritance Effectively TheirPolygon()
YourSquare()
MyRectangle()
(Concrete)
(Concrete)
(Concrete)
TheirPolygon
YourSquare
MyRectangle
void setSide(int side)
(Modifiable)
Square ConstSquare void setWidth(int width) void setLength(int length)
int side() const (Modifiable)
Rectangle void appendVertex(const Point& v) void removeVertex(int i) (Modifiable)
ConstRectangle int width() const int length() const
Polygon ConstPolygon void setOrigin(const Point& v);
Point vertex(int i) const int numVertices() const
(Modifiable)
Shape ConstShape origin() const; 336
4. Proper Inheritance
Using Interface Inheritance Effectively TheirPolygon()
YourSquare()
MyRectangle()
(Concrete)
(Concrete)
(Concrete)
TheirPolygon
YourSquare
MyRectangle
void setSide(int side)
(Modifiable)
Square ConstSquare void setWidth(int width) void setLength(int length)
int side() const (Modifiable)
Rectangle void appendVertex(const Point& v) void removeVertex(int i) (Modifiable)
ConstRectangle int width() const int length() const
Polygon ConstPolygon void setOrigin(const Point& v);
Point vertex(int i) const int numVertices() const
(Modifiable)
Shape ConstShape origin() const; 337
4. Proper Inheritance
Using Interface Inheritance Effectively TheirPolygon()
YourSquare()
MyRectangle()
(Concrete)
(Concrete)
(Concrete)
TheirPolygon
YourSquare
MyRectangle
void setSide(int side)
(Modifiable)
Square ConstSquare void setWidth(int width) void setLength(int length)
int side() const (Modifiable)
Rectangle void appendVertex(const Point& v) void removeVertex(int i) (Modifiable)
ConstRectangle int width() const int length() const
Polygon ConstPolygon void setOrigin(const Point& v);
Point vertex(int i) const int numVertices() const
(Modifiable)
Shape ConstShape origin() const; 338
4. Proper Inheritance
Using Interface Inheritance Effectively TheirPolygon()
YourSquare()
MyRectangle()
(Concrete)
(Concrete)
(Concrete)
TheirPolygon
YourSquare
MyRectangle
void setSide(int side)
(Modifiable)
Square ConstSquare void setWidth(int width) void setLength(int length)
int side() const (Modifiable)
Rectangle void appendVertex(const Point& v) void removeVertex(int i) (Modifiable)
ConstRectangle int width() const int length() const
Polygon ConstPolygon void setOrigin(const Point& v);
Point vertex(int i) const int numVertices() const
(Modifiable)
Shape ConstShape origin() const; 339
4. Proper Inheritance
Using Interface Inheritance Effectively TheirPolygon()
YourSquare()
MyRectangle()
(Concrete)
(Concrete)
(Concrete)
TheirPolygon
YourSquare
MyRectangle
void setSide(int side)
(Modifiable)
Square ConstSquare void setWidth(int width) void setLength(int length)
int side() const (Modifiable)
Rectangle void appendVertex(const Point& v) void removeVertex(int i) (Modifiable)
ConstRectangle int width() const int length() const
Polygon ConstPolygon void setOrigin(const Point& v);
Point vertex(int i) const int numVertices() const
(Modifiable)
Shape ConstShape origin() const; 340
4. Proper Inheritance
Using Interface Inheritance Effectively TheirPolygon()
YourSquare()
MyRectangle()
(Concrete)
(Concrete)
(Concrete)
TheirPolygon
YourSquare
MyRectangle
void setSide(int side)
(Modifiable)
Square ConstSquare void setWidth(int width) void setLength(int length)
int side() const (Modifiable)
Rectangle void appendVertex(const Point& v) void removeVertex(int i) (Modifiable)
ConstRectangle int width() const int length() const
Polygon ConstPolygon void setOrigin(const Point& v);
Point vertex(int i) const int numVertices() const
(Modifiable)
Shape ConstShape origin() const; 341
4. Proper Inheritance
Using Interface Inheritance Effectively TheirPolygon()
YourSquare()
MyRectangle()
(Concrete)
(Concrete)
(Concrete)
TheirPolygon
YourSquare
MyRectangle
void setSide(int side)
(Modifiable)
Square ConstSquare void setWidth(int width) void setLength(int length)
int side() const (Modifiable)
Rectangle void appendVertex(const Point& v) void removeVertex(int i) (Modifiable)
ConstRectangle int width() const int length() const
Polygon ConstPolygon void setOrigin(const Point& v);
Point vertex(int i) const int numVertices() const
(Modifiable)
Shape ConstShape origin() const; 342
4. Proper Inheritance
Using Interface Inheritance Effectively TheirPolygon()
YourSquare()
MyRectangle()
(Concrete)
(Concrete)
(Concrete)
TheirPolygon
YourSquare
MyRectangle
void setSide(int side)
(Modifiable)
Square ConstSquare void setWidth(int width) void setLength(int length)
int side() const (Modifiable)
Rectangle void appendVertex(const Point& v) void removeVertex(int i) (Modifiable)
ConstRectangle int width() const int length() const
Polygon ConstPolygon void setOrigin(const Point& v);
Point vertex(int i) const int numVertices() const
(Modifiable)
Shape ConstShape origin() const; 343
4. Proper Inheritance
Using Interface Inheritance Effectively TheirPolygon()
YourSquare()
MyRectangle()
(Concrete)
(Concrete)
(Concrete)
TheirPolygon
YourSquare
MyRectangle
void setSide(int side)
(Modifiable)
Square ConstSquare void setWidth(int width) void setLength(int length)
int side() const (Modifiable)
Rectangle void appendVertex(const Point& v) void removeVertex(int i) (Modifiable)
ConstRectangle int width() const int length() const
Polygon ConstPolygon void setOrigin(const Point& v);
Point vertex(int i) const int numVertices() const
(Modifiable)
Shape ConstShape origin() const; 344
4. Proper Inheritance
Using Interface Inheritance Effectively TheirPolygon()
YourSquare()
MyRectangle()
(Concrete)
(Concrete)
(Concrete)
TheirPolygon
YourSquare
MyRectangle
void setSide(int side)
(Modifiable)
Square ConstSquare void setWidth(int width) void setLength(int length)
int side() const (Modifiable)
Rectangle void appendVertex(const Point& v) void removeVertex(int i) (Modifiable)
ConstRectangle int width() const int length() const
Polygon ConstPolygon void setOrigin(const Point& v);
Point vertex(int i) const int numVertices() const
(Modifiable)
Shape ConstShape origin() const; 345
4. Proper Inheritance
Using Interface Inheritance Effectively TheirPolygon()
YourSquare()
MyRectangle()
(Concrete)
(Concrete)
(Concrete)
TheirPolygon
YourSquare
MyRectangle
void setSide(int side)
(Modifiable)
Square ConstSquare void setWidth(int width) void setLength(int length)
int side() const (Modifiable)
Rectangle void appendVertex(const Point& v) void removeVertex(int i) (Modifiable)
ConstRectangle int width() const int length() const
Polygon ConstPolygon void setOrigin(const Point& v);
Point vertex(int i) const int numVertices() const
(Modifiable)
Shape ConstShape origin() const; 346
4. Proper Inheritance
Using Interface Inheritance Effectively TheirPolygon()
YourSquare()
MyRectangle()
(Concrete)
(Concrete)
(Concrete)
TheirPolygon
YourSquare
MyRectangle
void setSide(int side)
(Modifiable)
Square ConstSquare void setWidth(int width) void setLength(int length)
int side() const (Modifiable)
Rectangle void appendVertex(const Point& v) void removeVertex(int i) (Modifiable)
ConstRectangle int width() const int length() const
Polygon ConstPolygon void setOrigin(const Point& v);
Point vertex(int i) const int numVertices() const
(Modifiable)
Shape ConstShape origin() const; 347
4. Proper Inheritance
Using Interface Inheritance Effectively
The principal clients of Interface Inheritance are both the PUBLIC CLIENT and the DERIVED-CLASS AUTHOR. 348
4. Proper Inheritance
Using Interface Inheritance Effectively
Channel int write(const char *b, int n); int read(char *b, int n); 349
4. Proper Inheritance
Using Interface Inheritance Effectively
MyChannel Channel int write(const char *b, int n); int read(char *b, int n); 350
4. Proper Inheritance
Using Interface Inheritance Effectively
MyChannel
Client1
Channel int write(const char *b, int n); int read(char *b, int n); 351
4. Proper Inheritance
Using Interface Inheritance Effectively
TimedChannel int write(const char *b, int n, int t); int read(char *b, int n, int t);
MyChannel
Client1
Channel int write(const char *b, int n); int read(char *b, int n); 352
4. Proper Inheritance
Using Interface Inheritance Effectively YourTimedChannel
TimedChannel int write(const char *b, int n, int t); int read(char *b, int n, int t);
MyChannel
Client1
Channel int write(const char *b, int n); int read(char *b, int n); 353
4. Proper Inheritance
Using Interface Inheritance Effectively YourTimedChannel
Client2
TimedChannel int write(const char *b, int n, int t); int read(char *b, int n, int t);
MyChannel
Client1
Channel int write(const char *b, int n); int read(char *b, int n); 354
4. Proper Inheritance
Using Interface Inheritance Effectively YourTimedChannel
Client2
TimedChannel int write(const char *b, int n, int t); int read(char *b, int n, int t);
MyChannel
Client1
Channel int write(const char *b, int n); int read(char *b, int n);
3rd Party Product A
355
4. Proper Inheritance
Using Interface Inheritance Effectively YourTimedChannel
Client2
TimedChannel int write(const char *b, int n, int t); int read(char *b, int n, int t);
PaChannelAdapter
MyChannel
Client1
Channel int write(const char *b, int n); int read(char *b, int n);
3rd Party Product A
356
4. Proper Inheritance
Using Interface Inheritance Effectively YourTimedChannel
Client2
TimedChannel int write(const char *b, int n, int t); int read(char *b, int n, int t);
PaChannelAdapter Client3 MyChannel
Client1
Channel int write(const char *b, int n); int read(char *b, int n);
3rd Party Product A
357
4. Proper Inheritance
Using Interface Inheritance Effectively YourTimedChannel
Client2
TimedChannel int write(const char *b, int n, int t); int read(char *b, int n, int t);
PaChannelAdapter Client3 MyChannel
Client1
Channel int write(const char *b, int n); int read(char *b, int n);
3rd Party Product A
3rd Party Product B 358
4. Proper Inheritance
Using Interface Inheritance Effectively YourTimedChannel
PbTimedChannelAdapter
Client2
TimedChannel int write(const char *b, int n, int t); int read(char *b, int n, int t);
PaChannelAdapter Client3 MyChannel
Client1
Channel int write(const char *b, int n); int read(char *b, int n);
3rd Party Product A
3rd Party Product B 359
4. Proper Inheritance
Using Interface Inheritance Effectively YourTimedChannel
Client4
PbTimedChannelAdapter
Client2
TimedChannel int write(const char *b, int n, int t); int read(char *b, int n, int t);
PaChannelAdapter Client3 MyChannel
Client1
Channel int write(const char *b, int n); int read(char *b, int n);
3rd Party Product A
3rd Party Product B 360
4. Proper Inheritance
Using Interface Inheritance Effectively YourTimedChannel
Client4
PbTimedChannelAdapter
Client2
TimedChannel Client5
int write(const char *b, int n, int t); int read(char *b, int n, int t);
PaChannelAdapter Client3 MyChannel
Client1
Channel int write(const char *b, int n); int read(char *b, int n);
3rd Party Product A
3rd Party Product B 361
4. Proper Inheritance
Using Structural Inheritance Effectively (Non-Modifiable)
ConstElemRef
(Modifiable)
ElemRef
362
4. Proper Inheritance
Using Structural Inheritance Effectively (Non-Modifiable)
ConstElemRef elem() ;
template class ConstElemRef { const TYPE *d_elem_p; friend class ElemRef; //
Note friendship
private: // Not Implemented. TBD ConstElemRef& operator=(Const ConstElemRef&); public: // CREATORS ConstElemRef(const TYPE *elem); ConstElemRef(const ConstElemRef& ref); ~ConstElemRef(); // ACCESSORS const TYPE& elem() const; }; 363
4. Proper Inheritance
Using Structural Inheritance Effectively (Non-Modifiable)
ConstElemRef elem() ;
template class ConstElemRef { const TYPE *d_elem_p; friend class ElemRef; //
Single Pointer Data Member Note friendship
private: // Not Implemented. TBD ConstElemRef& operator=(Const ConstElemRef&); public: // CREATORS ConstElemRef(const TYPE *elem); ConstElemRef(const ConstElemRef& ref); ~ConstElemRef(); // ACCESSORS const TYPE& elem() const; }; 364
4. Proper Inheritance
Using Structural Inheritance Effectively (Non-Modifiable)
ConstElemRef elem() ;
template class ConstElemRef { const TYPE *d_elem_p; friend class ElemRef; //
Derived Class Declared Friend Note friendship
private: // Not Implemented. TBD ConstElemRef& operator=(Const ConstElemRef&); public: // CREATORS ConstElemRef(const TYPE *elem); ConstElemRef(const ConstElemRef& ref); ~ConstElemRef(); // ACCESSORS const TYPE& elem() const; }; 365
4. Proper Inheritance
Using Structural Inheritance Effectively (Non-Modifiable)
ConstElemRef elem() ;
template class ConstElemRef { const TYPE *d_elem_p; friend class ElemRef; //
Copy Assignment Not Implemented Note friendship
private: // Not Implemented. TBD ConstElemRef& operator=(Const ConstElemRef&); public: // CREATORS ConstElemRef(const TYPE *elem); ConstElemRef(const ConstElemRef& ref); ~ConstElemRef(); // ACCESSORS const TYPE& elem() const; }; 366
4. Proper Inheritance
Using Structural Inheritance Effectively (Non-Modifiable)
ConstElemRef elem() ;
template class ConstElemRef { const TYPE *d_elem_p; friend class ElemRef; //
Note friendship
private: // Not Implemented. TBD ConstElemRef& operator=(Const ConstElemRef&); public: // CREATORS ConstElemRef(const TYPE *elem); ConstElemRef(const ConstElemRef& ref); ~ConstElemRef();
Read-Only Access
// ACCESSORS const TYPE& elem() const; }; 367
4. Proper Inheritance
Using Structural Inheritance Effectively ConstElemRef elem() ;
class ConstElemRef { const TYPE *d_elem_p; // … const TYPE& elem() const; };
(Modifiable)
ElemRef elem() ;
template class ElemRef : public ConstElemRef { public: // CREATORS ElemRef(TYPE *elem); ElemRef(const ElemRef& ref); ~ElemRef(); // MANIPULATORS ElemRef& operator=(const ElemRef&); // Fine. TBD // ACCESSORS TYPE& elem() const; };
368
4. Proper Inheritance
Using Structural Inheritance Effectively ConstElemRef elem() ;
class ConstElemRef { const TYPE *d_elem_p; // … const TYPE& elem() const; };
(Modifiable)
ElemRef elem() ;
Public Structural Inheritance
template class ElemRef : public ConstElemRef { public: // CREATORS ElemRef(TYPE *elem); ElemRef(const ElemRef& ref); ~ElemRef(); // MANIPULATORS ElemRef& operator=(const ElemRef&); // Fine. TBD // ACCESSORS TYPE& elem() const; };
369
4. Proper Inheritance
Using Structural Inheritance Effectively ConstElemRef elem() ;
class ConstElemRef { const TYPE *d_elem_p; // … const TYPE& elem() const; };
(Modifiable)
ElemRef elem() ;
No Additional Member Data
template class ElemRef : public ConstElemRef { public: // CREATORS ElemRef(TYPE *elem); ElemRef(const ElemRef& ref); ~ElemRef(); // MANIPULATORS ElemRef& operator=(const ElemRef&); // Fine. TBD // ACCESSORS TYPE& elem() const; };
370
4. Proper Inheritance
Using Structural Inheritance Effectively ConstElemRef elem() ;
class ConstElemRef { const TYPE *d_elem_p; // … const TYPE& elem() const; };
(Modifiable)
ElemRef elem() ;
Copy Assignment Implemented
template class ElemRef : public ConstElemRef { public: // CREATORS ElemRef(TYPE *elem); ElemRef(const ElemRef& ref); ~ElemRef(); // MANIPULATORS ElemRef& operator=(const ElemRef&); // Fine. TBD // ACCESSORS TYPE& elem() const; };
371
4. Proper Inheritance
Using Structural Inheritance Effectively ConstElemRef elem() ;
class ConstElemRef { const TYPE *d_elem_p; // … const TYPE& elem() const; };
(Modifiable)
ElemRef elem() ;
template class ElemRef : public ConstElemRef { public: // CREATORS ElemRef(TYPE *elem); ElemRef(const ElemRef& ref); ~ElemRef(); // MANIPULATORS ElemRef& operator=(const ElemRef&); // Fine. TBD // ACCESSORS TYPE& elem() const;
Read-Write Access };
372
4. Proper Inheritance
Using Structural Inheritance Effectively ConstElemRef elem() ;
class ConstElemRef { const TYPE *d_elem_p; // … const TYPE& elem() const; };
(Modifiable)
ElemRef elem() ;
An ElemRef template Is-A class ElemRef : public ConstElemRef { ConstElemRef public: // CREATORS with ElemRef(TYPE *elem); “Write ElemRef(const ElemRef& ref); Access” ~ElemRef(); // MANIPULATORS ElemRef& operator=(const ElemRef&); // Fine. TBD // ACCESSORS TYPE& elem() const;
Read-Write Access };
373
4. Proper Inheritance
Using Structural Inheritance Effectively ConstElemRef elem() ;
class ConstElemRef { const TYPE *d_elem_p; // … const TYPE& elem() const; };
(Modifiable)
ElemRef elem() ;
An std::iterator template Is-A class ElemRef : public ConstElemRef { std::const_iterator public: // CREATORS with ElemRef(TYPE *elem); “Write Access” ElemRef(const ElemRef& ref); ~ElemRef(); // MANIPULATORS ElemRef& operator=(const ElemRef&); // Fine. TBD // ACCESSORS TYPE& elem() const;
Read-Write Access };
374
4. Proper Inheritance
Using Structural Inheritance Effectively (Non-Modifiable)
ConstElemRef
(Modifiable)
ElemRef
const TYPE& ConstElemRef::elem() const { return *d_elem_p; }
TYPE& ElemRef::elem() const { return *const_cast(d_elem_p); } // Note use of friendship as well.
Note: same component due to friendship. 375
4. Proper Inheritance
Using Structural Inheritance Effectively Note we are casting-away const TYPE& ConstElemRef::elem() const const (Non-Modifiable)
{ return *d_elem_p;
ConstElemRef
}
(Modifiable)
ElemRef
TYPE& ElemRef::elem() const { return *const_cast(d_elem_p); } // Note use of friendship as well.
Note: same component due to friendship. 376
4. Proper Inheritance
Using Structural Inheritance Effectively Be especially careful to ensure const-correctness when const-casting is involved.
(Non-Modifiable)
ConstElemRef
(Modifiable)
ElemRef
377
4. Proper Inheritance
Using Structural Inheritance Effectively Be especially careful to ensure const-correctness when const-casting is involved.
void g(ConstElemRef *cer1, const ConstElemRef& cer2) { *cer1 = cer2; // Enable const-correctness violation due to slicing. } // Assumes copy assignment is enabled on the ConstElemRef base class.
(Non-Modifiable)
ConstElemRef
(Modifiable)
ElemRef
378
4. Proper Inheritance
Using Structural Inheritance Effectively Be especially careful to ensure const-correctness when const-casting is involved.
void g(ConstElemRef *cer1, const ConstElemRef& cer2) { *cer1 = cer2; // Enable const-correctness violation due to slicing. } // Assumes copy assignment is enabled on the ConstElemRef base class.
(Non-Modifiable)
ConstElemRef
template < class TYPE> void f(const TYPE& constElem) { TYPE dummy; ElemRef er(&dummy); ConstElemRef cer(&constElem);
(Modifiable)
ElemRef
g(&er, cer); // Rebind (modifiable) 'ElemRef' 'er'. er.elem() = TYPE(); // Clobber 'constElem'.
379
4. Proper Inheritance
Using Structural Inheritance Effectively Be especially careful to ensure const-correctness when const-casting is involved.
void g(ConstElemRef *cer1, const ConstElemRef& cer2) { *cer1 = cer2; // Enable const-correctness violation due to slicing. } // Assumes copy assignment is enabled on the ConstElemRef base class.
(Non-Modifiable)
ConstElemRef
template < class TYPE> void f(const TYPE& constElem) { TYPE dummy; ElemRef er(&dummy); ConstElemRef cer(&constElem);
(Modifiable)
ElemRef
g(&er, cer); // Rebind (modifiable) 'ElemRef' 'er'. er.elem() = TYPE(); // Clobber 'constElem'.
380
4. Proper Inheritance
Using Structural Inheritance Effectively Be especially careful to ensure const-correctness when const-casting is involved.
void g(ConstElemRef *cer1, const ConstElemRef& cer2) { *cer1 = cer2; // Enable const-correctness violation due to slicing. } // Assumes copy assignment is enabled on the ConstElemRef base class.
(Non-Modifiable)
ConstElemRef
template < class TYPE> void f(const TYPE& constElem) { TYPE dummy; ElemRef er(&dummy); ConstElemRef cer(&constElem);
(Modifiable)
ElemRef
g(&er, cer); // Rebind (modifiable) 'ElemRef' 'er'. er.elem() = TYPE(); // Clobber 'constElem'.
381
4. Proper Inheritance
Using Structural Inheritance Effectively Be especially careful to ensure const-correctness when const-casting is involved.
void g(ConstElemRef *cer1, const ConstElemRef& cer2) { *cer1 = cer2; // Enable const-correctness violation due to slicing. } // Assumes copy assignment is enabled on the ConstElemRef base class.
(Non-Modifiable)
ConstElemRef
template < class TYPE> void f(const TYPE& constElem) { TYPE dummy; ElemRef er(&dummy); ConstElemRef cer(&constElem);
(Modifiable)
ElemRef
g(&er, cer); // Rebind (modifiable) 'ElemRef' 'er'. er.elem() = TYPE(); // Clobber 'constElem'.
382
4. Proper Inheritance
Using Structural Inheritance Effectively Be especially careful to ensure const-correctness when const-casting is involved.
void g(ConstElemRef *cer1, const ConstElemRef& cer2) { *cer1 = cer2; // Enable const-correctness violation due to slicing. } // Assumes copy assignment is enabled on the ConstElemRef base class.
(Non-Modifiable)
ConstElemRef
template < class TYPE> void f(const TYPE& constElem) { TYPE dummy; ElemRef er(&dummy); ConstElemRef cer(&constElem);
(Modifiable)
ElemRef
g(&er, cer); // Rebind (modifiable) 'ElemRef' 'er'. er.elem() = TYPE(); // Clobber 'constElem'.
383
4. Proper Inheritance
Using Structural Inheritance Effectively Be especially careful to ensure const-correctness when const-casting is involved.
void g(ConstElemRef *cer1, const ConstElemRef& cer2) { *cer1 = cer2; // Enable const-correctness violation due to slicing. } // Assumes copy assignment is enabled on the ConstElemRef base class.
(Non-Modifiable)
ConstElemRef
template < class TYPE> void f(const TYPE& constElem) { TYPE dummy; ElemRef er(&dummy); ConstElemRef cer(&constElem);
(Modifiable)
ElemRef
g(&er, cer); // Rebind (modifiable) 'ElemRef' 'er'. er.elem() = TYPE(); // Clobber 'constElem'.
384
4. Proper Inheritance
Using Structural Inheritance Effectively
The principal client of Structural Inheritance is the PUBLIC CLIENT.
385
4. Proper Inheritance
Using Structural Inheritance Effectively ConstElemRefClient
(Non-Modifiable)
ConstElemRef ElemRefClient
(Modifiable)
ElemRef
Note: No Runtime-Performance Overhead (e.g., due to Dynamic Binding). 386
4. Proper Inheritance
Using Implementation Inheritance Effectively CompositeWidget
WidgetClient
Widget instanceName origin move draw numChildren addChild removeChild
387
4. Proper Inheritance
Using Implementation Inheritance Effectively CompositeWidgetClient CompositeWidget numChildren addChild removeChild
WidgetClient Widget instanceName origin move draw
388
4. Proper Inheritance
Using Implementation Inheritance Effectively CompositeWidgetClient CompositeWidget numChildren addChild removeChild
WidgetClient
MyWidget Widget instanceName origin move draw
389
4. Proper Inheritance
Using Implementation Inheritance Effectively CompositeWidgetClient CompositeWidget numChildren addChild removeChild
WidgetClient
MyWidget WidgetImp
Widget
instanceName origin move draw
390
4. Proper Inheritance
Using Implementation Inheritance Effectively CompositeWidgetClient CompositeWidget numChildren addChild removeChild
WidgetClient
MyWidget WidgetImp
YourWidget Widget
instanceName origin move draw
391
4. Proper Inheritance
Using Implementation Inheritance Effectively CompositeWidgetClient
MyCompositeWidget CompositeWidget numChildren addChild removeChild
WidgetClient
MyWidget WidgetImp
YourWidget Widget
instanceName origin move draw
392
4. Proper Inheritance
Using Implementation Inheritance Effectively CompositeWidgetClient
MyCompositeWidget CompositeWidgetImp CompositeWidget numChildren addChild removeChild
MyWidget
WidgetClient
WidgetImp
YourWidget Widget
instanceName origin move draw
393
4. Proper Inheritance
Using Implementation Inheritance Effectively CompositeWidgetClient
MyCompositeWidget CompositeWidgetImp YourCompositeWidget CompositeWidget numChildren addChild removeChild
MyWidget
WidgetClient
WidgetImp
YourWidget Widget
instanceName origin move draw
394
4. Proper Inheritance
Using Implementation Inheritance Effectively CompositeWidgetClient
MyCompositeWidget CompositeWidgetImp YourCompositeWidget CompositeWidget numChildren addChild removeChild
MyWidget
WidgetClient
WidgetImp
YourWidget Widget
instanceName origin move draw
395
4. Proper Inheritance
Using Implementation Inheritance Effectively CompositeWidgetClient
MyCompositeWidget CompositeWidgetImp YourCompositeWidget CompositeWidget numChildren addChild removeChild
MyWidget
WidgetClient
WidgetImp
YourWidget Widget
instanceName origin move draw
396
4. Proper Inheritance
Using Implementation Inheritance Effectively CompositeWidgetClient
MyCompositeWidget CompositeWidgetImp YourCompositeWidget CompositeWidget numChildren addChild removeChild
MyWidget
WidgetClient
WidgetImp
YourWidget Widget
instanceName origin move draw
397
4. Proper Inheritance
Using Implementation Inheritance Effectively CompositeWidgetClient
MyCompositeWidget CompositeWidgetImp YourCompositeWidget CompositeWidget numChildren addChild removeChild
MyWidget
WidgetClient
WidgetImp
YourWidget Widget
instanceName origin move draw
398
4. Proper Inheritance
Using Implementation Inheritance Effectively CompositeWidgetClient
MyCompositeWidget CompositeWidgetImp YourCompositeWidget CompositeWidget numChildren addChild removeChild
MyWidget
WidgetClient
WidgetImp
YourWidget Widget
instanceName origin move draw
399
4. Proper Inheritance
Using Implementation Inheritance Effectively CompositeWidgetClient
MyCompositeWidget CompositeWidgetImp YourCompositeWidget CompositeWidget numChildren addChild removeChild
MyWidget
WidgetClient
WidgetImp
YourWidget Widget
instanceName origin move draw
400
4. Proper Inheritance
Using Implementation Inheritance Effectively CompositeWidgetClient
MyCompositeWidget CompositeWidgetImp YourCompositeWidget CompositeWidget numChildren addChild removeChild
MyWidget
WidgetClient
WidgetImp
Widen interface first;
YourWidget Widget
instanceName origin move draw
401
4. Proper Inheritance
Using Implementation Inheritance Effectively CompositeWidgetClient
MyCompositeWidget CompositeWidgetImp YourCompositeWidget CompositeWidget numChildren addChild removeChild
MyWidget
WidgetClient
WidgetImp
YourWidget Widget
instanceName origin move draw
Widen interface first; then provide implementation without widening. 402
4. Proper Inheritance
Using Implementation Inheritance Effectively
The principal client of Implementation Inheritance is the DERIVED-CLASS AUTHOR.
403
4. Proper Inheritance
Using Implementation Inheritance Effectively
CompositeWidget numChildren addChild removeChild
Widget instanceName origin move draw
404
4. Proper Inheritance
Using Implementation Inheritance Effectively
CompositeWidget numChildren addChild removeChild
Widget instanceName origin move draw
Part of Framework using CompositeWidget
Part of Framework using Widget Framework
405
4. Proper Inheritance
Using Implementation Inheritance Effectively
CompositeWidget numChildren addChild removeChild
Widget instanceName origin move draw
Part of Framework using CompositeWidget
Part of Framework using Widget Framework
406
4. Proper Inheritance
Using Implementation Inheritance Effectively
WidgetPartialImp
CompositeWidget numChildren addChild removeChild
Widget instanceName origin move draw
Part of Framework using CompositeWidget
Part of Framework using Widget Framework
407
4. Proper Inheritance
Using Implementation Inheritance Effectively
CompositeWidgetPartialImp WidgetPartialImp
CompositeWidget numChildren addChild removeChild
Widget instanceName origin move draw
Part of Framework using CompositeWidget
Part of Framework using Widget Framework
408
4. Proper Inheritance
Using Implementation Inheritance Effectively
MyWidget CompositeWidgetPartialImp WidgetPartialImp
CompositeWidget numChildren addChild removeChild
Widget instanceName origin move draw
Part of Framework using CompositeWidget
Part of Framework using Widget Framework
409
4. Proper Inheritance
Using Implementation Inheritance Effectively MyCompositeWidget MyWidget CompositeWidgetPartialImp WidgetPartialImp
CompositeWidget numChildren addChild removeChild
Widget instanceName origin move draw
Part of Framework using CompositeWidget
Part of Framework using Widget Framework
410
4. Proper Inheritance
Using Implementation Inheritance Effectively YourWidget
MyCompositeWidget
MyWidget CompositeWidgetPartialImp WidgetPartialImp
CompositeWidget numChildren addChild removeChild
Widget instanceName origin move draw
Part of Framework using CompositeWidget
Part of Framework using Widget Framework
411
4. Proper Inheritance
Using Implementation Inheritance Effectively YourCompositeWidget YourWidget
MyCompositeWidget
MyWidget CompositeWidgetPartialImp WidgetPartialImp
CompositeWidget numChildren addChild removeChild
Widget instanceName origin move draw
Part of Framework using CompositeWidget
Part of Framework using Widget Framework
412
4. Proper Inheritance
Using Implementation Inheritance Effectively YourCompositeWidget YourWidget MyWidget
MyCompositeWidget
TheirCompositeWidget CompositeWidgetPartialImp
WidgetPartialImp
CompositeWidget numChildren addChild removeChild
Widget instanceName origin move draw
Part of Framework using CompositeWidget
Part of Framework using Widget Framework
413
4. Proper Inheritance
Using Implementation Inheritance Effectively YourCompositeWidget YourWidget MyWidget
MyCompositeWidget
TheirCompositeWidget CompositeWidgetPartialImp
WidgetPartialImp
TheirWidget
CompositeWidget numChildren addChild removeChild
Widget instanceName origin move draw
Part of Framework using CompositeWidget
Part of Framework using Widget Framework
414
4. Proper Inheritance
Combining Kinds of Inheritance
415
4. Proper Inheritance
Combining Kinds of Inheritance • Structural & Interface
416
4. Proper Inheritance
Combining Kinds of Inheritance • Structural & Interface –Typically for Efficiency and Syntactic Sugar.
417
4. Proper Inheritance
Combining Kinds of Inheritance • Structural & Interface –Typically for Efficiency and Syntactic Sugar.
• Interface & Implementation
418
4. Proper Inheritance
Combining Kinds of Inheritance • Structural & Interface –Typically for Efficiency and Syntactic Sugar.
• Interface & Implementation –Interface inheritance (widening) first; then implementation inheritance (no widening) .
419
4. Proper Inheritance
Combining Kinds of Inheritance • Structural & Interface –Typically for Efficiency and Syntactic Sugar.
• Interface & Implementation –Interface inheritance (widening) first; then implementation inheritance (no widening) .
• Implementation & Structural
420
4. Proper Inheritance
Combining Kinds of Inheritance • Structural & Interface –Typically for Efficiency and Syntactic Sugar.
• Interface & Implementation –Interface inheritance (widening) first; then implementation inheritance (no widening) .
• Implementation & Structural –Bad Idea: Unnecessarily addresses the needs of derived class authors and public clients in the same physical component. 421
4. Proper Inheritance
Combining Kinds of Inheritance virtual void g();
virtual void g() { } virtual void h() { }
Public Client
inline int f(); virtual void g() = 0; virtual void h() = 0;
422
4. Proper Inheritance
Combining Kinds of Inheritance virtual void g();
Public Client
inline int f(); virtual void g(); virtual void h();
423
4. Proper Inheritance
Combining Kinds of Inheritance virtual void g();
Public Client
inline int f(); virtual void g(); virtual void h();
424
4. Proper Inheritance
Combining Kinds of Inheritance virtual void g();
Public Client
inline int f(); virtual void g(); virtual void h();
425
4. Proper Inheritance
Relative Utility Interface Inheritance
Structural Inheritance Implementation Inheritance 426
4. Proper Inheritance
Physical Substitutability
427
4. Proper Inheritance
Physical Substitutability
428
4. Proper Inheritance
Physical Substitutability
429
4. Proper Inheritance
Physical Substitutability What Criteria Must Be Satisfied?
430
4. Proper Inheritance
Physical Substitutability The new component’s logical behavior:
431
4. Proper Inheritance
Physical Substitutability The new component’s logical behavior: • Preconditions needed for defined behavior can be made weaker, but no stronger.
432
4. Proper Inheritance
Physical Substitutability The new component’s logical behavior: • Preconditions needed for defined behavior can be made weaker, but no stronger. • Pre-existing essential behavior of the component must remain unchanged.
433
4. Proper Inheritance
Physical Substitutability The new component’s logical behavior: • Preconditions needed for defined behavior can be made weaker, but no stronger. • Pre-existing essential behavior of the component must remain unchanged. • New behaviors may be defined, and essential ones extended, so long as the component is backward compatible with pre-existing clients. 434
4. Proper Inheritance
Physical Substitutability The new component’s physical characteristics:
435
4. Proper Inheritance
Physical Substitutability The new component’s physical characteristics: • Physical dependencies cannot increase (much).
436
4. Proper Inheritance
Physical Substitutability The new component’s physical characteristics: • Physical dependencies cannot increase (much). • Compile-time cannot increase substantially.
437
4. Proper Inheritance
Physical Substitutability The new component’s physical characteristics: • Physical dependencies cannot increase (much). • Compile-time cannot increase substantially. • Size (footprint) cannot increase (much).
438
4. Proper Inheritance
Physical Substitutability The new component’s physical characteristics: • Physical dependencies cannot increase (much). • Compile-time cannot increase substantially. • Size (footprint) cannot increase (much). • Dynamic memory usage can’t increase (much).
439
4. Proper Inheritance
Physical Substitutability The new component’s physical characteristics: • Physical dependencies cannot increase (much). • Compile-time cannot increase substantially. • Size (footprint) cannot increase (much). • Dynamic memory usage can’t increase (much). • Can’t introduce dynamic memory allocation.
440
4. Proper Inheritance
Physical Substitutability The new component’s physical characteristics: • Physical dependencies cannot increase (much). • Compile-time cannot increase substantially. • Size (footprint) cannot increase (much). • Dynamic memory usage can’t increase (much). • Can’t introduce dynamic memory allocation. • Runtime must not be increased significantly for important (relevant) use-cases. 441
4. Proper Inheritance
End of Section
Questions? 442
4. Proper Inheritance
What Questions are we Answering? • What distinguishes Interface, Structural, and Implementation inheritance? • What do we mean by the Is-A relationship, & how does proper inheritance vary from one form to the next. – What does LSP (Liskov Substitution Principle) have to do with it?
• How are each of the three inheritances used effectively? – – – –
Who is the principal client of each kind of inheritance? How are interface and implementation inheritance ordered? Does it make sense to combine two (or all three) inheritances? What is the relative utility of the three forms of inheritance?
• How are structural inheritance, (logical) substitutability, & backward compatibility of (physical) components related? 443
Outline 1. Components (review) Modularity, Logical/Physical Dependencies, & Level numbers
2. Interfaces and Contracts (review) Syntax versus Semantics & Essential Behavior
3. Narrow versus Wide Contracts (review) The Significance of Undefined Behavior
4. Proper Inheritance Is-A for Interface, Structural, & Implementation Inheritance
444
Conclusion 1. Components (review) Modularity, Logical/Physical Dependencies, & Level numbers
445
Conclusion 1. Components (review) Modularity, Logical/Physical Dependencies, & Level numbers
• A Component – a .h/.cpp pair satisfying four essential properties – is our fundamental unit of both logical and physical software design.
446
Conclusion 1. Components (review) Modularity, Logical/Physical Dependencies, & Level numbers
• A Component – a .h/.cpp pair satisfying four essential properties – is our fundamental unit of both logical and physical software design. • Logical relationships, such as Is-A and Uses between classes, imply physical dependencies among the components that defined them. 447
Conclusion 1. Components (review) Modularity, Logical/Physical Dependencies, & Level numbers
• A Component – a .h/.cpp pair satisfying four essential properties – is our fundamental unit of both logical and physical software design. • Logical relationships, such as Is-A and Uses between classes, imply physical dependencies among the components that defined them. • No cyclic dependencies/long-distance friendships! 448
Conclusion 2. Interfaces and Contracts (review) Syntax versus Semantics & Essential Behavior
449
Conclusion 2. Interfaces and Contracts (review) Syntax versus Semantics & Essential Behavior
• An interface is syntactic; a contract is semantic.
450
Conclusion 2. Interfaces and Contracts (review) Syntax versus Semantics & Essential Behavior
• An interface is syntactic; a contract is semantic. • A contract defines both pre- & postconditions.
451
Conclusion 2. Interfaces and Contracts (review) Syntax versus Semantics & Essential Behavior
• An interface is syntactic; a contract is semantic. • A contract defines both pre- & postconditions. • Undefined Behavior if a precondition isn’t met.
452
Conclusion 2. Interfaces and Contracts (review) Syntax versus Semantics & Essential Behavior
• • • •
An interface is syntactic; a contract is semantic. A contract defines both pre- & postconditions. Undefined Behavior if a precondition isn’t met. What undefined behavior does is undefined!
453
Conclusion 2. Interfaces and Contracts (review) Syntax versus Semantics & Essential Behavior
• • • • •
An interface is syntactic; a contract is semantic. A contract defines both pre- & postconditions. Undefined Behavior if a precondition isn’t met. What undefined behavior does is undefined! Documented essential behavior must not change!
454
Conclusion 2. Interfaces and Contracts (review) Syntax versus Semantics & Essential Behavior
• • • • • •
An interface is syntactic; a contract is semantic. A contract defines both pre- & postconditions. Undefined Behavior if a precondition isn’t met. What undefined behavior does is undefined! Documented essential behavior must not change! Test drivers must verify all essential behavior. 455
Conclusion 2. Interfaces and Contracts (review) Syntax versus Semantics & Essential Behavior
• • • • • • •
An interface is syntactic; a contract is semantic. A contract defines both pre- & postconditions. Undefined Behavior if a precondition isn’t met. What undefined behavior does is undefined! Documented essential behavior must not change! Test drivers must verify all essential behavior. Assertions in destructors help verify invariants. 456
Conclusion 3. Narrow versus Wide Contracts (review) The Significance of Undefined Behavior
457
Conclusion 3. Narrow versus Wide Contracts (review) The Significance of Undefined Behavior
• Narrow contracts admit undefined behavior.
458
Conclusion 3. Narrow versus Wide Contracts (review) The Significance of Undefined Behavior
• Narrow contracts admit undefined behavior. • Appropriately narrow contracts are GOOD:
459
Conclusion 3. Narrow versus Wide Contracts (review) The Significance of Undefined Behavior
• Narrow contracts admit undefined behavior. • Appropriately narrow contracts are GOOD: – Reduce costs associated with development/testing
460
Conclusion 3. Narrow versus Wide Contracts (review) The Significance of Undefined Behavior
• Narrow contracts admit undefined behavior. • Appropriately narrow contracts are GOOD: – Reduce costs associated with development/testing – Improve performance and reduces object-code size
461
Conclusion 3. Narrow versus Wide Contracts (review) The Significance of Undefined Behavior
• Narrow contracts admit undefined behavior. • Appropriately narrow contracts are GOOD: – Reduce costs associated with development/testing – Improve performance and reduces object-code size – Allow useful behavior to be added as needed
462
Conclusion 3. Narrow versus Wide Contracts (review) The Significance of Undefined Behavior
• Narrow contracts admit undefined behavior. • Appropriately narrow contracts are GOOD: – – – –
Reduce costs associated with development/testing Improve performance and reduces object-code size Allow useful behavior to be added as needed Enable practical/effective Defensive Programming 463
Conclusion 3. Narrow versus Wide Contracts (review) The Significance of Undefined Behavior
• Narrow contracts admit undefined behavior. • Appropriately narrow contracts are GOOD: – – – –
Reduce costs associated with development/testing Improve performance and reduces object-code size Allow useful behavior to be added as needed Enable practical/effective Defensive Programming
• Defensive programming means fault intolerance! 464
Conclusion 4. Proper Inheritance Is-A for Interface, Structural, & Implementation Inheritance
465
Conclusion 4. Proper Inheritance Is-A for Interface, Structural, & Implementation Inheritance
• The derived class must adhere to both contracts.
466
Conclusion 4. Proper Inheritance Is-A for Interface, Structural, & Implementation Inheritance
• The derived class must adhere to both contracts. • The static type of the pointer/reference should make no difference in programmatic behavior.
467
Conclusion 4. Proper Inheritance Is-A for Interface, Structural, & Implementation Inheritance
• The derived class must adhere to both contracts. • The static type of the pointer/reference should make no difference in programmatic behavior. • Interface inheritance is (virtually :-) all we need!
468
Conclusion 4. Proper Inheritance Is-A for Interface, Structural, & Implementation Inheritance
• The derived class must adhere to both contracts. • The static type of the pointer/reference should make no difference in programmatic behavior. • Interface inheritance is (virtually :-) all we need! • Backward compatibility for components is a whole lot like proper structural inheritance. 469
Conclusion
The End 470