toward a common intuition and reusable testing methodology€¦ · 1. the .cpp file includes its .h...
TRANSCRIPT
1
Proper Inheritance
John Lakos
Tuesday, May 10, 2016
2
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.
3
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.
4
What’s The Problem?
5
What’s The Problem? Large-Scale C++ Software Design is Multi-Dimensional:
6
What’s The Problem? Large-Scale C++ Software Design is Multi-Dimensional:
• It involves many subtle logical and physical aspects.
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.
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.
9
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.
10
Purpose of this Talk
11
Purpose of this Talk
Review:
12
Purpose of this Talk
Review: 1. Components – Our fundamental unit of logical and physical design
13
Purpose of this Talk
Review: 1. Components – Our fundamental unit of logical and physical design
2. Interfaces and contracts (in general)
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)
15
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.
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
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
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: Classes and Functions
Logical
physical
19
1. Components (review)
Logical versus Physical Design
What distinguishes Logical from Physical Design? Logical: Classes and Functions Physical: Files and Libraries
Logical
physical
20
1. Components (review)
Logical versus Physical Design
Logical content aggregated into a Physical hierarchy of components
a b
c
21
1. Components (review)
Component: Uniform Physical Structure
// component.t.cpp
#include <component.h>
// ...
int main(...)
{
//...
}
//-- END OF FILE --
component.t.cpp
// component.h
// ...
//-- END OF FILE --
// component.cpp
#include <component.h>
// ...
//-- END OF FILE --
component.h component.cpp
component
A Component Is Physical
22
1. Components (review)
Component: Uniform Physical Structure
// component.t.cpp
#include <component.h>
// ...
int main(...)
{
//...
}
//-- END OF FILE --
component.t.cpp
// component.h
// ...
//-- END OF FILE --
// component.cpp
#include <component.h>
// ...
//-- END OF FILE --
component.h component.cpp
component
Implementation
23
1. Components (review)
Component: Uniform Physical Structure
// component.t.cpp
#include <component.h>
// ...
int main(...)
{
//...
}
//-- END OF FILE --
component.t.cpp
// component.h
// ...
//-- END OF FILE --
// component.cpp
#include <component.h>
// ...
//-- END OF FILE --
component.h component.cpp
component
Header
24
1. Components (review)
Component: Uniform Physical Structure
// component.t.cpp
#include <component.h>
// ...
int main(...)
{
//...
}
//-- END OF FILE --
component.t.cpp
// component.h
// ...
//-- END OF FILE --
// component.cpp
#include <component.h>
// ...
//-- END OF FILE --
component.h component.cpp
component
Test Driver
25
1. Components (review)
Component: Uniform Physical Structure
// component.t.cpp
#include <component.h>
// ...
int main(...)
{
//...
}
//-- END OF FILE --
component.t.cpp
// component.h
// ...
//-- END OF FILE --
// component.cpp
#include <component.h>
// ...
//-- END OF FILE --
component.h component.cpp
component
The Fundamental Unit of Design
26
1. Components (review)
Component: Not Just a .h/.cpp Pair
my::Widget
my_widget
27
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
1. 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.
30
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.
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.
31
1. Components (review)
Component: Not Just a .h/.cpp Pair
We could easily spend 20 minutes on this slide alone!
1. 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.
32
1. Components (review)
Component: Not Just a .h/.cpp Pair Achieve
Logical/Physical Modularity
Avoid Global Namespace
Pollution
Enable Efficient Extraction of
Physical Dependencies
1. 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.
33
1. Components (review)
Component: Not Just a .h/.cpp Pair Achieve
Logical/Physical Modularity
Avoid Global Namespace
Pollution
Enable Efficient Extraction of
Physical Dependencies
For much more see
ADVANCED LEVELIZATION TECHNIQUES
1. Components (review)
Logical Relationships
Shape
PointList
PointList_Link
Polygon
Point
34
Underscore Implies Component-Local Class
1. Components (review)
Logical Relationships
Shape
PointList
PointList_Link
Is-A
Polygon
Point
35
1. Components (review)
Logical Relationships
Shape
PointList
PointList_Link
Is-A
Polygon
Point
36
1. Components (review)
Logical Relationships
Shape
PointList
PointList_Link
Is-A
Uses-in-the-Interface
Polygon
Point
37
1. Components (review)
Logical Relationships
Shape
PointList
PointList_Link
Is-A
Uses-in-the-Interface
Polygon
Point
38
1. Components (review)
Logical Relationships
Shape
PointList
PointList_Link
Is-A
Uses-in-the-Interface
Polygon
Point
39
1. Components (review)
Logical Relationships
Shape
PointList
PointList_Link
Is-A Uses-in-the-Implementation
Uses-in-the-Interface
Polygon
Point
40
1. Components (review)
Logical Relationships
Shape
PointList
PointList_Link
Is-A Uses-in-the-Implementation
Uses-in-the-Interface
Polygon
Point
41
1. Components (review)
Logical Relationships
Shape
PointList
PointList_Link
Is-A Uses-in-the-Implementation
Uses-in-the-Interface
Polygon
Point
42
1. Components (review)
Logical Relationships
Shape
PointList
PointList_Link
Is-A Uses-in-the-Implementation
Uses-in-the-Interface Uses in name only
Polygon
Point
43
1. Components (review)
Logical Relationships
Shape
PointList
PointList_Link
Is-A Uses-in-the-Implementation
Uses-in-the-Interface Uses in name only
Polygon
Point
44
1. Components (review)
Implied Dependency
Shape
PointList
PointList_Link
Is-A Uses-in-the-Implementation
Uses-in-the-Interface Uses in name only
Polygon
Point
45
1. Components (review)
Implied Dependency
Shape
PointList
PointList_Link
Is-A Uses-in-the-Implementation
Uses-in-the-Interface Uses in name only
Depends-On
Polygon
Point
46
1. Components (review)
Implied Dependency
Shape
PointList
PointList_Link
Is-A Uses-in-the-Implementation
Uses-in-the-Interface Uses in name only
Depends-On
Polygon
Point
47
1. Components (review)
Implied Dependency
Shape
PointList
PointList_Link
Is-A Uses-in-the-Implementation
Uses-in-the-Interface Uses in name only
Depends-On
Polygon
Point
48
1. Components (review)
Implied Dependency
Shape
PointList
PointList_Link
Is-A Uses-in-the-Implementation
Uses-in-the-Interface Uses in name only
Depends-On
Polygon
Point
49
1. Components (review)
Implied Dependency
Shape
PointList
PointList_Link
Is-A Uses-in-the-Implementation
Uses-in-the-Interface Uses in name only
Depends-On
Polygon
Point
50
1. Components (review)
Level Numbers
Shape
PointList
PointList_Link
Is-A Uses-in-the-Implementation
Uses-in-the-Interface Uses in name only
Depends-On
Polygon
Point
51
1. Components (review)
Level Numbers
Shape
PointList
PointList_Link
Is-A Uses-in-the-Implementation
Uses-in-the-Interface
1
Uses in name only
Depends-On
Polygon
Point
52
1. Components (review)
Level Numbers
Shape
PointList
PointList_Link
Is-A Uses-in-the-Implementation
Uses-in-the-Interface
1 1
Uses in name only
Depends-On
Polygon
Point
53
1. Components (review)
Level Numbers
Shape
PointList
PointList_Link
Is-A Uses-in-the-Implementation
Uses-in-the-Interface
1 1
2
Uses in name only
Depends-On
Polygon
Point
54
1. Components (review)
Level Numbers
Shape
PointList
PointList_Link
Is-A Uses-in-the-Implementation
Uses-in-the-Interface
1 1
2 3
Uses in name only
Depends-On
Polygon
Point
55
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
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
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
64
2. Interfaces and Contracts (review)
Interfaces and Contracts
What do we mean by Interface versus Contract for
• A Function? • A Class? • A Component?
65
2. Interfaces and Contracts (review)
Interfaces and Contracts
Function std::ostream& print(std::ostream& stream,
int level = 0,
int spacesPerLevel = 4) const;
66
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
67
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.
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’ // 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.
// …
};
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.
// …
};
Public
Interface
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
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.
// …
};
74
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’.
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’.
“Public”
Interface
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 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’.
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’.
79
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’.
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
2. Interfaces and Contracts (review)
Preconditions and Postconditions
93
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
Note that Essential Behavior refers to a superset of Postconditions that includes
behavioral guarantees, such as runtime complexity.
Defined & Essential Behavior
2. Interfaces and Contracts (review)
Preconditions and Postconditions
Undefined Behavior 94
Not
Undefined
Behavior
Defined & Essential Behavior
2. Interfaces and Contracts (review)
Preconditions and Postconditions
Essential Behavior
Undefined Behavior 95
Defined & Essential Behavior
2. Interfaces and Contracts (review)
Preconditions and Postconditions
Essential Behavior
Undefined Behavior 96
Defined but not
Essential
Defined & Essential Behavior
2. Interfaces and Contracts (review)
Preconditions and Postconditions
Essential Behavior
Undefined Behavior 97
Defined but not
Essential
Unspecified and Implementation
-dependent
Defined & Essential Behavior
2. Interfaces and Contracts (review)
Preconditions and Postconditions
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
Defined & Essential Behavior
2. Interfaces and Contracts (review)
Preconditions and Postconditions
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
Defined & Essential Behavior
2. Interfaces and Contracts (review)
Preconditions and Postconditions
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
Defined & Essential Behavior
2. Interfaces and Contracts (review)
Preconditions and Postconditions
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
Defined & Essential Behavior
2. Interfaces and Contracts (review)
Preconditions and Postconditions
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
Defined & Essential Behavior
2. Interfaces and Contracts (review)
Preconditions and Postconditions
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.
Any Undefined Behavior?
103
Defined & Essential Behavior
2. Interfaces and Contracts (review)
Preconditions and Postconditions
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.
Any Non-Essential
Behavior?
104
Defined & Essential Behavior
2. Interfaces and Contracts (review)
Preconditions and Postconditions
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.
Any Non-Essential
Behavior?
105
Hint
Defined & Essential Behavior
2. Interfaces and Contracts (review)
Preconditions and Postconditions
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
Defined & Essential Behavior
2. Interfaces and Contracts (review)
Preconditions and Postconditions
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. // … };
Any Undefined Behavior?
107
Defined & Essential Behavior
2. Interfaces and Contracts (review)
Preconditions and Postconditions
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. // … };
Any Undefined Behavior?
108
Defined & Essential Behavior
2. Interfaces and Contracts (review)
Preconditions and Postconditions
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
Defined & Essential Behavior
2. Interfaces and Contracts (review)
Preconditions and Postconditions
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. // … };
Any Undefined Behavior?
110
(Object) Invariants
2. Interfaces and Contracts (review)
Preconditions and Postconditions
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
(Object) Invariants
2. Interfaces and Contracts (review)
Preconditions and Postconditions
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
(Object) Invariants
2. Interfaces and Contracts (review)
Preconditions and Postconditions
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
(Object) Invariants
2. Interfaces and Contracts (review)
Preconditions and Postconditions
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
(Object) Invariants
2. Interfaces and Contracts (review)
Preconditions and Postconditions
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
(Object) Invariants
2. Interfaces and Contracts (review)
Preconditions and Postconditions
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
(DbC)
“If you give me valid input*, I will behave as advertised; otherwise, all bets are off!”
*including state
2. Interfaces and Contracts (review)
Design by Contract
118
There are five aspects: 1. What it does. 2. What it returns. 3. Essential Behavior. 4. Undefined Behavior. 5. Note that…
2. Interfaces and Contracts (review)
Design by Contract
Documentation
119
There are five aspects: 1. What it does. 2. What it returns. 3. Essential Behavior. 4. Undefined Behavior. 5. Note that…
2. Interfaces and Contracts (review)
Design by Contract
Documentation
120
There are five aspects: 1. What it does. 2. What it returns. 3. Essential Behavior. 4. Undefined Behavior. 5. Note that…
2. Interfaces and Contracts (review)
Design by Contract
Documentation
121
There are five aspects: 1. What it does. 2. What it returns. 3. Essential Behavior. 4. Undefined Behavior. 5. Note that…
2. Interfaces and Contracts (review)
Design by Contract
Documentation
122
There are five aspects: 1. What it does. 2. What it returns. 3. Essential Behavior. 4. Undefined Behavior. 5. Note that…
2. Interfaces and Contracts (review)
Design by Contract
Documentation
123
There are five aspects: 1. What it does. 2. What it returns. 3. Essential Behavior. 4. Undefined Behavior. 5. Note that…
2. Interfaces and Contracts (review)
Design by Contract
Documentation
124
Invariants: Assert invariants in the destructor.
2. Interfaces and Contracts (review)
Design by Contract
Verification
125
Preconditions: Invariants: Assert invariants in the destructor.
2. Interfaces and Contracts (review)
Design by Contract
Verification
126
Preconditions: RTFM (Read the Manual).
Invariants: Assert invariants in the destructor.
2. Interfaces and Contracts (review)
Design by Contract
Verification
127
Preconditions: RTFM (Read the Manual). Assert (only in ‘debug’ or ‘safe’ mode).
Invariants: Assert invariants in the destructor.
2. Interfaces and Contracts (review)
Design by Contract
Verification
128
Preconditions: RTFM (Read the Manual). Assert (only in ‘debug’ or ‘safe’ mode).
Postconditions: Invariants: Assert invariants in the destructor.
2. Interfaces and Contracts (review)
Design by Contract
Verification
129
Preconditions: RTFM (Read the Manual). Assert (only in ‘debug’ or ‘safe’ mode).
Postconditions: Component-level test drivers.
Invariants: Assert invariants in the destructor.
2. Interfaces and Contracts (review)
Design by Contract
Verification
130
Preconditions: RTFM (Read the Manual). Assert (only in ‘debug’ or ‘safe’ mode).
Postconditions: Component-level test drivers.
Invariants: Assert invariants in the destructor.
2. Interfaces and Contracts (review)
Design by Contract
Verification
131
Preconditions: RTFM (Read the Manual). Assert (only in ‘debug’ or ‘safe’ mode).
Postconditions: Component-level test drivers.
Invariants: Assert invariants in the destructor.
2. Interfaces and Contracts (review)
Design by Contract
Verification
132
133
Preconditions always Imply Postconditions:
2. Interfaces and Contracts (review)
Contracts and Exceptions
134
Preconditions always Imply Postconditions:
If a function cannot satisfy its contract (given valid
preconditions) it must not return normally.
2. Interfaces and Contracts (review)
Contracts and Exceptions
135
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).
2. Interfaces and Contracts (review)
Contracts and Exceptions
136
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).
2. Interfaces and Contracts (review)
Contracts and Exceptions
Questions?
2. Interfaces and Contracts (review)
End of Section
137
138
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?
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
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
141
3. Narrow versus Wide Contracts (review)
Narrow versus Wide Contracts
142
3. Narrow versus Wide Contracts (review)
Narrow versus Wide Contracts
Pejorative terms:
143
3. Narrow versus Wide Contracts (review)
Narrow versus Wide Contracts
Pejorative terms:
•Fat Interface (4. Proper Inheritance)
144
3. Narrow versus Wide Contracts (review)
Narrow versus Wide Contracts
Pejorative terms:
•Fat Interface (4. Proper Inheritance)
•Large (Non-Primitive) Interface
145
3. Narrow versus Wide Contracts (review)
Narrow versus Wide Contracts
Pejorative terms:
•Fat Interface (4. Proper Inheritance)
•Large (Non-Primitive) Interface
•Wide Contract
146
3. Narrow versus Wide Contracts (review)
Narrow versus Wide Contracts
Narrow Contracts Imply Undefined Behavior:
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);
148
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?
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;
// …
}
How about it must return 0?
Wide
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;
// …
}
How about it must return 0?
Wide
Likely to mask a defect
151
3. Narrow versus Wide Contracts (review)
Narrow versus Wide Contracts
Narrow Contracts Imply Undefined Behavior:
int strlen(const char *s)
{
if (!s) return 0;
// …
}
How about it must return 0?
Wide
Likely to mask a defect
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: What should happen with the following call?
int x = std::strlen(0);
154
3. Narrow versus Wide Contracts (review)
Narrow versus Wide Contracts
Narrow Contracts Imply Undefined Behavior:
int strlen(const char *s)
{
assert(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:
int strlen(const char *s)
{
// …
}
Narrow
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: Should
Date::setDate(int, int, int);
Return a status?
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: 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);
162
3. Narrow versus Wide Contracts (review)
Narrow versus Wide Contracts
Narrow Contracts Imply Undefined Behavior:
Returning status implies a wide contract.
163
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.
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; d_day = d; }
166
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; }
Narrow Contract:
Checked Only In
“Debug Mode”
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; } d_year = y; d_month = m; d_day = d; return 0; }
168
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; }
Wide Contract:
Checked in
Every Build Mode
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);
170
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);
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?
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: • 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);
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?
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?
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();
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? If so, what should it be?
if (idx < 0) idx = 0;
if (idx > length()) idx = length();
idx = abs(idx) % (length() + 1);
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?
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!
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)
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());
186
3. Narrow versus Wide Contracts (review)
Appropriately Narrow Contracts
187
3. Narrow versus Wide Contracts (review)
Appropriately Narrow Contracts
Narrow, but not too narrow.
188
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?
189
3. Narrow versus Wide Contracts (review)
Appropriately Narrow Contracts
0 1 2 3 4 5
1
2
3
4
0
numElements
index
5 E.g., length() is 5.
?
190
3. Narrow versus Wide Contracts (review)
Appropriately Narrow Contracts
0 1 2 3 4 5
1
2
3
4
0
numElements
index
5 E.g., length() is 5.
?
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
0 1 2 3 4 5
1
2
3
4
0
numElements
index
5 E.g., length() is 5.
?
void replace(int index, const TYPE& value, int numElements) { assert(0 <= index); assert(0 <= numElements); assert(index + numElements <= length()); // … }
X
192
3. Narrow versus Wide Contracts (review)
Appropriately Narrow Contracts
0 1 2 3 4 5
1
2
3
4
0
numElements
index
5 E.g., length() is 5.
?
void replace(int index, const TYPE& value, int numElements) { assert(0 <= index); assert(0 <= numElements); assert(index + numElements <= length()); // … }
Now a client would have to check for this special case.
X
193
3. Narrow versus Wide Contracts (review)
Appropriately Narrow Contracts
0 1 2 3 4 5
1
2
3
4
0
numElements
index
5 E.g., length() is 5.
?
void replace(int index, const TYPE& value, int numElements) { assert(0 <= index); assert(0 <= numElements); assert(index + numElements <= length()); // … }
Now a client would have to check for this special case.
X
194
3. Narrow versus Wide Contracts (review)
Appropriately Narrow Contracts
0 1 2 3 4 5
1
2
3
4
0
numElements
index
5 E.g., length() is 5.
?
void replace(int index, const TYPE& value, int numElements) { assert(0 <= index); assert(0 <= numElements); assert(index + numElements <= length()); // … }
Assuming no extra code is needed to handle it …
195
3. Narrow versus Wide Contracts (review)
Appropriately Narrow Contracts
0 1 2 3 4 5
1
2
3
4
0
numElements
index
5 E.g., length() is 5.
!
void replace(int index, const TYPE& value, int numElements) { assert(0 <= index); assert(0 <= numElements); assert(index + numElements <= length()); // … }
Assuming no extra code is needed to handle it …
… it is naturally more efficient to allow it.
196
3. Narrow versus Wide Contracts (review)
End of Section
Questions?
197
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)?
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
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
200
4. Proper Inheritance
Three Kinds of Inheritance
201
4. Proper Inheritance
Three Kinds of Inheritance There are three kinds of inheritance because there are three kinds of member functions:
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:
Public, Protected,
Private?
204
4. Proper Inheritance
Three Kinds of Inheritance There are three kinds of inheritance because there are three kinds of member 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
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
207
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
208
4. Proper Inheritance
Interface Inheritance
class TcpChannel : public Channel {
/* … */
public:
// … (creators)
virtual int read(char *buffer, int numBytes) {…}
virtual int write(const char *buffer, int numBytes) {…}
};
class Channel {
public:
virtual ~Channel() { }
virtual int read(char *buffer, int numBytes) = 0;
virtual int write(const char *buffer, int numBytes) = 0;
};
TcpChannel
Channel
209
4. Proper Inheritance
Structural Inheritance
class Pixel : public Point {
public:
enum Color { RED, GREEN, BLUE };
private:
Color d_color;
public:
// … (creators)
void setColor(Color color) { /* … */ }
Color color ( ) const { /* … */ } };
class Point {
int d_x;
int d_y;
public:
// … (creators)
void setX(int x) { /* … */ }
void setY(int y) { /* … */ }
int x() const { /* … */ }
int y() const { /* … */ }
};
Pixel
Point
210
4. Proper Inheritance
Implementation Inheritance
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; }
// … };
CompositeWidget
Widget
211
4. Proper Inheritance
What Is Proper Inheritance?
Base
Derived
2
1
Is-Substitutable-For
Is-A
Implements
Extends
212
4. Proper Inheritance
What Is Proper Inheritance? • The “IsA” Relationship?
– What does it mean?
213
4. Proper Inheritance
What Is Proper Inheritance? • The “IsA” Relationship?
– What does it mean?
• Weaker Preconditions?
• Stronger Postconditions?
• Same Invariants?
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?
215
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?
216
4. Proper Inheritance
What Is Proper Inheritance?
Base
Derived
2
1
Is-Substitutable-For
Is-A
Implements
Extends
217
4. Proper Inheritance
What Is Proper Inheritance?
Base
Derived
2
1
Is-Substitutable-For
Is-A
Implements
Extends
The Is-A Relation:
The implementation
of a derived class
must satisfy
(simultaneously) its
own contract, as
well as that of
“each” base class.
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 base-class 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 base-class 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?
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 base-class pointer (or reference) with a corresponding derived-class one.
Note that this is how virtual functions behave!
221
4. Proper Inheritance
What Is Proper Inheritance?
Derived
Base
f(int, double, const char *)
f(int, double, const char *)
222
4. Proper Inheritance
What Is Proper Inheritance?
Derived
Base
f(int, double, const char *)
f(int, double, const char *)
223
4. Proper Inheritance
What Is Proper Inheritance?
Derived
Base
f(int, double, const char *)
f(int, double, const char *)
224
4. Proper Inheritance
What Is Proper Inheritance?
Derived
Base
f(int, double, const char *)
f(int, double, const char *)
225
4. Proper Inheritance
What Is Proper Inheritance?
Derived *dp = new Derived();
Base *bp = dp; // bp = 0x002140
Derived
Base
f(int, double, const char *)
f(int, double, const char *)
226
4. Proper Inheritance
What Is Proper Inheritance?
Derived *dp = new Derived(); // dp = 0x002140
Base *bp = dp; // bp = 0x002140
Derived
Base
f(int, double, const char *)
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 = dp; // bp = 0x002140
Derived
Base
f(int, double, const char *)
f(int, double, const char *)
0x002130:
0x002138:
0x002140:
0x002148:
0x002150:
0x002158:
0x002160:
0x002168:
0x002170:
228
4. Proper Inheritance
What Is Proper Inheritance?
Derived *dp = new Derived(); // dp = 0x002140
Base *bp = dp; // bp = 0x002140
Derived
Base
f(int, double, const char *)
f(int, double, const char *) Object of type
Derived
0x002130:
0x002138:
0x002140:
0x002148:
0x002150:
0x002158:
0x002160:
0x002168:
0x002170:
229
4. Proper Inheritance
What Is Proper Inheritance?
Derived *dp = new Derived(); // dp = 0x002140
Base *bp = dp; // bp = 0x002140
Derived
Base
f(int, double, const char *)
f(int, double, const char *) Object of type
Derived
0x002130:
0x002138:
0x002140:
0x002148:
0x002150:
0x002158:
0x002160:
0x002168:
0x002170:
230
4. Proper Inheritance
What Is Proper Inheritance?
Derived *dp = new Derived(); // dp = 0x002140
Base *bp = dp; // bp = 0x002140
Derived
Base
f(int, double, const char *)
f(int, double, const char *) Object of type
Derived
0x002130:
0x002138:
0x002140:
0x002148:
0x002150:
0x002158:
0x002160:
0x002168:
0x002170:
231
4. Proper Inheritance
What Is Proper Inheritance?
Derived *dp = new Derived(); // dp = 0x002140
Base *bp = dp; // bp = 0x002140
Derived
Base
f(int, double, const char *)
f(int, double, const char *) Object of type
Derived
0x002130:
0x002138:
0x002140:
0x002148:
0x002150:
0x002158:
0x002160:
0x002168:
0x002170:
232
4. Proper Inheritance
What Is Proper Inheritance?
Derived *dp = new Derived(); // dp = 0x002140
Base *bp = dp; // bp = 0x002140
Derived
Base
f(int, double, const char *)
f(int, double, const char *) Object of type
Derived
0x002130:
0x002138:
0x002140:
0x002148:
0x002150:
0x002158:
0x002160:
0x002168:
0x002170:
233
4. Proper Inheritance
What Is Proper Inheritance?
Derived *dp = new Derived(); // dp = 0x002140
Base *bp = dp; // bp = 0x002140
Derived
Base
f(int, double, const char *)
f(int, double, const char *) Object of type
Derived
0x002130:
0x002138:
0x002140:
0x002148:
0x002150:
0x002158:
0x002160:
0x002168:
0x002170:
234
4. Proper Inheritance
What Is Proper Inheritance?
Derived *dp = new Derived(); // dp = 0x002140
Base *bp = dp; // bp = 0x002140
Derived
Base
f(int, double, const char *)
f(int, double, const char *) Object of type
Derived
0x002130:
0x002138:
0x002140:
0x002148:
0x002150:
0x002158:
0x002160:
0x002168:
0x002170:
bp->f(1, 2.0, “three”);
235
4. Proper Inheritance
What Is Proper Inheritance?
Derived *dp = new Derived(); // dp = 0x002140
Base *bp = dp; // bp = 0x002140
Derived
Base
f(int, double, const char *)
f(int, double, const char *) Object of type
Derived
0x002130:
0x002138:
0x002140:
0x002148:
0x002150:
0x002158:
0x002160:
0x002168:
0x002170:
dp->f(1, 2.0, “three”);
bp->f(1, 2.0, “three”);
236
4. Proper Inheritance
What Is Proper Inheritance?
Derived *dp = new Derived(); // dp = 0x002140
Base *bp = dp; // bp = 0x002140
Derived
Base
f(int, double, const char *)
f(int, double, const char *) Object of type
Derived
0x002130:
0x002138:
0x002140:
0x002148:
0x002150:
0x002158:
0x002160:
0x002168:
0x002170:
dp->f(1, 2.0, “three”);
bp->f(1, 2.0, “three”); Identical
Behavior
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 base-class 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?
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 base-class pointer (or reference) with a corresponding derived-class one.
Note that this is how virtual functions behave!
239
4. Proper Inheritance
What Is Proper Inheritance?
Derived Interface
and Contract
Base Interface
and Contract
Derived
Base
h()
g(int)
g(int)
f(int)
f(int)
Base::g(int x); // Defined only for 0 <= x.
Derived::g(int x); // Defined for all x.
Base::f(int x); // Defined for all x.
Derived::f(int x); // Defined for all x.
Derived::h(); // Note: not accessible from Base class.
240
4. Proper Inheritance
What Is Proper Inheritance?
Derived Interface
and Contract
Base Interface
and Contract
Derived
Base
h()
g(int)
g(int)
f(int)
f(int)
Base::g(int x); // Defined only for 0 <= x.
Derived::g(int x); // Defined for all x.
Base::f(int x); // Defined for all x.
Derived::f(int x); // Defined for all x.
Derived::h(); // Note: not accessible from Base class.
241
4. Proper Inheritance
What Is Proper Inheritance?
Derived Interface
and Contract
Base Interface
and Contract
Derived
Base
h()
g(int)
g(int)
f(int)
f(int)
Base::g(int x); // Defined only for 0 <= x.
Derived::g(int x); // Defined for all x.
Base::f(int x); // Defined for all x.
Derived::f(int x); // Defined for all x.
Derived::h(); // Note: not accessible from Base class.
242
4. Proper Inheritance
What Is Proper Inheritance?
Derived Interface
and Contract
Base Interface
and Contract
Derived
Base
h()
g(int)
g(int)
f(int)
f(int)
Base::g(int x); // Defined only for 0 <= x.
Derived::g(int x); // Defined for all x.
Base::f(int x); // Defined for all x.
Derived::f(int x); // Defined for all x.
Derived::h(); // Note: not accessible from Base class.
4. Proper Inheritance
Pure Interface Inheritance
243
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.
Derived
Base
Implementation
Interface
Constructors D::f
B::f
4. Proper Inheritance
Pure Interface Inheritance
244
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.
Derived
Base
Implementation
Interface
Constructors D::f
B::f
Implements the Interface
4. Proper Inheritance
Pure Interface Inheritance
245
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.
Derived
Base
Implementation
Interface
Constructors D::f
B::f
Implements the Interface
4. Proper Inheritance
Pure Interface Inheritance
246
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.
Derived
Base
Implementation
Interface
Constructors D::f
B::f
Implements the Interface
are typically the same as
247
4. Proper Inheritance
Pure Interface Inheritance
Channel
B::f
248
4. Proper Inheritance
Pure Interface Inheritance
Channel
B::f
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
TcpChannel
Channel
D::f
B::f
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
TcpChannel
Channel
D::f
B::f
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'.
251
4. Proper Inheritance
Pure Interface Inheritance
TcpChannel
Channel
D::f
B::f
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'.
252
4. Proper Inheritance
Pure Interface Inheritance
TcpChannel
Channel
D::f
B::f
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'.
253
4. Proper Inheritance
What Is a Proper Subtype/Subclass?
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)
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)
TcpChannel
Channel
Interface Inheritance
Can create it.
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)
Pixel
Point
TcpChannel
Channel
Interface Inheritance
Structural Inheritance
Can create it. Can do something
more with it.
257
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)
Pixel
Point
TcpChannel
Channel
CompositeWidget
Widget
Interface Inheritance
Structural Inheritance
Implementation Inheritance
Can create it. Can do something
more with it.
Can create something
else with it.
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
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)
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)
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()
{ 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 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, …);
}
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) { } };
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) { } };
main()
{ Bool b0(false);
Bool b1(true);
// …
p(b0, b1, …);
}
void p(const Bool& x, const Bool& y, …) { /* … */ }
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) { } };
main()
{ Bool b0(false);
Bool b1(true);
// …
p(b0, b1, …);
}
void p(const Bool& x, const Bool& y, …) { /* … */ }
main()
{ Fool f0(false);
Fool f1(true);
// …
p(f1, f0, …);
}
Note order is different!
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) { } };
main()
{ Bool b0(false);
Bool b1(true);
// …
p(b0, b1, …);
}
void p(const Bool& x, const Bool& y, …) { /* … */ }
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)
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) { } };
main()
{ Bool b0(false);
Bool b1(true);
// …
p(b0, b1, …);
}
void p(const Bool& x, const Bool& y, …) { /* … */ }
main()
{ Fool f0(false);
Fool f1(true);
// …
p(f1, f0, …);
}
Note order is different!
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)
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.
279
4. Proper Inheritance
What Is Liskov Substitution?
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 “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 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)
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 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.
282
4. Proper Inheritance
What Is Liskov Substitution?
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) { } };
main()
{ Bool b0(false);
Bool b1(true);
// …
p(b0, b1, …);
}
void p(const Bool& x, const Bool& y, …) { /* … */ }
main()
{ Fool f0(false);
Fool f1(true);
// …
p(f1, f0, …);
}
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.
283
4. Proper Inheritance
What Is Liskov Substitution?
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) { } };
main()
{ Bool b0(false);
Bool b1(true);
// …
p(b0, b1, …);
}
void p(const Bool& x, const Bool& y, …) { /* … */ }
main()
{ Fool f0(false);
Fool f1(true);
// …
p(f1, f0, …);
}
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.
284
4. Proper Inheritance
What Is Liskov Substitution?
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) { } };
main()
{ Bool b0(false);
Bool b1(true);
// …
p(b0, b1, …);
}
void p(const Bool& x, const Bool& y, …) { /* … */ }
main()
{ Fool f0(false);
Fool f1(true);
// …
p(f1, f0, …);
}
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.
285
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 base-class pointer (or reference) with a corresponding derived-class one.
Note that this is how virtual functions behave!
286
4. Proper Inheritance
What Is Proper Inheritance?
void example(Derived *pDerived)
{
Base *pBase = pDerived;
#ifdef USE_DERIVED_CLASS_INTERFACE
pDerived->someMethod(/* … */);
int result = someFunction(*pDerive);
#else
pBase->someMethod(/* … */);
int result = someFunction(*pBase);
#endif
Derived
Base
287
4. Proper Inheritance
Pure Structural Inheritance #ifdef USE_BASE_CLASS_INTERFACE
typedef Point Type;
#else
typedef Pixel Type;
#endif
void anyProgram(Type *p);
void main()
{
Pixel pixel(1, 2, Pixel::BLUE);
anyProgram(&pixel);
}
using std::cout; // (We do this only
using std::endl; // in test drivers.)
Pixel
Point
288
4. Proper Inheritance
Pure Structural Inheritance
Pixel
Point
void anyProgram(Type *p)
{
cout << p->x() << endl;
}
int Point::x() const
{
return d_x;
}
289
4. Proper Inheritance
Pure Structural Inheritance
Pixel
Point
void anyProgram(Type *p)
{
p->setY(10);
}
void Point::setY(int y)
{
d_y = y;
}
290
4. Proper Inheritance
Pure Structural Inheritance
Pixel
Point
void anyProgram(Type *p)
{
cout << p->color() << endl;
}
Pixel::Color Pixel::color() const
{
return d_color;
}
class Pixel : public Point {
// …
Color d_color;
// …
};
291
4. Proper Inheritance
Pure Structural Inheritance
Pixel
Point
void anyProgram(Type *p)
{
p->setY(10);
}
void Pixel::setY(int y)
{
cout << "Pixel::setY(int y)" << endl;
d_y = y;
}
void Point::setY(int y)
{
d_y = y;
}
292
4. Proper Inheritance
Pure Structural Inheritance
Pixel
Point
void anyProgram(Type *p)
{
p->setY(10);
}
void Pixel::setY(int y)
{
cout << "Pixel::setY(int y)" << endl;
d_y = y;
}
void Point::setY(int y)
{
d_y = y;
}
!
293
4. Proper Inheritance
Pure Structural Inheritance
Pixel
Point
void anyProgram(Type *p)
{
p->setY(10);
}
void Pixel::setY(int y)
{
++s_numSetY; // Pixel class data
d_y = y;
}
void Point::setY(int y)
{
d_y = y;
}
class Pixel : public Point {
// …
static int s_numSetY;
public:
// …
};
294
4. Proper Inheritance
Pure Structural Inheritance
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;
}
class Pixel : public Point {
// …
static int s_numSetY;
public:
// …
};
295
4. Proper Inheritance
Pure Structural Inheritance
Pixel
Point
void anyProgram(Type *p)
{
p->setY(10);
}
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;
}
296
4. Proper Inheritance
Pure Structural Inheritance
Pixel
Point
void anyProgram(Type *p)
{
if (sizeof p->self() > sizeof(Point))
cout << "It's not a Point!" << endl;
}
const Pixel& Pixel::self() const
{
return *this;
}
const Point& Point::self() const
{
return *this;
}
class Pixel : public Point {
// …
Color d_color;
// …
};
297
4. Proper Inheritance
Pure Structural Inheritance
Pixel
Point
void anyProgram(Type *p)
{
if (sizeof p->self() > sizeof(Point)) ?
cout << "It's not a Point!" << endl;
}
const Pixel& Pixel::self() const
{
return *this;
}
const Point& Point::self() const
{
return *this;
}
class Pixel : public Point {
// …
Color d_color;
// …
};
298
4. Proper Inheritance
Pure Structural Inheritance
Pixel
Point
void anyProgram(Type *p)
{
if (sizeof p->self() > 8) // sizeof(Point)
cout << "It's not a Point!" << endl;
}
const Pixel& Pixel::self() const
{
return *this;
}
const Point& Point::self() const
{
return *this;
}
class Pixel : public Point {
// …
Color d_color;
// …
};
299
4. Proper Inheritance
Pure Structural Inheritance
Pixel
Point
void anyProgram(Type *p)
{
if (sizeof p->self() > 8) // sizeof(Point)
cout << "It's not a Point!" << endl;
}
const Pixel& Pixel::self() const
{
return *this;
}
const Point& Point::self() const
{
return *this;
}
class Pixel : public Point {
// …
Color d_color;
// …
};
300
4. Proper Inheritance
Pure Structural Inheritance
Pixel
Point
void anyProgram(Type *p)
{
if (sizeof p->self() > 8) // sizeof(Point)
cout << "It's not a point!" << endl;
}
const Pixel& Pixel::self() const
{
return *this;
}
const Point& Point::self() const
{
return *this;
}
class Pixel : public Point {
// …
Color d_color;
// …
};
301
4. Proper Inheritance
Pure Structural Inheritance
Pixel
Point
void anyProgram(Type *p)
{
if (sizeof p->self() > sizeof(Point))
cout << "It's not a Point!" << endl;
}
const Pixel& Pixel::self() const
{
return *this;
}
const Point& Point::self() const
{
return *this;
}
class Pixel : public Point {
// …
Color d_color;
// …
};
302
4. Proper Inheritance
Pure Structural Inheritance
Pixel
Point
void anyProgram(Type *p)
{
if (sizeof p->self() > sizeof(Point))
cout << "It's not a Point!" << endl;
}
const Pixel& Pixel::self() const
{
return *this;
}
const Point& Point::self() const
{
return *this;
}
!
class Pixel : public Point {
// …
Color d_color;
// …
};
303
4. Proper Inheritance
Pure Structural Inheritance
Pixel
Point
void anyProgram(Type *p)
{
if (sizeof *p > sizeof(Point))
cout << "It's not a Point!" << endl;
}
class Pixel : public Point {
// …
Color d_color;
// …
};
304
4. Proper Inheritance
Pure Structural Inheritance
Pixel
Point
void anyProgram(Type *p)
{
if (sizeof *p > sizeof(Point))
cout << "It's not a Point!" << endl;
}
class Pixel : public Point {
// …
Color d_color;
// …
}; !
305
4. Proper Inheritance
Pure Structural Inheritance
Pixel
Point
void anyProgram(Type *p)
{
if (sizeof *p > sizeof(Point))
cout << "It's not a Point!" << endl;
}
class Pixel : public Point {
// …
Color d_color;
// …
}; !
306
4. Proper Inheritance
Pure Structural Inheritance
Pixel
Point
void anyProgram(Type *p)
{
if (sizeof *p > sizeof(Point))
cout << "It's not a Point!" << endl;
}
class Pixel : public Point {
// …
Color d_color;
// …
}; !
Proper Structural
Inheritance extends
functionality, but
does not extend the
object’s footprint.
307
4. Proper Inheritance
Pure Structural Inheritance
Pixel
Point
void anyProgram(Type *p)
{
double alignmentHack; // Don’t do it!
char buffer[sizeof(Point)];
(Type *)&buffer = *p;
}
class Pixel : public Point {
// …
Color d_color;
// …
}; !
buffer
buffer
The same “size” issue applies to arrays of objects!
?!?!
308
4. Proper Inheritance
Implementation Inheritance
CompositeWidget
Widget
Implementation hierarchies
are highly problematic!
Incorporating implementation with interface
inheritance: – Makes software brittle, inflexible, and hard to
maintain.
– Exposes public clients to physical (compile-
and 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!
4. Proper Inheritance
Using Interface Inheritance Effectively
309
Point
Polygon Square
Rectangle
Shape
MyRectangle
4. Proper Inheritance
Using Interface Inheritance Effectively
310
Point
Polygon Square
Rectangle
Shape
origin; width
origin; width; length
origin
origin; numVertices;
operator[](int index)
MyRectangle
x; y
4. Proper Inheritance
Using Interface Inheritance Effectively
311
Point
Polygon Square
Rectangle
Shape
x; y
origin; width
origin; width; length
origin
?
A Rectangle
Is-A Square
with a length
attribute?
origin; numVertices;
operator[](int index)
MyRectangle
4. Proper Inheritance
Using Interface Inheritance Effectively
312
Point
Polygon Square
Rectangle
Shape
x; y
origin; width
origin; width; length
origin
?
A Rectangle
Is-A Square
with a length
attribute?
origin; numVertices;
operator[](int index)
MyRectangle
Rectangle does not respect
Square
Invariant
4. Proper Inheritance
Using Interface Inheritance Effectively
313
Point
Polygon Square
Rectangle
Shape
x; y
origin; width
origin; width; length
origin
?
A Rectangle
Is-A Square
with a length
attribute?
origin; numVertices;
operator[](int index)
MyRectangle
4. Proper Inheritance
Using Interface Inheritance Effectively
314
Point
Polygon Square
Shape
Rectangle
x; y
origin; width
origin; width; length
origin
origin; numVertices;
operator[](int index)
MyRectangle
4. Proper Inheritance
Using Interface Inheritance Effectively
315
Point
Polygon Square
Shape
Rectangle
x; y
origin; width
origin; width; length
origin
origin; numVertices;
operator[](int index)
MyRectangle
4. Proper Inheritance
Using Interface Inheritance Effectively
316
Point
Polygon Square
Shape
Rectangle
x; y
origin; width
origin; width; length
origin
origin; numVertices;
operator[](int index)
MyRectangle
4. Proper Inheritance
Using Interface Inheritance Effectively
317
Point
Polygon Square
Shape
Rectangle
x; y
origin; width
origin; width; length
origin
origin; numVertices;
operator[](int index)
MyRectangle
4. Proper Inheritance
Using Interface Inheritance Effectively
318
Square
Rectangle
4. Proper Inheritance
Using Interface Inheritance Effectively
319
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());
}
4. Proper Inheritance
Using Interface Inheritance Effectively
320
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());
}
4. Proper Inheritance
Using Interface Inheritance Effectively
321
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());
}
4. Proper Inheritance
Using Interface Inheritance Effectively
322
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());
}
4. Proper Inheritance
Using Interface Inheritance Effectively
323
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());
}
4. Proper Inheritance
Using Interface Inheritance Effectively
324
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());
}
4. Proper Inheritance
Using Interface Inheritance Effectively
325
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());
}
4. Proper Inheritance
Using Interface Inheritance Effectively
326
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());
}
Either Assert
or No Longer
Square
4. Proper Inheritance
Using Interface Inheritance Effectively
327
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());
}
Either Assert
or No Longer
Square
4. Proper Inheritance
Using Interface Inheritance Effectively
328
Point
Polygon Square
Shape
Rectangle
x; y
origin; width
origin; width; length
origin
origin; numVertices;
operator[](int index)
MyRectangle
4. Proper Inheritance
Using Interface Inheritance Effectively
329
Point
Polygon Square
Shape
Rectangle
x; y
origin; width
origin; width; length
origin
origin; numVertices;
operator[](int index)
MyRectangle
4. Proper Inheritance
Using Interface Inheritance Effectively
330
Point
Polygon Square
Shape
Rectangle
x; y
origin; width
origin; width; length
origin
origin; numVertices;
operator[](int index)
MyRectangle
4. Proper Inheritance
Using Interface Inheritance Effectively
331
Point
Polygon Square
Shape
Rectangle
x; y
origin; width
origin; width; length
origin
origin; numVertices;
operator[](int index)
MyRectangle
4. Proper Inheritance
Using Interface Inheritance Effectively
332
Point
Polygon Square
Shape
Rectangle
x; y
origin; width
origin; width; length
origin
origin; numVertices;
operator[](int index)
MyRectangle
4. Proper Inheritance
Using Interface Inheritance Effectively
333
Point
Polygon Square
Shape
Rectangle
x; y
origin; width
origin; width; length
origin
origin; numVertices;
operator[](int index)
MyRectangle
4. Proper Inheritance
Using Interface Inheritance Effectively
334 origin() const;
(Modifiable) Rectangle
ConstShape
(Modifiable) Square
(Modifiable) Polygon
(Modifiable) Shape
(Concrete)
YourSquare (Concrete)
TheirPolygon
ConstRectangle
ConstPolygon
ConstSquare int side() const
int width() const
int length() const
Point vertex(int i) const
int numVertices() const
void setOrigin(const Point& v);
void setSide(int side)
void setWidth(int width)
void setLength(int length)
void appendVertex(const Point& v)
void removeVertex(int i)
TheirPolygon() YourSquare() MyRectangle()
(Concrete)
MyRectangle
4. Proper Inheritance
Using Interface Inheritance Effectively
335 origin() const;
(Modifiable) Rectangle
ConstShape
(Modifiable) Square
(Modifiable) Polygon
(Modifiable) Shape
(Concrete)
YourSquare (Concrete)
TheirPolygon
ConstRectangle
ConstPolygon
ConstSquare int side() const
int width() const
int length() const
Point vertex(int i) const
int numVertices() const
void setOrigin(const Point& v);
void setSide(int side)
void setWidth(int width)
void setLength(int length)
void appendVertex(const Point& v)
void removeVertex(int i)
TheirPolygon() YourSquare() MyRectangle()
(Concrete)
MyRectangle
4. Proper Inheritance
Using Interface Inheritance Effectively
336 origin() const;
(Modifiable) Rectangle
ConstShape
(Modifiable) Square
(Modifiable) Polygon
(Modifiable) Shape
(Concrete)
YourSquare (Concrete)
TheirPolygon
ConstRectangle
ConstPolygon
ConstSquare int side() const
int width() const
int length() const
Point vertex(int i) const
int numVertices() const
void setOrigin(const Point& v);
void setSide(int side)
void setWidth(int width)
void setLength(int length)
void appendVertex(const Point& v)
void removeVertex(int i)
TheirPolygon() YourSquare() MyRectangle()
(Concrete)
MyRectangle
4. Proper Inheritance
Using Interface Inheritance Effectively
337 origin() const;
(Modifiable) Rectangle
ConstShape
(Modifiable) Square
(Modifiable) Polygon
(Modifiable) Shape
(Concrete)
YourSquare (Concrete)
TheirPolygon
ConstRectangle
ConstPolygon
ConstSquare int side() const
int width() const
int length() const
Point vertex(int i) const
int numVertices() const
void setOrigin(const Point& v);
void setSide(int side)
void setWidth(int width)
void setLength(int length)
void appendVertex(const Point& v)
void removeVertex(int i)
TheirPolygon() YourSquare() MyRectangle()
(Concrete)
MyRectangle
4. Proper Inheritance
Using Interface Inheritance Effectively
338 origin() const;
(Modifiable) Rectangle
ConstShape
(Modifiable) Square
(Modifiable) Polygon
(Modifiable) Shape
(Concrete)
YourSquare (Concrete)
TheirPolygon
ConstRectangle
ConstPolygon
ConstSquare int side() const
int width() const
int length() const
Point vertex(int i) const
int numVertices() const
void setOrigin(const Point& v);
void setSide(int side)
void setWidth(int width)
void setLength(int length)
void appendVertex(const Point& v)
void removeVertex(int i)
TheirPolygon() YourSquare() MyRectangle()
(Concrete)
MyRectangle
4. Proper Inheritance
Using Interface Inheritance Effectively
339 origin() const;
(Modifiable) Rectangle
ConstShape
(Modifiable) Square
(Modifiable) Polygon
(Modifiable) Shape
(Concrete)
YourSquare (Concrete)
TheirPolygon
ConstRectangle
ConstPolygon
ConstSquare int side() const
int width() const
int length() const
Point vertex(int i) const
int numVertices() const
void setOrigin(const Point& v);
void setSide(int side)
void setWidth(int width)
void setLength(int length)
void appendVertex(const Point& v)
void removeVertex(int i)
TheirPolygon() YourSquare() MyRectangle()
(Concrete)
MyRectangle
4. Proper Inheritance
Using Interface Inheritance Effectively
340 origin() const;
(Modifiable) Rectangle
ConstShape
(Modifiable) Square
(Modifiable) Polygon
(Modifiable) Shape
(Concrete)
YourSquare (Concrete)
TheirPolygon
ConstRectangle
ConstPolygon
ConstSquare int side() const
int width() const
int length() const
Point vertex(int i) const
int numVertices() const
void setOrigin(const Point& v);
void setSide(int side)
void setWidth(int width)
void setLength(int length)
void appendVertex(const Point& v)
void removeVertex(int i)
TheirPolygon() YourSquare() MyRectangle()
(Concrete)
MyRectangle
4. Proper Inheritance
Using Interface Inheritance Effectively
341 origin() const;
(Modifiable) Rectangle
ConstShape
(Modifiable) Square
(Modifiable) Polygon
(Modifiable) Shape
(Concrete)
YourSquare (Concrete)
TheirPolygon
ConstRectangle
ConstPolygon
ConstSquare int side() const
int width() const
int length() const
Point vertex(int i) const
int numVertices() const
void setOrigin(const Point& v);
void setSide(int side)
void setWidth(int width)
void setLength(int length)
void appendVertex(const Point& v)
void removeVertex(int i)
TheirPolygon() YourSquare() MyRectangle()
(Concrete)
MyRectangle
4. Proper Inheritance
Using Interface Inheritance Effectively
342 origin() const;
(Modifiable) Rectangle
ConstShape
(Modifiable) Square
(Modifiable) Polygon
(Modifiable) Shape
(Concrete)
YourSquare (Concrete)
TheirPolygon
ConstRectangle
ConstPolygon
ConstSquare int side() const
int width() const
int length() const
Point vertex(int i) const
int numVertices() const
void setOrigin(const Point& v);
void setSide(int side)
void setWidth(int width)
void setLength(int length)
void appendVertex(const Point& v)
void removeVertex(int i)
TheirPolygon() YourSquare() MyRectangle()
(Concrete)
MyRectangle
4. Proper Inheritance
Using Interface Inheritance Effectively
343 origin() const;
(Modifiable) Rectangle
ConstShape
(Modifiable) Square
(Modifiable) Polygon
(Modifiable) Shape
(Concrete)
YourSquare (Concrete)
TheirPolygon
ConstRectangle
ConstPolygon
ConstSquare int side() const
int width() const
int length() const
Point vertex(int i) const
int numVertices() const
void setOrigin(const Point& v);
void setSide(int side)
void setWidth(int width)
void setLength(int length)
void appendVertex(const Point& v)
void removeVertex(int i)
TheirPolygon() YourSquare() MyRectangle()
(Concrete)
MyRectangle
4. Proper Inheritance
Using Interface Inheritance Effectively
344 origin() const;
(Modifiable) Rectangle
ConstShape
(Modifiable) Square
(Modifiable) Polygon
(Modifiable) Shape
(Concrete)
YourSquare (Concrete)
TheirPolygon
ConstRectangle
ConstPolygon
ConstSquare int side() const
int width() const
int length() const
Point vertex(int i) const
int numVertices() const
void setOrigin(const Point& v);
void setSide(int side)
void setWidth(int width)
void setLength(int length)
void appendVertex(const Point& v)
void removeVertex(int i)
TheirPolygon() YourSquare() MyRectangle()
(Concrete)
MyRectangle
4. Proper Inheritance
Using Interface Inheritance Effectively
345 origin() const;
(Modifiable) Rectangle
ConstShape
(Modifiable) Square
(Modifiable) Polygon
(Modifiable) Shape
(Concrete)
YourSquare (Concrete)
TheirPolygon
ConstRectangle
ConstPolygon
ConstSquare int side() const
int width() const
int length() const
Point vertex(int i) const
int numVertices() const
void setOrigin(const Point& v);
void setSide(int side)
void setWidth(int width)
void setLength(int length)
void appendVertex(const Point& v)
void removeVertex(int i)
TheirPolygon() YourSquare() MyRectangle()
(Concrete)
MyRectangle
4. Proper Inheritance
Using Interface Inheritance Effectively
346
(Concrete)
MyRectangle
origin() const;
(Modifiable) Rectangle
ConstShape
(Modifiable) Square
(Modifiable) Polygon
(Modifiable) Shape
(Concrete)
YourSquare (Concrete)
TheirPolygon
ConstRectangle
ConstPolygon
ConstSquare int side() const
int width() const
int length() const
Point vertex(int i) const
int numVertices() const
void setOrigin(const Point& v);
void setSide(int side)
void setWidth(int width)
void setLength(int length)
void appendVertex(const Point& v)
void removeVertex(int i)
TheirPolygon() YourSquare() MyRectangle()
4. Proper Inheritance
Using Interface Inheritance Effectively
347
(Concrete)
MyRectangle
origin() const;
(Modifiable) Rectangle
ConstShape
(Modifiable) Square
(Modifiable) Polygon
(Modifiable) Shape
(Concrete)
YourSquare (Concrete)
TheirPolygon
ConstRectangle
ConstPolygon
ConstSquare int side() const
int width() const
int length() const
Point vertex(int i) const
int numVertices() const
void setOrigin(const Point& v);
void setSide(int side)
void setWidth(int width)
void setLength(int length)
void appendVertex(const Point& v)
void removeVertex(int i)
TheirPolygon() YourSquare() MyRectangle()
The principal clients of Interface Inheritance
are both the
PUBLIC CLIENT
and the
DERIVED-CLASS AUTHOR.
4. Proper Inheritance
Using Interface Inheritance Effectively
348
4. Proper Inheritance
Using Interface Inheritance Effectively
349
Channel
int write(const char *b, int n);
int read(char *b, int n);
4. Proper Inheritance
Using Interface Inheritance Effectively
350
Channel
MyChannel
int write(const char *b, int n);
int read(char *b, int n);
4. Proper Inheritance
Using Interface Inheritance Effectively
351
Channel
MyChannel
Client1
int write(const char *b, int n);
int read(char *b, int n);
4. Proper Inheritance
Using Interface Inheritance Effectively
352
Channel
TimedChannel
MyChannel
Client1
int write(const char *b, int n, int t);
int read(char *b, int n, int t);
int write(const char *b, int n);
int read(char *b, int n);
4. Proper Inheritance
Using Interface Inheritance Effectively
353
Channel
TimedChannel
MyChannel
YourTimedChannel
Client1
int write(const char *b, int n, int t);
int read(char *b, int n, int t);
int write(const char *b, int n);
int read(char *b, int n);
4. Proper Inheritance
Using Interface Inheritance Effectively
354
Channel
TimedChannel
MyChannel
YourTimedChannel
Client2
Client1
int write(const char *b, int n, int t);
int read(char *b, int n, int t);
int write(const char *b, int n);
int read(char *b, int n);
4. Proper Inheritance
Using Interface Inheritance Effectively
355
Channel
TimedChannel
3rd Party
Product A
MyChannel
YourTimedChannel
Client2
Client1
int write(const char *b, int n, int t);
int read(char *b, int n, int t);
int write(const char *b, int n);
int read(char *b, int n);
4. Proper Inheritance
Using Interface Inheritance Effectively
356
Channel
TimedChannel
3rd Party
Product A
PaChannelAdapter
MyChannel
YourTimedChannel
Client2
Client1
int write(const char *b, int n, int t);
int read(char *b, int n, int t);
int write(const char *b, int n);
int read(char *b, int n);
4. Proper Inheritance
Using Interface Inheritance Effectively
357
Channel
TimedChannel
3rd Party
Product A
PaChannelAdapter
MyChannel
YourTimedChannel
Client2
Client3
Client1
int write(const char *b, int n, int t);
int read(char *b, int n, int t);
int write(const char *b, int n);
int read(char *b, int n);
4. Proper Inheritance
Using Interface Inheritance Effectively
358
Channel
TimedChannel
3rd Party
Product A
3rd Party
Product B
PaChannelAdapter
MyChannel
YourTimedChannel
Client2
Client3
Client1
int write(const char *b, int n, int t);
int read(char *b, int n, int t);
int write(const char *b, int n);
int read(char *b, int n);
4. Proper Inheritance
Using Interface Inheritance Effectively
359
Channel
TimedChannel
3rd Party
Product A
3rd Party
Product B
PaChannelAdapter
PbTimedChannelAdapter
MyChannel
YourTimedChannel
Client2
Client3
Client1
int write(const char *b, int n, int t);
int read(char *b, int n, int t);
int write(const char *b, int n);
int read(char *b, int n);
4. Proper Inheritance
Using Interface Inheritance Effectively
360
Channel
TimedChannel
3rd Party
Product A
3rd Party
Product B
PaChannelAdapter
PbTimedChannelAdapter
MyChannel
YourTimedChannel Client4
Client2
Client3
Client1
int write(const char *b, int n, int t);
int read(char *b, int n, int t);
int write(const char *b, int n);
int read(char *b, int n);
4. Proper Inheritance
Using Interface Inheritance Effectively
361
Channel
TimedChannel
3rd Party
Product A
3rd Party
Product B
PaChannelAdapter
PbTimedChannelAdapter
MyChannel
YourTimedChannel Client4
Client2
Client5
Client3
Client1
int write(const char *b, int n, int t);
int read(char *b, int n, int t);
int write(const char *b, int n);
int read(char *b, int n);
4. Proper Inheritance
Using Structural Inheritance Effectively
362
(Non-Modifiable)
ConstElemRef
(Modifiable)
ElemRef
4. Proper Inheritance
Using Structural Inheritance Effectively
363
(Non-Modifiable)
ConstElemRef
template <class TYPE>
class ConstElemRef<TYPE> {
const TYPE *d_elem_p;
friend class ElemRef<TYPE>; // 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;
};
elem() ;
4. Proper Inheritance
Using Structural Inheritance Effectively
364
(Non-Modifiable)
ConstElemRef
template <class TYPE>
class ConstElemRef<TYPE> {
const TYPE *d_elem_p;
friend class ElemRef<TYPE>; // 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;
};
elem() ;
Single Pointer Data Member
4. Proper Inheritance
Using Structural Inheritance Effectively
365
(Non-Modifiable)
ConstElemRef
template <class TYPE>
class ConstElemRef<TYPE> {
const TYPE *d_elem_p;
friend class ElemRef<TYPE>; // 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;
};
elem() ;
Derived Class Declared Friend
4. Proper Inheritance
Using Structural Inheritance Effectively
366
(Non-Modifiable)
ConstElemRef
template <class TYPE>
class ConstElemRef<TYPE> {
const TYPE *d_elem_p;
friend class ElemRef<TYPE>; // 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;
};
elem() ;
Copy Assignment Not Implemented
4. Proper Inheritance
Using Structural Inheritance Effectively
367
(Non-Modifiable)
ConstElemRef
template <class TYPE>
class ConstElemRef<TYPE> {
const TYPE *d_elem_p;
friend class ElemRef<TYPE>; // 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;
};
elem() ;
Read-Only Access
4. Proper Inheritance
Using Structural Inheritance Effectively
368
ConstElemRef
template <class TYPE>
class ElemRef<TYPE> : public ConstElemRef<TYPE> {
public:
// CREATORS
ElemRef(TYPE *elem);
ElemRef(const ElemRef& ref);
~ElemRef();
// MANIPULATORS
ElemRef& operator=(const ElemRef&); // Fine. TBD
// ACCESSORS
TYPE& elem() const;
};
(Modifiable)
ElemRef
class ConstElemRef<TYPE> {
const TYPE *d_elem_p;
// …
const TYPE& elem() const;
};
elem() ;
elem() ;
4. Proper Inheritance
Using Structural Inheritance Effectively
369
ConstElemRef
template <class TYPE>
class ElemRef<TYPE> : public ConstElemRef<TYPE> {
public:
// CREATORS
ElemRef(TYPE *elem);
ElemRef(const ElemRef& ref);
~ElemRef();
// MANIPULATORS
ElemRef& operator=(const ElemRef&); // Fine. TBD
// ACCESSORS
TYPE& elem() const;
};
(Modifiable)
ElemRef
class ConstElemRef<TYPE> {
const TYPE *d_elem_p;
// …
const TYPE& elem() const;
};
elem() ;
elem() ;
Public Structural
Inheritance
4. Proper Inheritance
Using Structural Inheritance Effectively
370
ConstElemRef
template <class TYPE>
class ElemRef<TYPE> : public ConstElemRef<TYPE> {
public:
// CREATORS
ElemRef(TYPE *elem);
ElemRef(const ElemRef& ref);
~ElemRef();
// MANIPULATORS
ElemRef& operator=(const ElemRef&); // Fine. TBD
// ACCESSORS
TYPE& elem() const;
};
(Modifiable)
ElemRef
class ConstElemRef<TYPE> {
const TYPE *d_elem_p;
// …
const TYPE& elem() const;
};
elem() ;
elem() ;
No Additional Member Data
4. Proper Inheritance
Using Structural Inheritance Effectively
371
ConstElemRef
template <class TYPE>
class ElemRef<TYPE> : public ConstElemRef<TYPE> {
public:
// CREATORS
ElemRef(TYPE *elem);
ElemRef(const ElemRef& ref);
~ElemRef();
// MANIPULATORS
ElemRef& operator=(const ElemRef&); // Fine. TBD
// ACCESSORS
TYPE& elem() const;
};
(Modifiable)
ElemRef
class ConstElemRef<TYPE> {
const TYPE *d_elem_p;
// …
const TYPE& elem() const;
};
elem() ;
elem() ;
Copy Assignment Implemented
4. Proper Inheritance
Using Structural Inheritance Effectively
372
ConstElemRef
template <class TYPE>
class ElemRef<TYPE> : public ConstElemRef<TYPE> {
public:
// CREATORS
ElemRef(TYPE *elem);
ElemRef(const ElemRef& ref);
~ElemRef();
// MANIPULATORS
ElemRef& operator=(const ElemRef&); // Fine. TBD
// ACCESSORS
TYPE& elem() const;
};
(Modifiable)
ElemRef
class ConstElemRef<TYPE> {
const TYPE *d_elem_p;
// …
const TYPE& elem() const;
};
elem() ;
elem() ;
Read-Write Access
4. Proper Inheritance
Using Structural Inheritance Effectively
373
ConstElemRef
template <class TYPE>
class ElemRef<TYPE> : public ConstElemRef<TYPE> {
public:
// CREATORS
ElemRef(TYPE *elem);
ElemRef(const ElemRef& ref);
~ElemRef();
// MANIPULATORS
ElemRef& operator=(const ElemRef&); // Fine. TBD
// ACCESSORS
TYPE& elem() const;
};
(Modifiable)
ElemRef
class ConstElemRef<TYPE> {
const TYPE *d_elem_p;
// …
const TYPE& elem() const;
};
elem() ;
elem() ;
Read-Write Access
An ElemRef
Is-A ConstElemRef
with “Write Access”
4. Proper Inheritance
Using Structural Inheritance Effectively
374
ConstElemRef
template <class TYPE>
class ElemRef<TYPE> : public ConstElemRef<TYPE> {
public:
// CREATORS
ElemRef(TYPE *elem);
ElemRef(const ElemRef& ref);
~ElemRef();
// MANIPULATORS
ElemRef& operator=(const ElemRef&); // Fine. TBD
// ACCESSORS
TYPE& elem() const;
};
(Modifiable)
ElemRef
class ConstElemRef<TYPE> {
const TYPE *d_elem_p;
// …
const TYPE& elem() const;
};
elem() ;
elem() ;
Read-Write Access
An std::iterator
Is-A std::const_iterator
with “Write Access”
4. Proper Inheritance
Using Structural Inheritance Effectively
375
const TYPE& ConstElemRef::elem() const
{
return *d_elem_p;
}
TYPE& ElemRef::elem() const
{
return *const_cast<TYPE *>(d_elem_p);
}
// Note use of friendship as well.
(Non-Modifiable)
ConstElemRef
(Modifiable)
ElemRef
Note: same component due to friendship.
4. Proper Inheritance
Using Structural Inheritance Effectively
376
const TYPE& ConstElemRef::elem() const
{
return *d_elem_p;
}
TYPE& ElemRef::elem() const
{
return *const_cast<TYPE *>(d_elem_p);
}
// Note use of friendship as well.
(Non-Modifiable)
ConstElemRef
(Modifiable)
ElemRef
Note: same component due to friendship.
Note we are casting-away const
4. Proper Inheritance
Using Structural Inheritance Effectively
377
(Non-Modifiable)
ConstElemRef
(Modifiable)
ElemRef
Be especially careful to ensure const-correctness when const-casting is involved.
4. Proper Inheritance
Using Structural Inheritance Effectively
378
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
Be especially careful to ensure const-correctness when const-casting is involved.
template < class TYPE>
void f(const TYPE& constElem)
{
TYPE dummy;
ElemRef er(&dummy);
ConstElemRef cer(&constElem);
g(&er, cer); // Rebind (modifiable) 'ElemRef' 'er'.
er.elem() = TYPE(); // Clobber 'constElem'.
4. Proper Inheritance
Using Structural Inheritance Effectively
379
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
Be especially careful to ensure const-correctness when const-casting is involved.
template < class TYPE>
void f(const TYPE& constElem)
{
TYPE dummy;
ElemRef er(&dummy);
ConstElemRef cer(&constElem);
g(&er, cer); // Rebind (modifiable) 'ElemRef' 'er'.
er.elem() = TYPE(); // Clobber 'constElem'.
4. Proper Inheritance
Using Structural Inheritance Effectively
380
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
Be especially careful to ensure const-correctness when const-casting is involved.
template < class TYPE>
void f(const TYPE& constElem)
{
TYPE dummy;
ElemRef er(&dummy);
ConstElemRef cer(&constElem);
g(&er, cer); // Rebind (modifiable) 'ElemRef' 'er'.
er.elem() = TYPE(); // Clobber 'constElem'.
4. Proper Inheritance
Using Structural Inheritance Effectively
381
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
Be especially careful to ensure const-correctness when const-casting is involved.
template < class TYPE>
void f(const TYPE& constElem)
{
TYPE dummy;
ElemRef er(&dummy);
ConstElemRef cer(&constElem);
g(&er, cer); // Rebind (modifiable) 'ElemRef' 'er'.
er.elem() = TYPE(); // Clobber 'constElem'.
4. Proper Inheritance
Using Structural Inheritance Effectively
382
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
Be especially careful to ensure const-correctness when const-casting is involved.
template < class TYPE>
void f(const TYPE& constElem)
{
TYPE dummy;
ElemRef er(&dummy);
ConstElemRef cer(&constElem);
g(&er, cer); // Rebind (modifiable) 'ElemRef' 'er'.
er.elem() = TYPE(); // Clobber 'constElem'.
4. Proper Inheritance
Using Structural Inheritance Effectively
383
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
Be especially careful to ensure const-correctness when const-casting is involved.
template < class TYPE>
void f(const TYPE& constElem)
{
TYPE dummy;
ElemRef er(&dummy);
ConstElemRef cer(&constElem);
g(&er, cer); // Rebind (modifiable) 'ElemRef' 'er'.
er.elem() = TYPE(); // Clobber 'constElem'.
4. Proper Inheritance
Using Structural Inheritance Effectively
384
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
Be especially careful to ensure const-correctness when const-casting is involved.
4. Proper Inheritance
Using Structural Inheritance Effectively
385
The principal client of Structural Inheritance
is the
PUBLIC CLIENT.
4. Proper Inheritance
Using Structural Inheritance Effectively
386
ConstElemRefClient
(Non-Modifiable)
ConstElemRef
(Modifiable)
ElemRef
ElemRefClient
Note: No Runtime-Performance
Overhead (e.g., due to Dynamic
Binding).
4. Proper Inheritance
Using Implementation Inheritance Effectively
387
CompositeWidget
Widget
instanceName origin move draw numChildren addChild removeChild
WidgetClient
4. Proper Inheritance
Using Implementation Inheritance Effectively
388
CompositeWidget
Widget
instanceName origin move draw
numChildren addChild removeChild
WidgetClient
CompositeWidgetClient
4. Proper Inheritance
Using Implementation Inheritance Effectively
389
CompositeWidget
Widget
instanceName origin move draw
numChildren addChild removeChild
WidgetClient
CompositeWidgetClient
MyWidget
4. Proper Inheritance
Using Implementation Inheritance Effectively
390
CompositeWidget
WidgetImp
numChildren addChild removeChild
WidgetClient
CompositeWidgetClient
instanceName origin move draw
Widget
MyWidget
4. Proper Inheritance
Using Implementation Inheritance Effectively
391
CompositeWidget
WidgetImp
numChildren addChild removeChild
WidgetClient
CompositeWidgetClient
instanceName origin move draw
Widget
MyWidget
YourWidget
392
CompositeWidget
WidgetImp
numChildren addChild removeChild
WidgetClient
CompositeWidgetClient
instanceName origin move draw
Widget
MyWidget
YourWidget
MyCompositeWidget
4. Proper Inheritance
Using Implementation Inheritance Effectively
4. Proper Inheritance
Using Implementation Inheritance Effectively
393
WidgetImp
numChildren addChild removeChild
WidgetClient
CompositeWidgetClient
instanceName origin move draw
Widget
MyWidget
YourWidget
CompositeWidget
CompositeWidgetImp
MyCompositeWidget
4. Proper Inheritance
Using Implementation Inheritance Effectively
394
WidgetImp
numChildren addChild removeChild
WidgetClient
CompositeWidgetClient
instanceName origin move draw
Widget
MyWidget
YourWidget
CompositeWidget
CompositeWidgetImp
MyCompositeWidget
YourCompositeWidget
4. Proper Inheritance
Using Implementation Inheritance Effectively
395
WidgetImp
numChildren addChild removeChild
WidgetClient
CompositeWidgetClient
instanceName origin move draw
Widget
MyWidget
YourWidget
CompositeWidget
CompositeWidgetImp
MyCompositeWidget
YourCompositeWidget
4. Proper Inheritance
Using Implementation Inheritance Effectively
396
WidgetImp
numChildren addChild removeChild
WidgetClient
CompositeWidgetClient
instanceName origin move draw
Widget
MyWidget
YourWidget
CompositeWidget
CompositeWidgetImp
MyCompositeWidget
YourCompositeWidget
4. Proper Inheritance
Using Implementation Inheritance Effectively
397
WidgetImp
numChildren addChild removeChild
WidgetClient
CompositeWidgetClient
instanceName origin move draw
Widget
MyWidget
YourWidget
CompositeWidget
CompositeWidgetImp
MyCompositeWidget
YourCompositeWidget
4. Proper Inheritance
Using Implementation Inheritance Effectively
398
WidgetImp
numChildren addChild removeChild
WidgetClient
CompositeWidgetClient
instanceName origin move draw
Widget
MyWidget
YourWidget
CompositeWidget
CompositeWidgetImp
MyCompositeWidget
YourCompositeWidget
4. Proper Inheritance
Using Implementation Inheritance Effectively
399
WidgetImp
numChildren addChild removeChild
WidgetClient
CompositeWidgetClient
instanceName origin move draw
Widget
MyWidget
YourWidget
CompositeWidget
CompositeWidgetImp
MyCompositeWidget
YourCompositeWidget
4. Proper Inheritance
Using Implementation Inheritance Effectively
400
WidgetImp
numChildren addChild removeChild
WidgetClient
CompositeWidgetClient
instanceName origin move draw
Widget
MyWidget
YourWidget
CompositeWidget
CompositeWidgetImp
MyCompositeWidget
YourCompositeWidget
4. Proper Inheritance
Using Implementation Inheritance Effectively
401
WidgetImp
numChildren addChild removeChild
WidgetClient
CompositeWidgetClient
instanceName origin move draw
Widget
MyWidget
YourWidget
CompositeWidget
CompositeWidgetImp
MyCompositeWidget
YourCompositeWidget
Widen interface
first;
4. Proper Inheritance
Using Implementation Inheritance Effectively
402
WidgetImp
numChildren addChild removeChild
WidgetClient
CompositeWidgetClient
instanceName origin move draw
Widget
MyWidget
YourWidget
CompositeWidget
CompositeWidgetImp
MyCompositeWidget
YourCompositeWidget
Widen interface
first; then provide
implementation
without widening.
4. Proper Inheritance
Using Implementation Inheritance Effectively
403
The principal client of Implementation Inheritance
is the
DERIVED-CLASS AUTHOR.
4. Proper Inheritance
Using Implementation Inheritance Effectively
404
Widget
CompositeWidget
numChildren addChild removeChild
instanceName origin move draw
4. Proper Inheritance
Using Implementation Inheritance Effectively
405 Framework
Part of Framework using CompositeWidget
Part of Framework using Widget
Widget
CompositeWidget
numChildren addChild removeChild
instanceName origin move draw
4. Proper Inheritance
Using Implementation Inheritance Effectively
406 Framework
Part of Framework using CompositeWidget
Part of Framework using Widget
Widget
CompositeWidget
numChildren addChild removeChild
instanceName origin move draw
4. Proper Inheritance
Using Implementation Inheritance Effectively
407
WidgetPartialImp
Framework
Part of Framework using CompositeWidget
Part of Framework using Widget
Widget
CompositeWidget
numChildren addChild removeChild
instanceName origin move draw
4. Proper Inheritance
Using Implementation Inheritance Effectively
408
WidgetPartialImp
CompositeWidgetPartialImp
Framework
Part of Framework using CompositeWidget
Part of Framework using Widget
Widget
CompositeWidget
numChildren addChild removeChild
instanceName origin move draw
4. Proper Inheritance
Using Implementation Inheritance Effectively
409
WidgetPartialImp
CompositeWidgetPartialImp
MyWidget
Framework
Part of Framework using CompositeWidget
Part of Framework using Widget
Widget
CompositeWidget
numChildren addChild removeChild
instanceName origin move draw
4. Proper Inheritance
Using Implementation Inheritance Effectively
410
WidgetPartialImp
CompositeWidgetPartialImp
MyCompositeWidget
MyWidget
Framework
Part of Framework using CompositeWidget
Part of Framework using Widget
Widget
CompositeWidget
numChildren addChild removeChild
instanceName origin move draw
4. Proper Inheritance
Using Implementation Inheritance Effectively
411
WidgetPartialImp
CompositeWidgetPartialImp
MyCompositeWidget YourWidget
MyWidget
Framework
Part of Framework using CompositeWidget
Part of Framework using Widget
Widget
CompositeWidget
numChildren addChild removeChild
instanceName origin move draw
4. Proper Inheritance
Using Implementation Inheritance Effectively
412
WidgetPartialImp
CompositeWidgetPartialImp
YourCompositeWidget
MyCompositeWidget YourWidget
MyWidget
Framework
Part of Framework using CompositeWidget
Part of Framework using Widget
Widget
CompositeWidget
numChildren addChild removeChild
instanceName origin move draw
4. Proper Inheritance
Using Implementation Inheritance Effectively
413
WidgetPartialImp
CompositeWidgetPartialImp
YourCompositeWidget
MyCompositeWidget YourWidget
TheirCompositeWidget MyWidget
Framework
Part of Framework using CompositeWidget
Part of Framework using Widget
Widget
CompositeWidget
numChildren addChild removeChild
instanceName origin move draw
4. Proper Inheritance
Using Implementation Inheritance Effectively
414
WidgetPartialImp
CompositeWidgetPartialImp
YourCompositeWidget
MyCompositeWidget YourWidget
TheirCompositeWidget MyWidget
TheirWidget
Framework
Part of Framework using CompositeWidget
Part of Framework using Widget
Widget
CompositeWidget
numChildren addChild removeChild
instanceName origin move draw
415
4. Proper Inheritance
Combining Kinds of Inheritance
416
4. Proper Inheritance
Combining Kinds of Inheritance • Structural & Interface
417
4. Proper Inheritance
Combining Kinds of Inheritance • Structural & Interface
–Typically for Efficiency and Syntactic Sugar.
418
4. Proper Inheritance
Combining Kinds of Inheritance • Structural & Interface
–Typically for Efficiency and Syntactic Sugar.
• Interface & Implementation
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) .
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
421
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.
422
4. Proper Inheritance
Combining Kinds of Inheritance
inline int f(); virtual void g() = 0; virtual void h() = 0;
Public
Client
virtual void g();
virtual void g() { } virtual void h() { }
423
4. Proper Inheritance
Combining Kinds of Inheritance
inline int f(); virtual void g(); virtual void h();
virtual void g();
Public
Client
424
4. Proper Inheritance
Combining Kinds of Inheritance
inline int f(); virtual void g(); virtual void h();
virtual void g();
Public
Client
425
4. Proper Inheritance
Combining Kinds of Inheritance
inline int f(); virtual void g(); virtual void h();
virtual void g();
Public
Client
426
4. Proper Inheritance
Relative Utility Interface Inheritance
Structural Inheritance
Implementation Inheritance
427
4. Proper Inheritance
Physical Substitutability
428
4. Proper Inheritance
Physical Substitutability
429
4. Proper Inheritance
Physical Substitutability
430
4. Proper Inheritance
Physical Substitutability
What Criteria Must Be Satisfied?
431
4. Proper Inheritance
Physical Substitutability
The new component’s logical behavior:
432
4. Proper Inheritance
Physical Substitutability
The new component’s logical behavior:
• Preconditions needed for defined behavior can be made weaker, but no stronger.
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.
434
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.
435
4. Proper Inheritance
Physical Substitutability
The new component’s physical characteristics:
436
4. Proper Inheritance
Physical Substitutability
The new component’s physical characteristics:
• Physical dependencies cannot increase (much).
437
4. Proper Inheritance
Physical Substitutability
The new component’s physical characteristics:
• Physical dependencies cannot increase (much).
• Compile-time cannot increase substantially.
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).
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).
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.
441
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.
442
4. Proper Inheritance
End of Section
Questions?
443
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?
444
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
445
1. Components (review)
Modularity, Logical/Physical Dependencies, & Level numbers
Conclusion
446
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.
Conclusion
447
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.
Conclusion
448
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!
Conclusion
449
2. Interfaces and Contracts (review)
Syntax versus Semantics & Essential Behavior
Conclusion
450
2. Interfaces and Contracts (review)
Syntax versus Semantics & Essential Behavior
• An interface is syntactic; a contract is semantic.
Conclusion
451
2. Interfaces and Contracts (review)
Syntax versus Semantics & Essential Behavior
• An interface is syntactic; a contract is semantic. • A contract defines both pre- & postconditions.
Conclusion
452
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.
Conclusion
453
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!
Conclusion
454
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!
Conclusion
455
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.
Conclusion
456
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.
Conclusion
457
3. Narrow versus Wide Contracts (review)
The Significance of Undefined Behavior
Conclusion
458
3. Narrow versus Wide Contracts (review)
The Significance of Undefined Behavior
• Narrow contracts admit undefined behavior.
Conclusion
459
3. Narrow versus Wide Contracts (review)
The Significance of Undefined Behavior
• Narrow contracts admit undefined behavior.
• Appropriately narrow contracts are GOOD:
Conclusion
460
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
Conclusion
461
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
Conclusion
462
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
Conclusion
463
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
Conclusion
464
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!
Conclusion
465
Conclusion
4. Proper Inheritance Is-A for Interface, Structural, & Implementation Inheritance
466
4. Proper Inheritance Is-A for Interface, Structural, & Implementation Inheritance
• The derived class must adhere to both contracts.
Conclusion
467
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.
Conclusion
468
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!
Conclusion
469
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.
Conclusion
470
Conclusion
The End